> Hello.
> Recently, I think that lcrash should have "sub-commands line completion".
>
> Lcrash has many sub-commands. And almost sub-commands have parameters such as
> filename or symbol name which should be specified.
> The present lcrash cannot complete on sub-commands line.
> For this reason, we have to memorize sub-commands names and parameters
> exactly.
> It is very inconvenient.
> So I'll add completion capability to librl.
>
> I'm considering as follows.
> While editing sub-commands line, if TAB key is pressed, lcrash completes the
> line (or do something as bash does).
> Lcrash will complete on sub-commands names with behavior almost equivalent to
> bash.
> And I consider that parameters of sub-commands have different characteristic
> each other, I'll add the mechanism let you be able to make your own
> completion
> function. Using this mechanism, you can call the function that behaves as you
> want when TAB key is pressed.
Hi, all.
I implemented the lcrash completion mechanism(first phase) that I mentioned
in the above mail.
Here is the description of the mechanism.
While editing sub-commands line, if TAB key is pressed, lcrash completes the
line.
- When TAB key is pressed at the head of line, lcrash prints the list of
sub-commands names.
- When TAB key is pressed in the middle of the first word of line, lcrash
completes sub-commands names.
* When there is no candidate, prints beep character.
* When there is a candidate, prints the string of it.
* When there are two or more candidates,
+ When there is the identical part of string of them, prints
the string.
+ When there isn't the identical part of string of them,
prints the list of them.
- When TAB key is pressed on the word after the second it of line, lcrash
completes sub-commands arguments.
Note) This function is not implemented, now lcrash prints beep character.
Given below is patch against the files taken from sourceforge cvs of lkcd.
Any comments and suggestions are welcomed.
diff -Naur lkcdutils/lcrash/cmds/command.c
lkcdutils+completion_func/lcrash/cmds/command.c
--- lkcdutils/lcrash/cmds/command.c Mon Aug 27 18:41:16 2001
+++ lkcdutils+completion_func/lcrash/cmds/command.c Mon Oct 1 11:50:43 2001
@@ -156,45 +156,15 @@
}
/*
- * get_cmd()
+ * line_to_words()
*/
-static void
-get_cmd(command_t *cmd)
+void
+line_to_words(command_t *cmd)
{
- int i = 0, len;
- char *cp, *ecp, *empty="";
- char *rl_getline(void);
-
- static char* cmd_buf;
- static size_t cmd_len;
-
- clean_cmd(cmd);
- cmd->ofp = stdout;
- cmd->efp = stderr;
-
- if (!ql_have_terminal) {
- fprintf(stdout, "\n>> ");
- fflush(stdout);
-
- if ((len = getline(&cmd_buf, &cmd_len, stdin)) <= 0) {
- /* stdin closed */
- exit(0);
- }
- else { /* get rid off the newline character */
- cmd_buf[len - 1] = '\0';
- cmd->command = cmd_buf;
- }
- }
- else {
- /* if we hit EOT we take that as a empty command line ]
- */
- if(!(cmd->command = rl_getline())) {
- cmd->command = empty;
- }
- }
+ int i;
+ char *cp, *ecp;
i = 0;
-
/* Get the command name. Make sure we strip off any
* leading blank space
*/
@@ -207,7 +177,7 @@
cmd->args[0] = 0;
cmd->nargs = 0;
return;
- }
+ }
cp++;
}
*cp++ = 0;
@@ -244,7 +214,6 @@
}
ecp++;
}
- len = (uaddr_t)ecp - (uaddr_t)cp + 1;
ecp++;
} else {
ecp = cp;
@@ -257,7 +226,6 @@
}
ecp++;
}
- len = (uaddr_t)ecp - (uaddr_t)cp;
}
cmd->args[i] = cp;
cp = ecp;
@@ -271,7 +239,7 @@
* return.
*/
if (!(*cp)) {
- if (i <= 128) {
+ if (i <= MAX_ARGS) {
cmd->args[i] = 0;
}
cmd->nargs = i;
@@ -287,6 +255,46 @@
}
/*
+ * get_cmd()
+ */
+static void
+get_cmd(command_t *cmd)
+{
+ int len;
+ char *empty="";
+ char *rl_getline(void);
+
+ static char* cmd_buf;
+ static size_t cmd_len;
+
+ clean_cmd(cmd);
+ cmd->ofp = stdout;
+ cmd->efp = stderr;
+
+ if (!ql_have_terminal) {
+ fprintf(stdout, "\n>> ");
+ fflush(stdout);
+
+ if ((len = getline(&cmd_buf, &cmd_len, stdin)) <= 0) {
+ /* stdin closed */
+ exit(0);
+ }
+ else { /* get rid off the newline character */
+ cmd_buf[len - 1] = '\0';
+ cmd->command = cmd_buf;
+ }
+ }
+ else {
+ /* if we hit EOT we take that as a empty command line ]
+ */
+ if(!(cmd->command = rl_getline())) {
+ cmd->command = empty;
+ }
+ }
+ line_to_words(cmd);
+}
+
+/*
* parse_options()
*/
static int
@@ -663,4 +671,159 @@
t[index++] = '\n';
t[index] = 0;
return (t);
+}
+
+/*
+ * complete_cmds() -- Devide the string from the head of command line to the
+ * position of TAB into words.
+ * Call the completion function for command names, if TAB
+ * is pressed on the first word.
+ * This function is registered to librl by
+ * rl_register_complete_func().
+ *
+ * Call the completion function for command arguments, if
+ * TAB is pressed on the word after the second it.
+ * Note that it is not implemented.
+ */
+char *
+complete_cmds(char *inputline, int tabpos)
+{
+ static command_t cmd;
+ static char cline[DEF_LENGTH];
+ int help_list(command_t *);
+
+ /* copy string from the head of command line to the position of TAB to
+ * buffer. (tabpos + 1) does not exceed DEF_LENGTH, so error check is
+ * not needed.
+ */
+ clean_cmd(&cmd);
+ strncpy(cline, inputline, tabpos + 1);
+ cline[tabpos] = '\0';
+ cmd.command = cline;
+
+ /* command line is devided into a command name and arguments */
+ line_to_words(&cmd);
+
+ /* TAB is pressed on the head of argument */
+ if (*cmd.command && inputline[tabpos - 1] == ' ') {
+ cmd.nargs++;
+ }
+
+ if (cmd.nargs == 0) {
+ /* TAB is pressed on the command name */
+ if (!(*(cmd.command))) {
+ /* if TAB is pressed at the head of a command name,
+ * display the list of command names */
+ cmd.ofp = stdout;
+ fprintf(stdout, "\n");
+ help_list(&cmd); /* display the list of command names */
+ return(DRAW_NEW_ENTIRE_LINE);
+ } else {
+ /* if TAB is pressed in the middle of a command name,
+ call completion function for a command name */
+ return(complete_subcmd_name(cmd.command));
+ }
+ } else {
+ /* TAB is pressed on the command arguments */
+ /* call completion function for command arguments */
+ /* -- not implemented -- */
+ return(PRINT_BEEP);
+ }
+}
+
+/*
+ * complete_subcmd_name() -- This function completes command names.
+ * When there is no candidate, return PRINT_BEEP.
+ * When there is a candidate, return the string.
+ * When there are two or more candidates, return the
+ * identical part of string of them.
+ * When there isn't the identical part of string,
+ * display the list of candidates and return
+ * DRAW_NEW_ENTIRE_LINE.
+ */
+#define SV_CNM(i) save_crp[i]->cmd_name
+char *
+complete_subcmd_name(char *string)
+{
+ int slen, i, j, index;
+ static int cmdnum = 0;
+ static cmd_rec_t **save_crp = (cmd_rec_t **)0;
+ static char cptstr[DEF_LENGTH];
+ cmd_rec_t *ncrp;
+ int candidates_cnt = 0;
+
+ /* get number of commands */
+ if (!cmdnum) {
+ if ((ncrp = first_cmd_rec())) {
+ do {
+ cmdnum++;
+ } while ((ncrp = next_cmd_rec(ncrp)));
+ }
+ }
+ /* allocate pointer array */
+ if (!save_crp) {
+ save_crp = kl_alloc_block(cmdnum * sizeof(cmd_rec_t *), K_PERM);
+ if (klib_error) {
+ fprintf(KL_ERRORFP, "Could not allocate memory for
completion\n");
+ return(PRINT_BEEP);
+ }
+ }
+
+ slen = strlen(string);
+ cptstr[0] = '\0';
+
+ /* find candidates */
+ ncrp = first_cmd_rec();
+ for (i = 0; i < cmdnum; i++) {
+ if (!strncmp(ncrp->cmd_name, string, slen)) {
+ save_crp[candidates_cnt] = ncrp;
+ /* get a string to complete */
+ if (candidates_cnt == 0) {
+ strcpy(cptstr, SV_CNM(candidates_cnt)+slen);
+ } else if (cptstr[0] != '\0') {
+ /* if there are two or more candidates, get the
identical part
+ of string of them */
+ for (j = 0; cptstr[j] != '\0' &&
+ *(SV_CNM(candidates_cnt)+slen+j) !=
'\0' &&
+ cptstr[j] ==
*(SV_CNM(candidates_cnt)+slen+j); j++);
+ cptstr[j] = '\0';
+ }
+ candidates_cnt++;
+ }
+ ncrp = next_cmd_rec(ncrp);
+ }
+
+ if (candidates_cnt == 0) {
+ /* there is no candidate */
+ return(PRINT_BEEP);
+ } else if (candidates_cnt == 1) {
+ /* there is a candidate */
+ strcat(cptstr, " ");
+ return(cptstr);
+ } else {
+ /* there are two or more candidates */
+ if (cptstr[0] == '\0') { /* there is no the identical part of
string */
+ goto print_list;
+ } else { /* there is the identical part of string */
+ return(cptstr);
+ }
+ }
+print_list:
+ fprintf(stdout, "\n");
+ index = candidates_cnt / 4 + (candidates_cnt % 4 ? 1 : 0);
+ for (i = 0; i < index; i++) {
+ fprintf(stdout, "%-17s", SV_CNM(i));
+ if ((j = index + i) < candidates_cnt) {
+ fprintf(stdout, "%-17s", SV_CNM(j));
+ }
+ if ((j = index * 2 + i) < candidates_cnt) {
+ fprintf(stdout, "%-17s", SV_CNM(j));
+ }
+ if ((j = index * 3 + i) < candidates_cnt) {
+ fprintf(stdout, "%-17s", SV_CNM(j));
+ }
+ fprintf(stdout, "\n");
+ }
+ fflush(stdout);
+ return(DRAW_NEW_ENTIRE_LINE);
}
diff -Naur lkcdutils/lcrash/commondefs
lkcdutils+completion_func/lcrash/commondefs
--- lkcdutils/lcrash/commondefs Mon Aug 27 18:41:15 2001
+++ lkcdutils+completion_func/lcrash/commondefs Mon Oct 1 11:49:30 2001
@@ -15,7 +15,7 @@
HPATH = $(DEPTH)/include
LKCDDIR = $(DEPTH)/..
HEADERS = $(HPATH)/command.h $(HPATH)/lcrash.h $(HPATH)/arch/trace.h
-EXTRA_CFLAGS += -I$(HPATH) -I$(LKCDDIR)/libklib/include -I$(TOPDIR)/include \
- -I$(LKCDDIR)/libsial
+EXTRA_CFLAGS += -I$(HPATH) -I$(LKCDDIR)/librl -I$(LKCDDIR)/libklib/include \
+ -I$(TOPDIR)/include -I$(LKCDDIR)/libsial
include $(LKCDDIR)/Rules.make
diff -Naur lkcdutils/lcrash/include/command.h
lkcdutils+completion_func/lcrash/include/command.h
--- lkcdutils/lcrash/include/command.h Mon Aug 27 18:41:15 2001
+++ lkcdutils+completion_func/lcrash/include/command.h Mon Oct 1 11:52:05 2001
@@ -1,6 +1,8 @@
/*
* Copyright 1999 Silicon Graphics, Inc. All rights reserved.
*/
+#include <rl.h>
+
#define MAX_ARGS 128
#define MAX_CMDLINE 256
@@ -158,3 +160,5 @@
char *helpformat(char *);
int process_cmds(void);
int register_cmds(_command_t *);
+char *complete_cmds(char *, int);
+char *complete_subcmd_name(char *);
diff -Naur lkcdutils/lcrash/main.c lkcdutils+completion_func/lcrash/main.c
--- lkcdutils/lcrash/main.c Tue Sep 18 10:12:17 2001
+++ lkcdutils+completion_func/lcrash/main.c Mon Oct 1 11:49:37 2001
@@ -51,6 +51,7 @@
{
int i;
int rl_init(char *, int, int);
+ void rl_register_complete_func(rl_complete_func_t);
program = argv[0];
ofp = stdout;
@@ -255,6 +256,8 @@
if(!rl_init(">> ", 0, 0)) {
exit(1);
}
+ /* register sub-commands-line completion fuction */
+ rl_register_complete_func(complete_cmds);
}
/* fire up sial interpreter and load macros */
diff -Naur lkcdutils/librl/rl.c lkcdutils+completion_func/librl/rl.c
--- lkcdutils/librl/rl.c Mon Aug 27 18:41:16 2001
+++ lkcdutils+completion_func/librl/rl.c Mon Oct 1 11:47:15 2001
@@ -4,7 +4,8 @@
/*
Leaner command input module.
- Support basic command line editing and history mecanism.
+ Support basic command line editing, history mechanism and completion
+ mechanism.
History mechanism supported:
@@ -40,8 +41,14 @@
^R : redraw input line
ESC-f : forward one word
ESC-b : backward one word
- ESC-d : delete next work
+ ESC-d : delete next word
ESC-DEL : delete previous word
+
+
+ Completion mechanism supported:
+
+ When TAB is pressed while having inputted line, call the function
+ registered beforehand to complete line.
*/
#include <stdlib.h>
#include <string.h>
@@ -71,6 +78,7 @@
static char *kwf="\033d";
static char *fw="\033f";
static char *bw="\033b";
+static rl_complete_func_t rl_complete_func; /* function for completing
line */
/*
setup terminal characteristics and allocate initial stuff
@@ -194,6 +202,7 @@
#define KILL_WORD_FORWARD 1016
#define WORD_BACKWARD 1017
#define WORD_FORWARD 1018
+#define COMPLETE_LINE 1019 /* for completing line */
#define NCTRL 16
static int ctrls[NCTRL][2]=
@@ -238,6 +247,10 @@
int i;
int found=0;
+ if (c == '\t') {
+ /* completing line */
+ return COMPLETE_LINE;
+ }
/* check the control characters */
for(i=0;i<NCTRL;i++)
if(ctrls[i][1]==c) return ctrls[i][0];
@@ -531,6 +544,55 @@
}
break;
+ case COMPLETE_LINE: /* complete line */
+ {
+ char *ret;
+ int retstr_len;
+ int save_curpos;
+
+ /* if rl_complete_func is not registered, bip */
+ if (!rl_complete_func) {
+ buz();
+ } else {
+ /* save current cursor position */
+ save_curpos = curpos;
+ /* since command list may be printed in
rl_complete_func(),
+ move cursor to max position */
+ curright(maxpos - curpos);
+ /* call line completion function */
+ ret = rl_complete_func(buf+plen,
save_curpos-plen);
+ if (ret == DRAW_NEW_ENTIRE_LINE) {
+ /* draw new entire line */
+ curpos = 0;
+ showbuf(0);
+ /* resume cursor position */
+ curleft(maxpos - save_curpos);
+ } else if (ret == PRINT_BEEP) {
+ /* resume cursor position */
+ curleft(maxpos - save_curpos);
+ /* print beep character */
+ buz();
+ } else if (ret > 0) {
+ /* insert string that returned before
position of cursor */
+ retstr_len = strlen(ret);
+ /* resume cursor position */
+ curleft(maxpos - save_curpos);
+ if (maxpos+retstr_len>maxl) {
+ /* if we exceed maximum command
length, bip */
+ buz();
+ } else {
+ /* insert string that returned
*/
+ memmove(buf+curpos+retstr_len,
buf+curpos, maxl-curpos-retstr_len);
+ strncpy(buf+curpos, ret,
retstr_len);
+ maxpos+=retstr_len;
+ showbuf(1);
+ curright(retstr_len);
+ }
+ }
+ }
+ }
+ break;
+
default:
{
if(maxpos==maxl) buz();
@@ -574,6 +636,13 @@
return buf;
}
}
+}
+
+/* register function which complete line */
+void
+rl_register_complete_func(rl_complete_func_t complete_func)
+{
+ rl_complete_func = complete_func;
}
diff -Naur lkcdutils/librl/rl.h lkcdutils+completion_func/librl/rl.h
--- lkcdutils/librl/rl.h Mon Aug 27 18:41:16 2001
+++ lkcdutils+completion_func/librl/rl.h Mon Oct 1 11:47:18 2001
@@ -9,5 +9,12 @@
char *hist_cmd(char *);
char *hist_getcmd(int);
int hist_init(int, int);
-char *rl_getline();
+char *rl_getline(void);
int rl_init(char*, int, int);
+
+#define PRINT_BEEP (char *)-1
+#define DRAW_NEW_ENTIRE_LINE (char *)0
+
+typedef char *(*rl_complete_func_t)(char *, int);
+
+void rl_register_complete_func(rl_complete_func_t);
diff -Naur lkcdutils/lkcd_config/commondefs
lkcdutils+completion_func/lkcd_config/commondefs
--- lkcdutils/lkcd_config/commondefs Thu Sep 6 13:41:17 2001
+++ lkcdutils+completion_func/lkcd_config/commondefs Mon Oct 1 14:08:50 2001
@@ -16,7 +16,7 @@
HPATH = $(DEPTH)/../lcrash/include
LKCDDIR = $(DEPTH)/..
HEADERS = $(HPATH)/command.h $(HPATH)/lcrash.h $(HPATH)/arch/trace.h
-EXTRA_CFLAGS += -I$(HPATH) -I$(LKCDDIR)/libklib/include \
+EXTRA_CFLAGS += -I$(HPATH) -I$(LKCDDIR)/librl -I$(LKCDDIR)/libklib/include \
-I$(TOPDIR)/include -I$(LKCDDIR)/libsial
diff -Naur lkcdutils/lkcd_ksyms/commondefs
lkcdutils+completion_func/lkcd_ksyms/commondefs
--- lkcdutils/lkcd_ksyms/commondefs Thu Sep 6 13:41:21 2001
+++ lkcdutils+completion_func/lkcd_ksyms/commondefs Mon Oct 1 14:09:05 2001
@@ -15,7 +15,7 @@
HPATH = $(DEPTH)/../lcrash/include
LKCDDIR = $(DEPTH)/..
HEADERS = $(HPATH)/command.h $(HPATH)/lcrash.h $(HPATH)/arch/trace.h
-EXTRA_CFLAGS += -I$(HPATH) -I$(LKCDDIR)/libklib/include \
+EXTRA_CFLAGS += -I$(HPATH) -I$(LKCDDIR)/librl -I$(LKCDDIR)/libklib/include \
-I$(TOPDIR)/include -I$(LKCDDIR)/libsial
Regards,
Naomi Haseo
|