rewrite command completion so that options to commands can be completed too

git-svn-id: https://urjtag.svn.sourceforge.net/svnroot/urjtag/trunk@1886 b68d4a1b-bc3d-0410-92ed-d4ac073336b7
master
Mike Frysinger 14 years ago
parent a26e39b34c
commit 7e618d7a8f

@ -7,6 +7,16 @@
of urj_parse_line and into its own function urj_tokenize_lines. This lets
us reuse this code elsewhere.
* include/urjtag/cmd.h, src/apps/jtag/jtag.c, src/cmd/cmd.h,
src/cmd/cmd_cmd.c: Rewrite command completion so that sub-options to
commands may be completed as well. Those commands must implement their
own sub-completion handlers in order to support this.
* src/cmd/cmd_bfin.c, src/cmd/cmd_cable.c, src/cmd/cmd_get.c,
src/cmd/cmd_help.c, src/cmd/cmd_include.c, src/cmd/cmd_initbus.c,
src/cmd/cmd_instruction.c, src/cmd/cmd_salias.c, src/cmd/cmd_set.c,
src/cmd/cmd_shift.c, src/cmd/cmd_signal.c, src/cmd/cmd_test.c: Implement
sub-completion handlers for these commands.
2011-02-18 Mike Frysinger <vapier@gentoo.org>
* src/bfin/bfin-part.c (_bfin_part_init): Add missing "void" to param list.

@ -42,18 +42,18 @@
* handled in the same way, urj_error is set to #URJ_ERROR_SYNTAX.
*/
int urj_cmd_run (urj_chain_t *chain, char *params[]);
/**
* Search through registered commands
* Attempt completion of part of a command string
*
* @param text match commands whose prefix equals <code>text</code>. Rotates
* through the registered commands. The prefix length is set when
* the rotating state is reset.
* @@@@ RFHH that is weird behaviour. Why not do the prefix length as strlen(text)?
* @param state if 0, reset the rotating state to start from the beginning
* @param chain chain to possibly use for some completions
* @param line full (incomplete) command line
* @param point current cursor position in the line
*
* @return malloc'ed value. The caller is responsible for freeing it.
* NULL for malloc failure or end of command list.
* @return malloc'ed array of strings. The caller is responsible for freeing
* all of them, and the array itself. NULL for malloc failure or end of
* possible completions.
*/
char *urj_cmd_find_next (const char *text, int state);
char **urj_cmd_complete (urj_chain_t *chain, const char *line, int point);
#endif /* URJ_CMD_H */

@ -152,13 +152,31 @@ jtag_create_jtagdir (void)
#ifdef HAVE_LIBREADLINE
#ifdef HAVE_READLINE_COMPLETION
static char **
urj_cmd_completion (const char *text, int start, int end)
static urj_chain_t *active_chain;
static char *
urj_cmd_completion (const char *text, int matches)
{
char **ret = NULL;
/* Cache the current set of matches */
static char **all_matches = NULL;
static int idx;
char *ret = NULL;
if (matches == 0)
{ /* Build new list of completions */
free (all_matches);
all_matches = NULL;
idx = 0;
all_matches = urj_cmd_complete (active_chain, rl_line_buffer, rl_point);
}
if (start == 0)
ret = rl_completion_matches (text, urj_cmd_find_next);
if (all_matches)
{
ret = all_matches[idx];
if (ret)
++idx;
}
return ret;
}
@ -544,10 +562,12 @@ main (int argc, char *const argv[])
#ifdef HAVE_LIBREADLINE
#ifdef HAVE_READLINE_COMPLETION
active_chain = chain;
rl_readline_name = "urjtag";
rl_completer_quote_characters = "\"";
rl_filename_completion_desired = 1;
rl_filename_quote_characters = " ";
rl_attempted_completion_function = urj_cmd_completion;
rl_completion_entry_function = urj_cmd_completion;
#endif
#endif

@ -47,6 +47,8 @@ typedef struct
/** @return URJ_STATUS_OK on success; URJ_STATUS_FAIL on error, both
* syntax and library errors */
int (*run) (urj_chain_t *chain, char *params[]);
void (*complete) (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len, size_t token_point);
} urj_cmd_t;
#define _URJ_CMD(cmd) extern const urj_cmd_t urj_cmd_##cmd;
@ -87,4 +89,51 @@ do { \
arr[i]->name, _(arr[i]->description)); \
} while (0)
/**
* Internal completion helper for adding to the set of matches.
*
* @param match this must be malloced memory that may be freed in the
* future by common code -- do not free it yourself
*/
void urj_completion_add_match (char ***matches, size_t *cnt, char *match);
/**
* Internal completion helper for adding to the set of matches.
*
* @param match this string will be strduped before being passed down
* to the urj_completion_add_match helper.
*/
void urj_completion_add_match_dupe (char ***matches, size_t *cnt,
const char *match);
/**
* Internal completion helper for possibly adding to the set of matches.
* If text matches the leading portion of match, then it will be added.
*
* @param text the string to compare to match (e.g. user input)
* @param match the string to possibly add to the set of matches
*/
void urj_completion_maybe_add_match (char ***matches, size_t *cnt,
const char *text, const char *match);
/**
* This is just like urj_completion_maybe_add_match, except the length of
* text is precomputed. This is so common, we get a dedicated function.
*
* @param text the string to compare to match (e.g. user input)
* @param text_len the length of text
* @param match the string to possibly add to the set of matches
*/
void urj_completion_mayben_add_match (char ***matches, size_t *cnt,
const char *text, size_t text_len,
const char *match);
/**
* Internal completion helper for matching against the signal list.
* Since many functions involve signals as an option, unify the code
* in one place.
*/
void cmd_signal_complete (urj_chain_t *chain, char ***matches,
size_t *match_cnt, const char *text, size_t text_len);
#endif /* URJ_CMD_H */

@ -499,9 +499,22 @@ cmd_bfin_help (void)
"bfin", "bfin", "bfin" );
}
static void
cmd_bfin_complete (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len, size_t token_point)
{
if (token_point != 1)
return;
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "execute");
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "emulation");
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "reset");
}
const urj_cmd_t urj_cmd_bfin = {
"bfin",
N_("Blackfin specific commands"),
cmd_bfin_help,
cmd_bfin_run
cmd_bfin_run,
cmd_bfin_complete,
};

@ -194,9 +194,32 @@ cmd_cable_help (void)
urj_cmd_show_list (urj_tap_cable_drivers);
}
static void
cmd_cable_complete (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len, size_t token_point)
{
size_t i;
switch (token_point)
{
case 1:
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "probe");
for (i = 0; urj_tap_cable_drivers[i]; i++)
urj_completion_mayben_add_match (matches, match_cnt, text, text_len,
urj_tap_cable_drivers[i]->name);
break;
case 2:
/* XXX: in the future, we want to complete cable options too */
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "help");
break;
}
}
const urj_cmd_t urj_cmd_cable = {
"cable",
N_("select JTAG cable"),
cmd_cable_help,
cmd_cable_run
cmd_cable_run,
cmd_cable_complete,
};

@ -26,10 +26,11 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <urjtag/error.h>
#include <urjtag/chain.h>
#include <urjtag/parse.h>
#include <urjtag/cmd.h>
#include "cmd.h"
@ -42,36 +43,130 @@ const urj_cmd_t * const urj_cmds[] = {
/*
* @param text match commands whose prefix equals <code>text</code>. Rotates
* through the registered commands. The prefix length is set when
* the rotating state is reset.
* @@@@ RFHH that is weird behaviour. Why not do the prefix length as strlen(text)?
* through the registered commands. The prefix length is set when the
* rotating state is reset. This is the behavior as dictated by readline.
*/
char *
urj_cmd_find_next (const char *text, int state)
static const urj_cmd_t *
urj_cmd_find (const char *text, ssize_t last_idx)
{
static size_t cmd_idx, len;
char *next = NULL;
static size_t len;
const urj_cmd_t *ret;
if (!state)
{
cmd_idx = 0;
if (last_idx == -1)
len = strlen (text);
while (urj_cmds[++last_idx])
{
ret = urj_cmds[last_idx];
if (!strncmp (ret->name, text, len))
return ret;
}
while (urj_cmds[cmd_idx])
return NULL;
}
/* These three funcs are meant to be used by sub-command completers */
void
urj_completion_add_match (char ***matches, size_t *cnt, char *match)
{
*matches = realloc (*matches, sizeof (**matches) * (*cnt + 2));
(*matches)[(*cnt)++] = match;
}
void
urj_completion_add_match_dupe (char ***matches, size_t *cnt, const char *match)
{
urj_completion_add_match (matches, cnt, strdup (match));
}
void
urj_completion_mayben_add_match (char ***matches, size_t *cnt, const char *text,
size_t text_len, const char *match)
{
if (!strncmp (text, match, text_len))
urj_completion_add_match_dupe (matches, cnt, match);
}
void
urj_completion_maybe_add_match (char ***matches, size_t *cnt, const char *text,
const char *match)
{
urj_completion_mayben_add_match (matches, cnt, text, strlen (text), match);
}
static size_t
urt_completion_find_token_point (const char *line, int point)
{
const char *cs = line;
size_t token_point = 0;
/* Skip all leading whitespace first to make 2nd loop easier */
while (isspace (*cs))
++cs;
while (*cs)
{
char *name = urj_cmds[cmd_idx++]->name;
if (!strncmp (name, text, len))
{
next = strdup (name);
if (next == NULL)
urj_error_set (URJ_ERROR_OUT_OF_MEMORY, "strdup(%s) fails",
name);
if (point <= (cs - line))
break;
++cs;
if (isspace (*cs))
{
++token_point;
while (isspace (*cs))
++cs;
}
}
return next;
return token_point;
}
char **
urj_cmd_complete (urj_chain_t *chain, const char *line, int point)
{
char **tokens, **ret;
size_t token_cnt, token_point, ret_cnt;
const urj_cmd_t *cmd;
const char *name;
/* Split up the current line to make completion easier */
if (urj_tokenize_line (line, &tokens, &token_cnt))
return NULL;
if (token_cnt == 0)
name = "";
else
name = tokens[0];
ret = NULL;
ret_cnt = 0;
/* Figure out which token we're pointing to */
token_point = urt_completion_find_token_point (line, point);
/* Are we completing the command itself ? Re-use the 'help' ... */
if (token_point == 0)
name = "help";
/* Figure out options for which command we want to complete */
cmd = urj_cmd_find (name, -1);
if (cmd && cmd->complete)
{
if (token_cnt)
name = tokens[token_point] ? : "";
else
name = "";
cmd->complete (chain, &ret, &ret_cnt, name, strlen (name), token_point);
if (ret_cnt)
ret[ret_cnt] = NULL;
}
if (token_cnt)
urj_tokens_free (tokens);
return ret;
}
int

@ -92,9 +92,20 @@ cmd_get_help (void)
"get");
}
static void
cmd_get_complete (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len, size_t token_point)
{
if (token_point != 1)
return;
cmd_signal_complete (chain, matches, match_cnt, text, text_len);
}
const urj_cmd_t urj_cmd_get = {
"get",
N_("get external signal value"),
cmd_get_help,
cmd_get_run
cmd_get_run,
cmd_get_complete,
};

@ -78,9 +78,25 @@ cmd_help_help (void)
"help");
}
static void
cmd_help_complete (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len, size_t token_point)
{
size_t i;
/* Completing the command itself will come here as token 0 */
if (token_point > 1)
return;
for (i = 0; urj_cmds[i]; ++i)
urj_completion_mayben_add_match (matches, match_cnt, text, text_len,
urj_cmds[i]->name);
}
const urj_cmd_t urj_cmd_help = {
"help",
N_("display this help"),
cmd_help_help,
cmd_help_run
cmd_help_run,
cmd_help_complete,
};

@ -29,6 +29,10 @@
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_LIBREADLINE
#include <readline/readline.h>
#endif
#include <urjtag/error.h>
#include <urjtag/parse.h>
#include <urjtag/jtag.h>
@ -97,11 +101,55 @@ cmd_include_help (void)
cmd_include_or_script_help ("include");
}
static void
cmd_include_complete (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len, size_t token_point)
{
#ifdef HAVE_LIBREADLINE
int state;
size_t implicit_len;
char *match, *search_text;
/* Use the search path if path isn't explicitly relative/absolute */
if (text[0] != '/' && text[0] != '.')
{
const char *jtag_data_dir = urj_get_data_dir ();
implicit_len = strlen (jtag_data_dir) + 1;
search_text = malloc (implicit_len + text_len + 1);
if (!search_text)
return;
sprintf (search_text, "%s/%s", jtag_data_dir, text);
text = search_text;
text_len += implicit_len;
}
else
{
implicit_len = 0;
search_text = NULL;
}
state = 0;
while (1)
{
match = rl_filename_completion_function (text, state++);
if (!match)
break;
urj_completion_add_match_dupe (matches, match_cnt, match + implicit_len);
free (match);
}
free (search_text);
#endif
}
const urj_cmd_t urj_cmd_include = {
"include",
N_("include command sequence from external repository"),
cmd_include_help,
cmd_include_run
cmd_include_run,
cmd_include_complete,
};
static int

@ -101,9 +101,24 @@ cmd_initbus_help (void)
urj_cmd_show_list (urj_bus_drivers);
}
static void
cmd_initbus_complete (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len, size_t token_point)
{
size_t i;
if (token_point != 1)
return;
for (i = 0; urj_bus_drivers[i]; i++)
urj_completion_mayben_add_match (matches, match_cnt, text, text_len,
urj_bus_drivers[i]->name);
}
const const urj_cmd_t urj_cmd_initbus = {
"initbus",
N_("initialize bus driver for active part"),
cmd_initbus_help,
cmd_initbus_run
cmd_initbus_run,
cmd_initbus_complete,
};

@ -30,6 +30,7 @@
#include <urjtag/error.h>
#include <urjtag/part.h>
#include <urjtag/part_instruction.h>
#include <urjtag/chain.h>
#include <urjtag/cmd.h>
@ -110,9 +111,33 @@ cmd_instruction_help (void)
"instruction", "instruction", "instruction");
}
static void
cmd_instruction_complete (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len, size_t token_point)
{
urj_part_t *part;
urj_part_instruction_t *i;
if (token_point != 1)
return;
part = urj_tap_chain_active_part (chain);
if (part == NULL)
return;
i = part->instructions;
while (i)
{
urj_completion_mayben_add_match (matches, match_cnt, text, text_len,
i->name);
i = i->next;
}
}
const urj_cmd_t urj_cmd_instruction = {
"instruction",
N_("change active instruction for a part or declare new instruction"),
cmd_instruction_help,
cmd_instruction_run
cmd_instruction_run,
cmd_instruction_complete,
};

@ -94,9 +94,20 @@ cmd_salias_help (void)
"signal");
}
static void
cmd_salias_complete (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len, size_t token_point)
{
if (token_point != 2)
return;
cmd_signal_complete (chain, matches, match_cnt, text, text_len);
}
const urj_cmd_t urj_cmd_salias = {
"salias",
N_("define an alias for a signal"),
cmd_salias_help,
cmd_salias_run
cmd_salias_run,
cmd_salias_complete,
};

@ -117,9 +117,33 @@ cmd_set_help (void)
"set");
}
static void
cmd_set_complete (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len, size_t token_point)
{
switch (token_point)
{
case 1: /* name */
cmd_signal_complete (chain, matches, match_cnt, text, text_len);
break;
case 2: /* direction */
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "in");
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "out");
break;
case 3: /* value */
/* XXX: Only applies if token[1] == "out" ... */
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "0");
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "1");
break;
}
}
const urj_cmd_t urj_cmd_set = {
"set",
N_("set external signal value"),
cmd_set_help,
cmd_set_run
cmd_set_run,
cmd_set_complete,
};

@ -77,9 +77,21 @@ cmd_shift_help (void)
"shift ir", "shift dr");
}
static void
cmd_shift_complete (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len, size_t token_point)
{
if (token_point != 1)
return;
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "dr");
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "ir");
}
const urj_cmd_t urj_cmd_shift = {
"shift",
N_("shift data/instruction registers through JTAG chain"),
cmd_shift_help,
cmd_shift_run
cmd_shift_run,
cmd_shift_complete,
};

@ -101,6 +101,27 @@ cmd_signal_help (void)
"signal");
}
/* This is used indirectly by other signal commands */
void
cmd_signal_complete (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len)
{
urj_part_t *part;
urj_part_signal_t *s;
part = urj_tap_chain_active_part (chain);
if (part == NULL)
return;
s = part->signals;
while (s)
{
urj_completion_mayben_add_match (matches, match_cnt, text, text_len,
s->name);
s = s->next;
}
}
const urj_cmd_t urj_cmd_signal = {
"signal",
N_("define new signal for a part"),

@ -108,9 +108,26 @@ cmd_test_help (void)
"test");
}
static void
cmd_test_complete (urj_chain_t *chain, char ***matches, size_t *match_cnt,
const char *text, size_t text_len, size_t token_point)
{
switch (token_point)
{
case 1: /* name */
cmd_signal_complete (chain, matches, match_cnt, text, text_len);
break;
case 2: /* value */
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "0");
urj_completion_mayben_add_match (matches, match_cnt, text, text_len, "1");
break;
}
}
const urj_cmd_t urj_cmd_test = {
"test",
N_("test external signal value"),
cmd_test_help,
cmd_test_run
cmd_test_run,
cmd_test_complete,
};

Loading…
Cancel
Save