lkcd
[Top] [All Lists]

Re: lcrash sub-commands line completion

To: lkcd@xxxxxxxxxxx
Subject: Re: lcrash sub-commands line completion
From: naomi@xxxxxxxxxxxxxxx
Date: Wed, 10 Oct 2001 10:04:17 +0900
In-reply-to: Your message of "Tue, 04 Sep 2001 16:27:53 +0900" <20010904162753R.naomi@xxxxxxxxxxxxxxx>
References: <20010904162753R.naomi@xxxxxxxxxxxxxxx>
Sender: owner-lkcd@xxxxxxxxxxx
> 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






<Prev in Thread] Current Thread [Next in Thread>