diff options
-rw-r--r-- | applets/busybox.c | 4 | ||||
-rwxr-xr-x | applets/busybox.mkll | 3 | ||||
-rwxr-xr-x | applets/busybox.sh | 11 | ||||
-rw-r--r-- | busybox.c | 4 | ||||
-rw-r--r-- | busybox.def.h | 17 | ||||
-rwxr-xr-x | busybox.mkll | 3 | ||||
-rwxr-xr-x | busybox.sh | 11 | ||||
-rw-r--r-- | cmdedit.c | 260 | ||||
-rw-r--r-- | kill.c | 13 | ||||
-rw-r--r-- | lash.c | 12 | ||||
-rw-r--r-- | procps/kill.c | 13 | ||||
-rw-r--r-- | sh.c | 12 | ||||
-rw-r--r-- | shell/cmdedit.c | 260 | ||||
-rw-r--r-- | shell/lash.c | 12 | ||||
-rw-r--r-- | utility.c | 5 |
15 files changed, 529 insertions, 111 deletions
diff --git a/applets/busybox.c b/applets/busybox.c index 7582647d5..ee745e73e 100644 --- a/applets/busybox.c +++ b/applets/busybox.c @@ -4,10 +4,6 @@ #include <string.h> #include <errno.h> -#ifndef BB_INIT -#undef BB_FEATURE_LINUXRC -#endif - static int been_there_done_that = 0; /* It has been alledged that doing such things can diff --git a/applets/busybox.mkll b/applets/busybox.mkll index fa1bff209..90f28e8b7 100755 --- a/applets/busybox.mkll +++ b/applets/busybox.mkll @@ -4,7 +4,8 @@ DF="busybox.def.h" MF="busybox.c" -LIST="$(sed -n '/^#define/{s/^#define BB_FEATURE_.*//g;s/^#define //p;}' $DF)" +#LIST="$(sed -n '/^#define/{s/^#define BB_FEATURE_.*//g;s/^#define //p;}' $DF)" +LIST="$(cpp $DF -dM | sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1/gp;' | sort)" for def in ${LIST}; do i=`sed -n '/^#ifdef \<'$def'\>.*/,/^#endif/{ s/.*\"\(.*\)\".*\(_BB_DIR_[A-Z_]*\).*$/\2\/\1/gp; }' $MF` diff --git a/applets/busybox.sh b/applets/busybox.sh index 304ac87e7..c4e241f84 100755 --- a/applets/busybox.sh +++ b/applets/busybox.sh @@ -1,3 +1,10 @@ #!/bin/sh -sed -n -e 's/^#define.*BB_FEATURE.*$//g;y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/;' \ - -e '/^#define/{s/.*bb_//;s/$/.o/p;}' busybox.def.h + +# I added in the extra "ls" so only source files that +# actually exist will show up in the compile list. +ls -1 ` \ + cpp busybox.def.h -dM | \ + sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \ + | tr [:upper:] [:lower:] | sort +` 2>/dev/null | sed -e 's/\.c$/\.o/g' + @@ -4,10 +4,6 @@ #include <string.h> #include <errno.h> -#ifndef BB_INIT -#undef BB_FEATURE_LINUXRC -#endif - static int been_there_done_that = 0; /* It has been alledged that doing such things can diff --git a/busybox.def.h b/busybox.def.h index d78a0efe8..cfafc29c8 100644 --- a/busybox.def.h +++ b/busybox.def.h @@ -40,6 +40,7 @@ // Don't bother turning BB_INSMOD on. It doesn't work. //#define BB_INSMOD #define BB_KILL +#define BB_KILLALL #define BB_KLOGD //#define BB_LENGTH #define BB_LN @@ -198,11 +199,23 @@ #define BB_MTAB #endif // -#ifdef BB_FEATURE_FULL_REGULAR_EXPRESSIONS +#if defined BB_FEATURE_FULL_REGULAR_EXPRESSIONS && (defined BB_SED || defined BB_GREP ) #define BB_REGEXP #endif // -#ifdef BB_FEATURE_SH_COMMAND_EDITING +#if defined BB_FEATURE_SH_COMMAND_EDITING && defined BB_SH #define BB_CMDEDIT #endif // +#ifdef BB_KILLALL +#ifndef BB_KILL +#define BB_KILL +#endif +#endif +// +#ifdef BB_FEATURE_LINUXRC +#ifndef BB_INIT +#define BB_INIT +#endif +#endif +// diff --git a/busybox.mkll b/busybox.mkll index fa1bff209..90f28e8b7 100755 --- a/busybox.mkll +++ b/busybox.mkll @@ -4,7 +4,8 @@ DF="busybox.def.h" MF="busybox.c" -LIST="$(sed -n '/^#define/{s/^#define BB_FEATURE_.*//g;s/^#define //p;}' $DF)" +#LIST="$(sed -n '/^#define/{s/^#define BB_FEATURE_.*//g;s/^#define //p;}' $DF)" +LIST="$(cpp $DF -dM | sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1/gp;' | sort)" for def in ${LIST}; do i=`sed -n '/^#ifdef \<'$def'\>.*/,/^#endif/{ s/.*\"\(.*\)\".*\(_BB_DIR_[A-Z_]*\).*$/\2\/\1/gp; }' $MF` diff --git a/busybox.sh b/busybox.sh index 304ac87e7..c4e241f84 100755 --- a/busybox.sh +++ b/busybox.sh @@ -1,3 +1,10 @@ #!/bin/sh -sed -n -e 's/^#define.*BB_FEATURE.*$//g;y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/;' \ - -e '/^#define/{s/.*bb_//;s/$/.o/p;}' busybox.def.h + +# I added in the extra "ls" so only source files that +# actually exist will show up in the compile list. +ls -1 ` \ + cpp busybox.def.h -dM | \ + sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \ + | tr [:upper:] [:lower:] | sort +` 2>/dev/null | sed -e 's/\.c$/\.o/g' + @@ -47,6 +47,8 @@ #define ESC 27 #define DEL 127 +#define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0) +#define whitespace(c) (((c) == ' ') || ((c) == '\t')) static struct history *his_front = NULL; /* First element in command line list */ static struct history *his_end = NULL; /* Last element in command line list */ @@ -104,7 +106,7 @@ void cmdedit_reset_term(void) xioctl(fileno(stdin), TCSETA, (void *) &old_term); } -void gotaSignal(int sig) +void prepareToDie(int sig) { cmdedit_reset_term(); fprintf(stdout, "\n"); @@ -175,6 +177,40 @@ void input_backspace(int outputFd, int *cursor, int *len) } } +char **username_completion_matches( char* matchBuf) +{ + fprintf(stderr, "\nin username_completion_matches\n"); + return ( (char**) NULL); +} +char **command_completion_matches( char* matchBuf) +{ + fprintf(stderr, "\nin command_completion_matches\n"); + return ( (char**) NULL); +} +char **directory_completion_matches( char* matchBuf) +{ + fprintf(stderr, "\nin directory_completion_matches\n"); + return ( (char**) NULL); +} + +/* + * This function is used to grab a character buffer + * from the input file descriptor and allows you to + * a string with full command editing (sortof like + * a mini readline). + * + * The following standard commands are not implemented: + * ESC-b -- Move back one word + * ESC-f -- Move forward one word + * ESC-d -- Delete back one word + * ESC-h -- Delete forward one word + * CTL-t -- Transpose two characters + * + * Furthermore, the "vi" command editing keys are not implemented. + * + * TODO: implement TAB command completion. :) + * + */ extern int cmdedit_read_input(int inputFd, int outputFd, char command[BUFSIZ]) { @@ -185,6 +221,8 @@ extern int cmdedit_read_input(int inputFd, int outputFd, int cursor = 0; int break_out = 0; int ret = 0; + int lastWasTab = FALSE; + char **matches = (char **)NULL; char c = 0; struct history *hp = his_end; @@ -209,90 +247,233 @@ extern int cmdedit_read_input(int inputFd, int outputFd, if ((ret = read(inputFd, &c, 1)) < 1) return ret; - + switch (c) { - case 1: /* Control-A Beginning of line */ + case 1: + /* Control-a -- Beginning of line */ input_home(outputFd, &cursor); - break; - case 5: /* Control-E EOL */ + case 5: + /* Control-e -- End of line */ input_end(outputFd, &cursor, len); break; - case 4: /* Control-D */ + case 2: + /* Control-b -- Move back one character */ + if (cursor > 0) { + xwrite(outputFd, "\033[D", 3); + cursor--; + } + break; + case 6: + /* Control-f -- Move forward one character */ + if (cursor < len) { + xwrite(outputFd, "\033[C", 3); + cursor++; + } + break; + case 4: + /* Control-d -- Delete one character */ if (cursor != len) { input_delete(outputFd, cursor); len--; + } else if (len == 0) { + prepareToDie(0); + exit(0); + } + break; + case 14: + /* Control-n -- Get next command */ + if (hp && hp->n && hp->n->s) { + free( hp->s); + hp->s = strdup(parsenextc); + hp = hp->n; + goto hop; } break; - case '\b': /* Backspace */ + case 16: + /* Control-p -- Get previous command */ + if (hp && hp->p) { + free( hp->s); + hp->s = strdup(parsenextc); + hp = hp->p; + goto hop; + } + break; + case '\t': + { + /* Do TAB completion */ + int in_command_position=0, ti=len-1; + + if (lastWasTab == FALSE) { + char *tmp; + char *matchBuf; + + if (matches) { + free(matches); + matches = (char **)NULL; + } + + matchBuf = (char *) calloc(BUFSIZ, sizeof(char)); + + /* Make a local copy of the string -- up + * to the the position of the cursor */ + strcpy( matchBuf, parsenextc); + matchBuf[cursor+1] = '\0'; + + /* skip leading white space */ + tmp = matchBuf; + while (*tmp && isspace(*tmp)) { + (tmp)++; + ti++; + } + + /* Determine if this is a command word or not */ + //while ((ti > -1) && (whitespace (matchBuf[ti]))) { +//printf("\nti=%d\n", ti); + // ti--; + // } +printf("\nti=%d\n", ti); + + if (ti < 0) { + in_command_position++; + } else if (member(matchBuf[ti], ";|&{(`")) { + int this_char, prev_char; + in_command_position++; + /* Handle the two character tokens `>&', `<&', and `>|'. + We are not in a command position after one of these. */ + this_char = matchBuf[ti]; + prev_char = matchBuf[ti - 1]; + + if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) || + (this_char == '|' && prev_char == '>')) { + in_command_position = 0; + } + /* For now, do not bother with catching quoted + * expressions and marking them as not in command + * positions. Some other day. Or not. + */ + //else if (char_is_quoted (matchBuf, ti)) { + // in_command_position = 0; + //} + } +printf("\nin_command_position=%d\n", in_command_position); + /* If the word starts in `~', and there is no slash in the word, + * then try completing this word as a username. */ + if (*matchBuf == '~' && !strchr (matchBuf, '/')) + matches = username_completion_matches(matchBuf); + + /* If this word is in a command position, then complete over possible + * command names, including aliases, built-ins, and executables. */ + if (!matches && in_command_position) { + matches = command_completion_matches(matchBuf); + + /* If we are attempting command completion and nothing matches, + * then try and match directories as a last resort... */ + if (!matches) + matches = directory_completion_matches(matchBuf); + } + } else { + printf("\nprinting match list\n"); + } + /* Rewrite the whole line (for debugging) */ + for (; cursor > 0; cursor--) + xwrite(outputFd, "\b", 1); + len = strlen(parsenextc); + xwrite(outputFd, parsenextc, len); + cursor = len; + break; + } + case '\b': case DEL: + /* Backspace */ input_backspace(outputFd, &cursor, &len); break; - case '\n': /* Enter */ + case '\n': + /* Enter */ *(parsenextc + len++ + 1) = c; xwrite(outputFd, &c, 1); break_out = 1; break; - case ESC: /* escape sequence follows */ + case ESC: { + /* escape sequence follows */ if ((ret = read(inputFd, &c, 1)) < 1) return ret; if (c == '[') { /* 91 */ if ((ret = read(inputFd, &c, 1)) < 1) return ret; - + switch (c) { case 'A': - if (hp && hp->p) { /* Up */ + /* Up Arrow -- Get previous command */ + if (hp && hp->p) { + free( hp->s); + hp->s = strdup(parsenextc); hp = hp->p; goto hop; } break; case 'B': - if (hp && hp->n && hp->n->s) { /* Down */ + /* Down Arrow -- Get next command */ + if (hp && hp->n && hp->n->s) { + free( hp->s); + hp->s = strdup(parsenextc); hp = hp->n; goto hop; } break; - hop: /* hop */ + /* This is where we rewrite the line + * using the selected history item */ + hop: len = strlen(parsenextc); - for (; cursor > 0; cursor--) /* return to begining of line */ + /* return to begining of line */ + for (; cursor > 0; cursor--) xwrite(outputFd, "\b", 1); + xwrite(outputFd, parsenextc, len); - for (j = 0; j < len; j++) /* erase old command */ + /* erase old command */ + for (j = 0; j < len; j++) xwrite(outputFd, " ", 1); - for (j = len; j > 0; j--) /* return to begining of line */ + /* return to begining of line */ + for (j = len; j > 0; j--) xwrite(outputFd, "\b", 1); - strcpy(parsenextc, hp->s); /* write new command */ + memset(parsenextc, 0, BUFSIZ); + /* write new command */ + strcpy(parsenextc, hp->s); len = strlen(hp->s); xwrite(outputFd, parsenextc, len); cursor = len; break; - case 'C': /* Right */ + case 'C': + /* Right Arrow -- Move forward one character */ if (cursor < len) { xwrite(outputFd, "\033[C", 3); cursor++; } break; - case 'D': /* Left */ + case 'D': + /* Left Arrow -- Move back one character */ if (cursor > 0) { xwrite(outputFd, "\033[D", 3); cursor--; } break; - case '3': /* Delete */ + case '3': + /* Delete */ if (cursor != len) { input_delete(outputFd, cursor); len--; } break; - case '1': /* Home (Ctrl-A) */ + case '1': + /* Home (Ctrl-A) */ input_home(outputFd, &cursor); break; - case '4': /* End (Ctrl-E) */ + case '4': + /* End (Ctrl-E) */ input_end(outputFd, &cursor, len); break; } @@ -300,20 +481,24 @@ extern int cmdedit_read_input(int inputFd, int outputFd, if ((ret = read(inputFd, &c, 1)) < 1) return ret; /* read 126 (~) */ } - if (c == 'O') { /* 79 */ + if (c == 'O') { + /* 79 */ if ((ret = read(inputFd, &c, 1)) < 1) return ret; switch (c) { - case 'H': /* Home (xterm) */ + case 'H': + /* Home (xterm) */ input_home(outputFd, &cursor); break; - case 'F': /* End (xterm) */ + case 'F': + /* End (xterm) */ input_end(outputFd, &cursor, len); break; } } c = 0; break; + } default: /* If it's regular input, do the normal thing */ @@ -343,6 +528,10 @@ extern int cmdedit_read_input(int inputFd, int outputFd, xwrite(outputFd, &c, 1); break; } + if (c=='\t') + lastWasTab = TRUE; + else + lastWasTab = FALSE; if (break_out) /* Enter is the command terminator, no more input. */ break; @@ -353,32 +542,33 @@ extern int cmdedit_read_input(int inputFd, int outputFd, reset_term = 0; - if (*(parsenextc)) { /* Handle command history log */ + /* Handle command history log */ + if (*(parsenextc)) { struct history *h = his_end; - if (!h) { /* No previous history */ + if (!h) { + /* No previous history */ h = his_front = malloc(sizeof(struct history)); h->n = malloc(sizeof(struct history)); h->p = NULL; h->s = strdup(parsenextc); - h->n->p = h; h->n->n = NULL; h->n->s = NULL; his_end = h->n; history_counter++; - } else { /* Add a new history command */ - + } else { + /* Add a new history command */ h->n = malloc(sizeof(struct history)); - h->n->p = h; h->n->n = NULL; h->n->s = NULL; h->s = strdup(parsenextc); his_end = h->n; - if (history_counter >= MAX_HISTORY) { /* After max history, remove the last known command */ + /* After max history, remove the oldest command */ + if (history_counter >= MAX_HISTORY) { struct history *p = his_front->n; @@ -398,8 +588,8 @@ extern int cmdedit_read_input(int inputFd, int outputFd, extern void cmdedit_init(void) { atexit(cmdedit_reset_term); - signal(SIGINT, gotaSignal); - signal(SIGQUIT, gotaSignal); - signal(SIGTERM, gotaSignal); + signal(SIGINT, prepareToDie); + signal(SIGQUIT, prepareToDie); + signal(SIGTERM, prepareToDie); } #endif /* BB_FEATURE_SH_COMMAND_EDITING */ @@ -36,11 +36,12 @@ static const char *kill_usage = "Send a signal (default is SIGTERM) to the specified process(es).\n\n" "Options:\n" "\t-l\tList all signal names and numbers.\n\n"; +#ifdef BB_KILLALL static const char *killall_usage = "killall [-signal] process-name [process-name ...]\n\n" "Send a signal (default is SIGTERM) to the specified process(es).\n\n" "Options:\n" "\t-l\tList all signal names and numbers.\n\n"; - +#endif #define KILL 0 #define KILLALL 1 @@ -132,10 +133,15 @@ extern int kill_main(int argc, char **argv) int whichApp, sig = SIGTERM; const char *appUsage; +#ifdef BB_KILLALL /* Figure out what we are trying to do here */ whichApp = (strcmp(*argv, "killall") == 0)? KILLALL : KILL; appUsage = (whichApp == KILLALL)? killall_usage : kill_usage; +#else + whichApp = KILL; + appUsage = kill_usage; +#endif argc--; argv++; @@ -213,7 +219,9 @@ extern int kill_main(int argc, char **argv) fatalError( "Could not kill pid '%d': %s\n", pid, strerror(errno)); argv++; } - } else { + } +#ifdef BB_KILLALL + else { /* Looks like they want to do a killall. Do that */ while (--argc >= 0) { int pid; @@ -225,6 +233,7 @@ extern int kill_main(int argc, char **argv) argv++; } } +#endif exit(TRUE); @@ -98,7 +98,7 @@ static int shell_fg_bg(struct job *cmd, struct jobSet *jobList); static int shell_help(struct job *cmd, struct jobSet *junk); static int shell_jobs(struct job *dummy, struct jobSet *jobList); static int shell_pwd(struct job *dummy, struct jobSet *junk); -static int shell_set(struct job *cmd, struct jobSet *junk); +static int shell_export(struct job *cmd, struct jobSet *junk); static int shell_source(struct job *cmd, struct jobSet *jobList); static int shell_unset(struct job *cmd, struct jobSet *junk); @@ -120,7 +120,7 @@ static struct builtInCommand bltins[] = { {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg}, {"jobs", "Lists the active jobs", "jobs", shell_jobs}, {"pwd", "Print current directory", "pwd", shell_pwd}, - {"set", "Set environment variable", "set [VAR=value]", shell_set}, + {"export", "Set environment variable", "export [VAR=value]", shell_export}, {"unset", "Unset environment variable", "unset VAR", shell_unset}, {".", "Source-in and run commands in a file", ". filename", @@ -182,7 +182,7 @@ static int shell_exit(struct job *cmd, struct jobSet *junk) static int shell_fg_bg(struct job *cmd, struct jobSet *jobList) { int i, jobNum; - struct job *job; + struct job *job=NULL; if (!jobList->head) { if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) { @@ -268,8 +268,8 @@ static int shell_pwd(struct job *dummy, struct jobSet *junk) return TRUE; } -/* built-in 'set VAR=value' handler */ -static int shell_set(struct job *cmd, struct jobSet *junk) +/* built-in 'export VAR=value' handler */ +static int shell_export(struct job *cmd, struct jobSet *junk) { int res; @@ -278,7 +278,7 @@ static int shell_set(struct job *cmd, struct jobSet *junk) } res = putenv(cmd->progs[0].argv[1]); if (res) - fprintf(stdout, "set: %s\n", strerror(errno)); + fprintf(stdout, "export: %s\n", strerror(errno)); return (res); } diff --git a/procps/kill.c b/procps/kill.c index 8a99e0f9e..10343a150 100644 --- a/procps/kill.c +++ b/procps/kill.c @@ -36,11 +36,12 @@ static const char *kill_usage = "Send a signal (default is SIGTERM) to the specified process(es).\n\n" "Options:\n" "\t-l\tList all signal names and numbers.\n\n"; +#ifdef BB_KILLALL static const char *killall_usage = "killall [-signal] process-name [process-name ...]\n\n" "Send a signal (default is SIGTERM) to the specified process(es).\n\n" "Options:\n" "\t-l\tList all signal names and numbers.\n\n"; - +#endif #define KILL 0 #define KILLALL 1 @@ -132,10 +133,15 @@ extern int kill_main(int argc, char **argv) int whichApp, sig = SIGTERM; const char *appUsage; +#ifdef BB_KILLALL /* Figure out what we are trying to do here */ whichApp = (strcmp(*argv, "killall") == 0)? KILLALL : KILL; appUsage = (whichApp == KILLALL)? killall_usage : kill_usage; +#else + whichApp = KILL; + appUsage = kill_usage; +#endif argc--; argv++; @@ -213,7 +219,9 @@ extern int kill_main(int argc, char **argv) fatalError( "Could not kill pid '%d': %s\n", pid, strerror(errno)); argv++; } - } else { + } +#ifdef BB_KILLALL + else { /* Looks like they want to do a killall. Do that */ while (--argc >= 0) { int pid; @@ -225,6 +233,7 @@ extern int kill_main(int argc, char **argv) argv++; } } +#endif exit(TRUE); @@ -98,7 +98,7 @@ static int shell_fg_bg(struct job *cmd, struct jobSet *jobList); static int shell_help(struct job *cmd, struct jobSet *junk); static int shell_jobs(struct job *dummy, struct jobSet *jobList); static int shell_pwd(struct job *dummy, struct jobSet *junk); -static int shell_set(struct job *cmd, struct jobSet *junk); +static int shell_export(struct job *cmd, struct jobSet *junk); static int shell_source(struct job *cmd, struct jobSet *jobList); static int shell_unset(struct job *cmd, struct jobSet *junk); @@ -120,7 +120,7 @@ static struct builtInCommand bltins[] = { {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg}, {"jobs", "Lists the active jobs", "jobs", shell_jobs}, {"pwd", "Print current directory", "pwd", shell_pwd}, - {"set", "Set environment variable", "set [VAR=value]", shell_set}, + {"export", "Set environment variable", "export [VAR=value]", shell_export}, {"unset", "Unset environment variable", "unset VAR", shell_unset}, {".", "Source-in and run commands in a file", ". filename", @@ -182,7 +182,7 @@ static int shell_exit(struct job *cmd, struct jobSet *junk) static int shell_fg_bg(struct job *cmd, struct jobSet *jobList) { int i, jobNum; - struct job *job; + struct job *job=NULL; if (!jobList->head) { if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) { @@ -268,8 +268,8 @@ static int shell_pwd(struct job *dummy, struct jobSet *junk) return TRUE; } -/* built-in 'set VAR=value' handler */ -static int shell_set(struct job *cmd, struct jobSet *junk) +/* built-in 'export VAR=value' handler */ +static int shell_export(struct job *cmd, struct jobSet *junk) { int res; @@ -278,7 +278,7 @@ static int shell_set(struct job *cmd, struct jobSet *junk) } res = putenv(cmd->progs[0].argv[1]); if (res) - fprintf(stdout, "set: %s\n", strerror(errno)); + fprintf(stdout, "export: %s\n", strerror(errno)); return (res); } diff --git a/shell/cmdedit.c b/shell/cmdedit.c index d1604f1d1..314e8cd66 100644 --- a/shell/cmdedit.c +++ b/shell/cmdedit.c @@ -47,6 +47,8 @@ #define ESC 27 #define DEL 127 +#define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0) +#define whitespace(c) (((c) == ' ') || ((c) == '\t')) static struct history *his_front = NULL; /* First element in command line list */ static struct history *his_end = NULL; /* Last element in command line list */ @@ -104,7 +106,7 @@ void cmdedit_reset_term(void) xioctl(fileno(stdin), TCSETA, (void *) &old_term); } -void gotaSignal(int sig) +void prepareToDie(int sig) { cmdedit_reset_term(); fprintf(stdout, "\n"); @@ -175,6 +177,40 @@ void input_backspace(int outputFd, int *cursor, int *len) } } +char **username_completion_matches( char* matchBuf) +{ + fprintf(stderr, "\nin username_completion_matches\n"); + return ( (char**) NULL); +} +char **command_completion_matches( char* matchBuf) +{ + fprintf(stderr, "\nin command_completion_matches\n"); + return ( (char**) NULL); +} +char **directory_completion_matches( char* matchBuf) +{ + fprintf(stderr, "\nin directory_completion_matches\n"); + return ( (char**) NULL); +} + +/* + * This function is used to grab a character buffer + * from the input file descriptor and allows you to + * a string with full command editing (sortof like + * a mini readline). + * + * The following standard commands are not implemented: + * ESC-b -- Move back one word + * ESC-f -- Move forward one word + * ESC-d -- Delete back one word + * ESC-h -- Delete forward one word + * CTL-t -- Transpose two characters + * + * Furthermore, the "vi" command editing keys are not implemented. + * + * TODO: implement TAB command completion. :) + * + */ extern int cmdedit_read_input(int inputFd, int outputFd, char command[BUFSIZ]) { @@ -185,6 +221,8 @@ extern int cmdedit_read_input(int inputFd, int outputFd, int cursor = 0; int break_out = 0; int ret = 0; + int lastWasTab = FALSE; + char **matches = (char **)NULL; char c = 0; struct history *hp = his_end; @@ -209,90 +247,233 @@ extern int cmdedit_read_input(int inputFd, int outputFd, if ((ret = read(inputFd, &c, 1)) < 1) return ret; - + switch (c) { - case 1: /* Control-A Beginning of line */ + case 1: + /* Control-a -- Beginning of line */ input_home(outputFd, &cursor); - break; - case 5: /* Control-E EOL */ + case 5: + /* Control-e -- End of line */ input_end(outputFd, &cursor, len); break; - case 4: /* Control-D */ + case 2: + /* Control-b -- Move back one character */ + if (cursor > 0) { + xwrite(outputFd, "\033[D", 3); + cursor--; + } + break; + case 6: + /* Control-f -- Move forward one character */ + if (cursor < len) { + xwrite(outputFd, "\033[C", 3); + cursor++; + } + break; + case 4: + /* Control-d -- Delete one character */ if (cursor != len) { input_delete(outputFd, cursor); len--; + } else if (len == 0) { + prepareToDie(0); + exit(0); + } + break; + case 14: + /* Control-n -- Get next command */ + if (hp && hp->n && hp->n->s) { + free( hp->s); + hp->s = strdup(parsenextc); + hp = hp->n; + goto hop; } break; - case '\b': /* Backspace */ + case 16: + /* Control-p -- Get previous command */ + if (hp && hp->p) { + free( hp->s); + hp->s = strdup(parsenextc); + hp = hp->p; + goto hop; + } + break; + case '\t': + { + /* Do TAB completion */ + int in_command_position=0, ti=len-1; + + if (lastWasTab == FALSE) { + char *tmp; + char *matchBuf; + + if (matches) { + free(matches); + matches = (char **)NULL; + } + + matchBuf = (char *) calloc(BUFSIZ, sizeof(char)); + + /* Make a local copy of the string -- up + * to the the position of the cursor */ + strcpy( matchBuf, parsenextc); + matchBuf[cursor+1] = '\0'; + + /* skip leading white space */ + tmp = matchBuf; + while (*tmp && isspace(*tmp)) { + (tmp)++; + ti++; + } + + /* Determine if this is a command word or not */ + //while ((ti > -1) && (whitespace (matchBuf[ti]))) { +//printf("\nti=%d\n", ti); + // ti--; + // } +printf("\nti=%d\n", ti); + + if (ti < 0) { + in_command_position++; + } else if (member(matchBuf[ti], ";|&{(`")) { + int this_char, prev_char; + in_command_position++; + /* Handle the two character tokens `>&', `<&', and `>|'. + We are not in a command position after one of these. */ + this_char = matchBuf[ti]; + prev_char = matchBuf[ti - 1]; + + if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) || + (this_char == '|' && prev_char == '>')) { + in_command_position = 0; + } + /* For now, do not bother with catching quoted + * expressions and marking them as not in command + * positions. Some other day. Or not. + */ + //else if (char_is_quoted (matchBuf, ti)) { + // in_command_position = 0; + //} + } +printf("\nin_command_position=%d\n", in_command_position); + /* If the word starts in `~', and there is no slash in the word, + * then try completing this word as a username. */ + if (*matchBuf == '~' && !strchr (matchBuf, '/')) + matches = username_completion_matches(matchBuf); + + /* If this word is in a command position, then complete over possible + * command names, including aliases, built-ins, and executables. */ + if (!matches && in_command_position) { + matches = command_completion_matches(matchBuf); + + /* If we are attempting command completion and nothing matches, + * then try and match directories as a last resort... */ + if (!matches) + matches = directory_completion_matches(matchBuf); + } + } else { + printf("\nprinting match list\n"); + } + /* Rewrite the whole line (for debugging) */ + for (; cursor > 0; cursor--) + xwrite(outputFd, "\b", 1); + len = strlen(parsenextc); + xwrite(outputFd, parsenextc, len); + cursor = len; + break; + } + case '\b': case DEL: + /* Backspace */ input_backspace(outputFd, &cursor, &len); break; - case '\n': /* Enter */ + case '\n': + /* Enter */ *(parsenextc + len++ + 1) = c; xwrite(outputFd, &c, 1); break_out = 1; break; - case ESC: /* escape sequence follows */ + case ESC: { + /* escape sequence follows */ if ((ret = read(inputFd, &c, 1)) < 1) return ret; if (c == '[') { /* 91 */ if ((ret = read(inputFd, &c, 1)) < 1) return ret; - + switch (c) { case 'A': - if (hp && hp->p) { /* Up */ + /* Up Arrow -- Get previous command */ + if (hp && hp->p) { + free( hp->s); + hp->s = strdup(parsenextc); hp = hp->p; goto hop; } break; case 'B': - if (hp && hp->n && hp->n->s) { /* Down */ + /* Down Arrow -- Get next command */ + if (hp && hp->n && hp->n->s) { + free( hp->s); + hp->s = strdup(parsenextc); hp = hp->n; goto hop; } break; - hop: /* hop */ + /* This is where we rewrite the line + * using the selected history item */ + hop: len = strlen(parsenextc); - for (; cursor > 0; cursor--) /* return to begining of line */ + /* return to begining of line */ + for (; cursor > 0; cursor--) xwrite(outputFd, "\b", 1); + xwrite(outputFd, parsenextc, len); - for (j = 0; j < len; j++) /* erase old command */ + /* erase old command */ + for (j = 0; j < len; j++) xwrite(outputFd, " ", 1); - for (j = len; j > 0; j--) /* return to begining of line */ + /* return to begining of line */ + for (j = len; j > 0; j--) xwrite(outputFd, "\b", 1); - strcpy(parsenextc, hp->s); /* write new command */ + memset(parsenextc, 0, BUFSIZ); + /* write new command */ + strcpy(parsenextc, hp->s); len = strlen(hp->s); xwrite(outputFd, parsenextc, len); cursor = len; break; - case 'C': /* Right */ + case 'C': + /* Right Arrow -- Move forward one character */ if (cursor < len) { xwrite(outputFd, "\033[C", 3); cursor++; } break; - case 'D': /* Left */ + case 'D': + /* Left Arrow -- Move back one character */ if (cursor > 0) { xwrite(outputFd, "\033[D", 3); cursor--; } break; - case '3': /* Delete */ + case '3': + /* Delete */ if (cursor != len) { input_delete(outputFd, cursor); len--; } break; - case '1': /* Home (Ctrl-A) */ + case '1': + /* Home (Ctrl-A) */ input_home(outputFd, &cursor); break; - case '4': /* End (Ctrl-E) */ + case '4': + /* End (Ctrl-E) */ input_end(outputFd, &cursor, len); break; } @@ -300,20 +481,24 @@ extern int cmdedit_read_input(int inputFd, int outputFd, if ((ret = read(inputFd, &c, 1)) < 1) return ret; /* read 126 (~) */ } - if (c == 'O') { /* 79 */ + if (c == 'O') { + /* 79 */ if ((ret = read(inputFd, &c, 1)) < 1) return ret; switch (c) { - case 'H': /* Home (xterm) */ + case 'H': + /* Home (xterm) */ input_home(outputFd, &cursor); break; - case 'F': /* End (xterm) */ + case 'F': + /* End (xterm) */ input_end(outputFd, &cursor, len); break; } } c = 0; break; + } default: /* If it's regular input, do the normal thing */ @@ -343,6 +528,10 @@ extern int cmdedit_read_input(int inputFd, int outputFd, xwrite(outputFd, &c, 1); break; } + if (c=='\t') + lastWasTab = TRUE; + else + lastWasTab = FALSE; if (break_out) /* Enter is the command terminator, no more input. */ break; @@ -353,32 +542,33 @@ extern int cmdedit_read_input(int inputFd, int outputFd, reset_term = 0; - if (*(parsenextc)) { /* Handle command history log */ + /* Handle command history log */ + if (*(parsenextc)) { struct history *h = his_end; - if (!h) { /* No previous history */ + if (!h) { + /* No previous history */ h = his_front = malloc(sizeof(struct history)); h->n = malloc(sizeof(struct history)); h->p = NULL; h->s = strdup(parsenextc); - h->n->p = h; h->n->n = NULL; h->n->s = NULL; his_end = h->n; history_counter++; - } else { /* Add a new history command */ - + } else { + /* Add a new history command */ h->n = malloc(sizeof(struct history)); - h->n->p = h; h->n->n = NULL; h->n->s = NULL; h->s = strdup(parsenextc); his_end = h->n; - if (history_counter >= MAX_HISTORY) { /* After max history, remove the last known command */ + /* After max history, remove the oldest command */ + if (history_counter >= MAX_HISTORY) { struct history *p = his_front->n; @@ -398,8 +588,8 @@ extern int cmdedit_read_input(int inputFd, int outputFd, extern void cmdedit_init(void) { atexit(cmdedit_reset_term); - signal(SIGINT, gotaSignal); - signal(SIGQUIT, gotaSignal); - signal(SIGTERM, gotaSignal); + signal(SIGINT, prepareToDie); + signal(SIGQUIT, prepareToDie); + signal(SIGTERM, prepareToDie); } #endif /* BB_FEATURE_SH_COMMAND_EDITING */ diff --git a/shell/lash.c b/shell/lash.c index e143cfe74..068159697 100644 --- a/shell/lash.c +++ b/shell/lash.c @@ -98,7 +98,7 @@ static int shell_fg_bg(struct job *cmd, struct jobSet *jobList); static int shell_help(struct job *cmd, struct jobSet *junk); static int shell_jobs(struct job *dummy, struct jobSet *jobList); static int shell_pwd(struct job *dummy, struct jobSet *junk); -static int shell_set(struct job *cmd, struct jobSet *junk); +static int shell_export(struct job *cmd, struct jobSet *junk); static int shell_source(struct job *cmd, struct jobSet *jobList); static int shell_unset(struct job *cmd, struct jobSet *junk); @@ -120,7 +120,7 @@ static struct builtInCommand bltins[] = { {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg}, {"jobs", "Lists the active jobs", "jobs", shell_jobs}, {"pwd", "Print current directory", "pwd", shell_pwd}, - {"set", "Set environment variable", "set [VAR=value]", shell_set}, + {"export", "Set environment variable", "export [VAR=value]", shell_export}, {"unset", "Unset environment variable", "unset VAR", shell_unset}, {".", "Source-in and run commands in a file", ". filename", @@ -182,7 +182,7 @@ static int shell_exit(struct job *cmd, struct jobSet *junk) static int shell_fg_bg(struct job *cmd, struct jobSet *jobList) { int i, jobNum; - struct job *job; + struct job *job=NULL; if (!jobList->head) { if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) { @@ -268,8 +268,8 @@ static int shell_pwd(struct job *dummy, struct jobSet *junk) return TRUE; } -/* built-in 'set VAR=value' handler */ -static int shell_set(struct job *cmd, struct jobSet *junk) +/* built-in 'export VAR=value' handler */ +static int shell_export(struct job *cmd, struct jobSet *junk) { int res; @@ -278,7 +278,7 @@ static int shell_set(struct job *cmd, struct jobSet *junk) } res = putenv(cmd->progs[0].argv[1]); if (res) - fprintf(stdout, "set: %s\n", strerror(errno)); + fprintf(stdout, "export: %s\n", strerror(errno)); return (res); } @@ -1247,8 +1247,7 @@ extern int device_open(char *device, int mode) #endif /* BB_INIT BB_SYSLOGD */ -#if defined BB_FEATURE_LINUXRC && ( defined BB_HALT || defined BB_REBOOT || defined BB_POWEROFF ) - +#if defined BB_KILLALL || ( defined BB_FEATURE_LINUXRC && ( defined BB_HALT || defined BB_REBOOT || defined BB_POWEROFF )) #ifdef BB_FEATURE_USE_DEVPS_PATCH #include <linux/devps.h> #endif @@ -1363,7 +1362,7 @@ extern pid_t findPidByName( char* pidName) return 0; } #endif /* BB_FEATURE_USE_DEVPS_PATCH */ -#endif /* BB_INIT || BB_HALT || BB_REBOOT || BB_POWEROFF */ +#endif /* BB_KILLALL || ( BB_FEATURE_LINUXRC && ( BB_HALT || BB_REBOOT || BB_POWEROFF )) */ #if defined BB_GUNZIP \ || defined BB_GZIP \ |