diff options
author | Erik Andersen <andersen@codepoet.org> | 2000-03-19 08:28:55 +0300 |
---|---|---|
committer | Erik Andersen <andersen@codepoet.org> | 2000-03-19 08:28:55 +0300 |
commit | c7c634bd88c57d910c6089de7f0091ca4e3d1843 (patch) | |
tree | df2b1c18c27729603880dd797ee6bd51d192d9cd | |
parent | 6c41c448982827ef5bfac9bac14bc9e509f45bc4 (diff) |
Some more stuff.
-Erik
-rw-r--r-- | cmdedit.c | 919 | ||||
-rw-r--r-- | cmdedit.h | 2 | ||||
-rw-r--r-- | internal.h | 6 | ||||
-rw-r--r-- | lash.c | 21 | ||||
-rw-r--r-- | sh.c | 21 | ||||
-rw-r--r-- | shell/cmdedit.c | 919 | ||||
-rw-r--r-- | shell/cmdedit.h | 2 | ||||
-rw-r--r-- | shell/lash.c | 21 |
8 files changed, 984 insertions, 927 deletions
@@ -1,3 +1,4 @@ +/* vi: set sw=4 ts=4: */ /* * Termios command line History and Editting for NetBSD sh (ash) * Copyright (c) 1999 @@ -40,10 +41,8 @@ #include <ctype.h> #include <signal.h> -#include "cmdedit.h" - -#define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */ +#define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */ #define ESC 27 #define DEL 127 @@ -55,142 +54,150 @@ static struct history *his_end = NULL; /* Last element in command line list */ static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */ static int history_counter = 0; /* Number of commands in history list */ -static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ -char *parsenextc; /* copy of parsefile->nextc */ +static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ +char *parsenextc; /* copy of parsefile->nextc */ struct history { - char *s; - struct history *p; - struct history *n; + char *s; + struct history *p; + struct history *n; }; /* Version of write which resumes after a signal is caught. */ int xwrite(int fd, char *buf, int nbytes) { - int ntry; - int i; - int n; - - n = nbytes; - ntry = 0; - for (;;) { - i = write(fd, buf, n); - if (i > 0) { - if ((n -= i) <= 0) - return nbytes; - buf += i; - ntry = 0; - } else if (i == 0) { - if (++ntry > 10) - return nbytes - n; - } else if (errno != EINTR) { - return -1; + int ntry; + int i; + int n; + + n = nbytes; + ntry = 0; + for (;;) { + i = write(fd, buf, n); + if (i > 0) { + if ((n -= i) <= 0) + return nbytes; + buf += i; + ntry = 0; + } else if (i == 0) { + if (++ntry > 10) + return nbytes - n; + } else if (errno != EINTR) { + return -1; + } } - } } /* Version of ioctl that retries after a signal is caught. */ int xioctl(int fd, unsigned long request, char *arg) { - int i; - while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); - return i; + int i; + + while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); + return i; } void cmdedit_reset_term(void) { - if (reset_term) - xioctl(fileno(stdin), TCSETA, (void *) &old_term); + if (reset_term) + xioctl(fileno(stdin), TCSETA, (void *) &old_term); } void prepareToDie(int sig) { - cmdedit_reset_term(); - fprintf(stdout, "\n"); - exit(TRUE); + cmdedit_reset_term(); + fprintf(stdout, "\n"); + exit(TRUE); } void input_home(int outputFd, int *cursor) -{ /* Command line input routines */ - while (*cursor > 0) { - xwrite(outputFd, "\b", 1); - --*cursor; - } +{ /* Command line input routines */ + while (*cursor > 0) { + xwrite(outputFd, "\b", 1); + --*cursor; + } } void input_delete(int outputFd, int cursor) { - int j = 0; - - memmove(parsenextc + cursor, parsenextc + cursor + 1, - BUFSIZ - cursor - 1); - for (j = cursor; j < (BUFSIZ - 1); j++) { - if (!*(parsenextc + j)) - break; - else - xwrite(outputFd, (parsenextc + j), 1); - } + int j = 0; + + memmove(parsenextc + cursor, parsenextc + cursor + 1, + BUFSIZ - cursor - 1); + for (j = cursor; j < (BUFSIZ - 1); j++) { + if (!*(parsenextc + j)) + break; + else + xwrite(outputFd, (parsenextc + j), 1); + } - xwrite(outputFd, " \b", 2); + xwrite(outputFd, " \b", 2); - while (j-- > cursor) - xwrite(outputFd, "\b", 1); + while (j-- > cursor) + xwrite(outputFd, "\b", 1); } void input_end(int outputFd, int *cursor, int len) { - while (*cursor < len) { - xwrite(outputFd, "\033[C", 3); - ++*cursor; - } + while (*cursor < len) { + xwrite(outputFd, "\033[C", 3); + ++*cursor; + } } void input_backspace(int outputFd, int *cursor, int *len) { - int j = 0; - - if (*cursor > 0) { - xwrite(outputFd, "\b \b", 3); - --*cursor; - memmove(parsenextc + *cursor, parsenextc + *cursor + 1, - BUFSIZ - *cursor + 1); - - for (j = *cursor; j < (BUFSIZ - 1); j++) { - if (!*(parsenextc + j)) - break; - else - xwrite(outputFd, (parsenextc + j), 1); - } + int j = 0; + + if (*cursor > 0) { + xwrite(outputFd, "\b \b", 3); + --*cursor; + memmove(parsenextc + *cursor, parsenextc + *cursor + 1, + BUFSIZ - *cursor + 1); + + for (j = *cursor; j < (BUFSIZ - 1); j++) { + if (!*(parsenextc + j)) + break; + else + xwrite(outputFd, (parsenextc + j), 1); + } - xwrite(outputFd, " \b", 2); + xwrite(outputFd, " \b", 2); - while (j-- > *cursor) - xwrite(outputFd, "\b", 1); + while (j-- > *cursor) + xwrite(outputFd, "\b", 1); - --*len; - } + --*len; + } } -char **username_completion_matches( char* matchBuf) +char** username_completion_matches(char* command, int *num_matches) { - fprintf(stderr, "\nin username_completion_matches\n"); - return ( (char**) NULL); + char **matches = (char **) NULL; + *num_matches=0; + fprintf(stderr, "\nin username_completion_matches\n"); + return (matches); } -char **command_completion_matches( char* matchBuf) +char** find_path_executable_n_cwd_matches(char* command, int *num_matches) { - 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); + char **matches = (char **) NULL; + matches = malloc(sizeof(char*)*100); + + matches[0] = malloc(sizeof(char)*50); + matches[1] = malloc(sizeof(char)*50); + + sprintf(matches[0], "Hello"); + sprintf(matches[1], "Howdy"); + *num_matches=2; + +// fprintf(stderr, "\nin find_path_executable_n_cwd_matches\n"); + return (matches); } /* @@ -211,387 +218,401 @@ char **directory_completion_matches( char* matchBuf) * TODO: implement TAB command completion. :) * */ -extern int cmdedit_read_input(int inputFd, int outputFd, - char command[BUFSIZ]) +extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, + char command[BUFSIZ]) { - int nr = 0; - int len = 0; - int j = 0; - 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; - - memset(command, 0, sizeof(command)); - parsenextc = command; - if (!reset_term) { - xioctl(inputFd, TCGETA, (void *) &old_term); - memcpy(&new_term, &old_term, sizeof(struct termio)); - new_term.c_cc[VMIN] = 1; - new_term.c_cc[VTIME] = 0; - new_term.c_lflag &= ~ICANON; /* unbuffered input */ - new_term.c_lflag &= ~ECHO; - xioctl(inputFd, TCSETA, (void *) &new_term); - reset_term = 1; - } else { - xioctl(inputFd, TCSETA, (void *) &new_term); - } - - memset(parsenextc, 0, BUFSIZ); - - while (1) { - - if ((ret = read(inputFd, &c, 1)) < 1) - return ret; - - switch (c) { - case 1: - /* Control-a -- Beginning of line */ - input_home(outputFd, &cursor); - case 5: - /* Control-e -- End of line */ - input_end(outputFd, &cursor, len); - break; - 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 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'; - - fprintf(stderr, "matchBuf='%s'\n", matchBuf); - - /* 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 */ - *(parsenextc + len++ + 1) = c; - xwrite(outputFd, &c, 1); - break_out = 1; - break; - 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': - /* Up Arrow -- Get previous command */ - if (hp && hp->p) { - free( hp->s); - hp->s = strdup(parsenextc); - hp = hp->p; - goto hop; - } - break; - case 'B': - /* 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; - - /* This is where we rewrite the line - * using the selected history item */ - hop: - len = strlen(parsenextc); - - /* return to begining of line */ - for (; cursor > 0; cursor--) - xwrite(outputFd, "\b", 1); - xwrite(outputFd, parsenextc, len); + int nr = 0; + int len = 0; + int j = 0; + int cursor = 0; + int break_out = 0; + int ret = 0; + int lastWasTab = FALSE; + char c = 0; + struct history *hp = his_end; + + memset(command, 0, sizeof(command)); + parsenextc = command; + if (!reset_term) { + xioctl(inputFd, TCGETA, (void *) &old_term); + memcpy(&new_term, &old_term, sizeof(struct termio)); + + new_term.c_cc[VMIN] = 1; + new_term.c_cc[VTIME] = 0; + new_term.c_lflag &= ~ICANON; /* unbuffered input */ + new_term.c_lflag &= ~ECHO; + xioctl(inputFd, TCSETA, (void *) &new_term); + reset_term = 1; + } else { + xioctl(inputFd, TCSETA, (void *) &new_term); + } - /* erase old command */ - for (j = 0; j < len; j++) - xwrite(outputFd, " ", 1); + memset(parsenextc, 0, BUFSIZ); - /* return to begining of line */ - for (j = len; j > 0; j--) - xwrite(outputFd, "\b", 1); + while (1) { - 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 Arrow -- Move forward one character */ - if (cursor < len) { - xwrite(outputFd, "\033[C", 3); - cursor++; - } - break; - case 'D': - /* Left Arrow -- Move back one character */ - if (cursor > 0) { - xwrite(outputFd, "\033[D", 3); - cursor--; - } - break; - case '3': - /* Delete */ - if (cursor != len) { - input_delete(outputFd, cursor); - len--; - } - break; - case '1': - /* Home (Ctrl-A) */ - input_home(outputFd, &cursor); - break; - case '4': - /* End (Ctrl-E) */ - input_end(outputFd, &cursor, len); - break; - } - if (c == '1' || c == '3' || c == '4') - if ((ret = read(inputFd, &c, 1)) < 1) - return ret; /* read 126 (~) */ - } - if (c == 'O') { - /* 79 */ if ((ret = read(inputFd, &c, 1)) < 1) - return ret; + return ret; + switch (c) { - case 'H': - /* Home (xterm) */ - input_home(outputFd, &cursor); - break; - case 'F': - /* End (xterm) */ - input_end(outputFd, &cursor, len); - break; - } - } - c = 0; - break; - } + case 1: + /* Control-a -- Beginning of line */ + input_home(outputFd, &cursor); + case 5: + /* Control-e -- End of line */ + input_end(outputFd, &cursor, len); + break; + 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 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 */ + static int num_matches=0; + static char **matches = (char **) NULL; + int pos = cursor; + + + if (lastWasTab == FALSE) { + char *tmp, *tmp1, *matchBuf; + + /* For now, we will not bother with trying to distinguish + * whether the cursor is in/at a command extression -- we + * will always try all possable matches. If you don't like + * that, feel free to fix it. + */ + + /* Make a local copy of the string -- up + * to the the position of the cursor */ + matchBuf = (char *) calloc(BUFSIZ, sizeof(char)); + strncpy(matchBuf, parsenextc, cursor); + tmp=matchBuf; + + /* skip past any command seperator tokens */ + while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { + tmp=++tmp1; + /* skip any leading white space */ + while (*tmp && isspace(*tmp)) + ++tmp; + } + + /* skip any leading white space */ + while (*tmp && isspace(*tmp)) + ++tmp; + + /* Free up any memory already allocated */ + if (matches) { + free(matches); + matches = (char **) NULL; + } + + /* If the word starts in `~', and there is no slash in the word, + * then try completing this word as a username. */ + if (*tmp == '~' && !strchr(tmp, '/')) + matches = username_completion_matches(tmp, &num_matches); + + /* Try to match any executable in our patch, and everything + * in the current working directory that matches. + */ + if (!matches) + matches = find_path_executable_n_cwd_matches(tmp, &num_matches); + } else { + if ( matches && num_matches>0 ) { + int i, col; + + fprintf(stderr, "\nTabbing...\n"); + + /* Make a list of the matches */ + col += xwrite(outputFd, "\n", 1); + for (i=0,col=0; i<num_matches; i++) { + col += xwrite(outputFd, prompt, strlen(matches[i])); + if (col > 60 && matches[i+1] != NULL) { + xwrite(outputFd, "\n", 1); + col = 0; + } + } + xwrite(outputFd, "\n", 1); + + len+=strlen(prompt); + fprintf(stderr, "len=%d\n", len); + + /* Move to the beginning of the line */ + input_home(outputFd, &len); + + /* erase everything */ + for (j = 0; j < len; j++) + xwrite(outputFd, " ", 1); + + /* return to begining of line */ + input_home(outputFd, &cursor); + + /* Rewrite the prompt) */ + xwrite(outputFd, prompt, strlen(prompt)); + + /* Rewrite the command */ + len = strlen(parsenextc); + xwrite(outputFd, parsenextc, len); + + /* Move back to where the cursor used to be */ + for (cursor=pos; cursor > 0; cursor--) + xwrite(outputFd, "\b", 1); + cursor = pos; + + //fprintf(stderr, "\nprompt='%s'\n", prompt); + } + } + break; + } + case '\b': + case DEL: + /* Backspace */ + input_backspace(outputFd, &cursor, &len); + break; + case '\n': + /* Enter */ + *(parsenextc + len++ + 1) = c; + xwrite(outputFd, &c, 1); + break_out = 1; + break; + 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': + /* Up Arrow -- Get previous command */ + if (hp && hp->p) { + free(hp->s); + hp->s = strdup(parsenextc); + hp = hp->p; + goto hop; + } + break; + case 'B': + /* 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; + + /* This is where we rewrite the line + * using the selected history item */ + hop: + len = strlen(parsenextc); + + /* return to begining of line */ + for (; cursor > 0; cursor--) + xwrite(outputFd, "\b", 1); + xwrite(outputFd, parsenextc, len); + + /* erase old command */ + for (j = 0; j < len; j++) + xwrite(outputFd, " ", 1); + + /* return to begining of line */ + for (j = len; j > 0; j--) + xwrite(outputFd, "\b", 1); + + 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 Arrow -- Move forward one character */ + if (cursor < len) { + xwrite(outputFd, "\033[C", 3); + cursor++; + } + break; + case 'D': + /* Left Arrow -- Move back one character */ + if (cursor > 0) { + xwrite(outputFd, "\033[D", 3); + cursor--; + } + break; + case '3': + /* Delete */ + if (cursor != len) { + input_delete(outputFd, cursor); + len--; + } + break; + case '1': + /* Home (Ctrl-A) */ + input_home(outputFd, &cursor); + break; + case '4': + /* End (Ctrl-E) */ + input_end(outputFd, &cursor, len); + break; + } + if (c == '1' || c == '3' || c == '4') + if ((ret = read(inputFd, &c, 1)) < 1) + return ret; /* read 126 (~) */ + } + if (c == 'O') { + /* 79 */ + if ((ret = read(inputFd, &c, 1)) < 1) + return ret; + switch (c) { + case 'H': + /* Home (xterm) */ + input_home(outputFd, &cursor); + break; + case 'F': + /* End (xterm) */ + input_end(outputFd, &cursor, len); + break; + } + } + c = 0; + break; + } + + default: /* If it's regular input, do the normal thing */ + + if (!isprint(c)) /* Skip non-printable characters */ + break; + + if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ + break; + + len++; + + if (cursor == (len - 1)) { /* Append if at the end of the line */ + *(parsenextc + cursor) = c; + } else { /* Insert otherwise */ + memmove(parsenextc + cursor + 1, parsenextc + cursor, + len - cursor - 1); + + *(parsenextc + cursor) = c; + + for (j = cursor; j < len; j++) + xwrite(outputFd, parsenextc + j, 1); + for (; j > cursor; j--) + xwrite(outputFd, "\033[D", 3); + } - default: /* If it's regular input, do the normal thing */ + cursor++; + xwrite(outputFd, &c, 1); + break; + } + if (c == '\t') + lastWasTab = TRUE; + else + lastWasTab = FALSE; - if (!isprint(c)) /* Skip non-printable characters */ - break; + if (break_out) /* Enter is the command terminator, no more input. */ + break; + } - if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ - break; + nr = len + 1; + xioctl(inputFd, TCSETA, (void *) &old_term); + reset_term = 0; - len++; - if (cursor == (len - 1)) { /* Append if at the end of the line */ - *(parsenextc + cursor) = c; - } else { /* Insert otherwise */ - memmove(parsenextc + cursor + 1, parsenextc + cursor, - len - cursor - 1); + /* Handle command history log */ + if (*(parsenextc)) { - *(parsenextc + cursor) = c; + struct history *h = his_end; - for (j = cursor; j < len; j++) - xwrite(outputFd, parsenextc + j, 1); - for (; j > cursor; j--) - xwrite(outputFd, "\033[D", 3); - } + if (!h) { + /* No previous history */ + h = his_front = malloc(sizeof(struct history)); + h->n = malloc(sizeof(struct history)); - cursor++; - 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; - } - - nr = len + 1; - xioctl(inputFd, TCSETA, (void *) &old_term); - reset_term = 0; - - - /* Handle command history log */ - if (*(parsenextc)) { - - struct history *h = his_end; - - 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 */ - 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; - - /* After max history, remove the oldest command */ - if (history_counter >= MAX_HISTORY) { - - struct history *p = his_front->n; - - p->p = NULL; - free(his_front->s); - free(his_front); - his_front = p; - } else { - history_counter++; - } + 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 */ + 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; + + /* After max history, remove the oldest command */ + if (history_counter >= MAX_HISTORY) { + + struct history *p = his_front->n; + + p->p = NULL; + free(his_front->s); + free(his_front); + his_front = p; + } else { + history_counter++; + } + } } - } - return nr; + return nr; } extern void cmdedit_init(void) { - atexit(cmdedit_reset_term); - signal(SIGINT, prepareToDie); - signal(SIGQUIT, prepareToDie); - signal(SIGTERM, prepareToDie); + atexit(cmdedit_reset_term); + signal(SIGINT, prepareToDie); + signal(SIGQUIT, prepareToDie); + signal(SIGTERM, prepareToDie); } -#endif /* BB_FEATURE_SH_COMMAND_EDITING */ +#endif /* BB_FEATURE_SH_COMMAND_EDITING */ @@ -12,6 +12,6 @@ * */ -extern int cmdedit_read_input(int inputFd, int outputFd, char command[BUFSIZ]); +extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]); extern void cmdedit_init(void); diff --git a/internal.h b/internal.h index c91d3b19d..353ec8ca3 100644 --- a/internal.h +++ b/internal.h @@ -216,6 +216,12 @@ extern int check_wildcard_match(const char* text, const char* pattern); extern long getNum (const char *cp); extern pid_t findPidByName( char* pidName); extern void *xmalloc (size_t size); +#ifdef BB_FEATURE_SH_COMMAND_EDITING +#include <stdio.h> +extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]); +extern void cmdedit_init(void); +#endif + #if defined BB_INIT || defined BB_SYSLOGD extern int device_open(char *device, int mode); #endif @@ -39,10 +39,6 @@ #include <unistd.h> -#ifdef BB_FEATURE_SH_COMMAND_EDITING -#include "cmdedit.h" -#endif - #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" @@ -123,8 +119,7 @@ static struct builtInCommand bltins[] = { {"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", - shell_source}, + {".", "Source-in and run commands in a file", ". filename", shell_source}, {"help", "List shell built-in commands", "help", shell_help}, {NULL, NULL, NULL, NULL} }; @@ -396,11 +391,19 @@ static void checkJobs(struct jobSet *jobList) static int getCommand(FILE * source, char *command) { if (source == stdin) { - fprintf(stdout, "BBSHELL %s %s", cwd, prompt); - fflush(stdout); #ifdef BB_FEATURE_SH_COMMAND_EDITING - cmdedit_read_input(fileno(stdin), fileno(stdout), command); + int len; + char *promptStr; + len=fprintf(stdout, "BBSHELL %s %s", cwd, prompt); + fflush(stdout); + promptStr=(char*)malloc(sizeof(char)*(len+1)); + sprintf(promptStr, "BBSHELL %s %s", cwd, prompt); + cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); + free( promptStr); return 0; +#else + fprintf(stdout, "%s %s", cwd, prompt); + fflush(stdout); #endif } @@ -39,10 +39,6 @@ #include <unistd.h> -#ifdef BB_FEATURE_SH_COMMAND_EDITING -#include "cmdedit.h" -#endif - #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" @@ -123,8 +119,7 @@ static struct builtInCommand bltins[] = { {"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", - shell_source}, + {".", "Source-in and run commands in a file", ". filename", shell_source}, {"help", "List shell built-in commands", "help", shell_help}, {NULL, NULL, NULL, NULL} }; @@ -396,11 +391,19 @@ static void checkJobs(struct jobSet *jobList) static int getCommand(FILE * source, char *command) { if (source == stdin) { - fprintf(stdout, "BBSHELL %s %s", cwd, prompt); - fflush(stdout); #ifdef BB_FEATURE_SH_COMMAND_EDITING - cmdedit_read_input(fileno(stdin), fileno(stdout), command); + int len; + char *promptStr; + len=fprintf(stdout, "BBSHELL %s %s", cwd, prompt); + fflush(stdout); + promptStr=(char*)malloc(sizeof(char)*(len+1)); + sprintf(promptStr, "BBSHELL %s %s", cwd, prompt); + cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); + free( promptStr); return 0; +#else + fprintf(stdout, "%s %s", cwd, prompt); + fflush(stdout); #endif } diff --git a/shell/cmdedit.c b/shell/cmdedit.c index 9e162c6ee..8a7a5fb03 100644 --- a/shell/cmdedit.c +++ b/shell/cmdedit.c @@ -1,3 +1,4 @@ +/* vi: set sw=4 ts=4: */ /* * Termios command line History and Editting for NetBSD sh (ash) * Copyright (c) 1999 @@ -40,10 +41,8 @@ #include <ctype.h> #include <signal.h> -#include "cmdedit.h" - -#define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */ +#define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */ #define ESC 27 #define DEL 127 @@ -55,142 +54,150 @@ static struct history *his_end = NULL; /* Last element in command line list */ static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */ static int history_counter = 0; /* Number of commands in history list */ -static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ -char *parsenextc; /* copy of parsefile->nextc */ +static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ +char *parsenextc; /* copy of parsefile->nextc */ struct history { - char *s; - struct history *p; - struct history *n; + char *s; + struct history *p; + struct history *n; }; /* Version of write which resumes after a signal is caught. */ int xwrite(int fd, char *buf, int nbytes) { - int ntry; - int i; - int n; - - n = nbytes; - ntry = 0; - for (;;) { - i = write(fd, buf, n); - if (i > 0) { - if ((n -= i) <= 0) - return nbytes; - buf += i; - ntry = 0; - } else if (i == 0) { - if (++ntry > 10) - return nbytes - n; - } else if (errno != EINTR) { - return -1; + int ntry; + int i; + int n; + + n = nbytes; + ntry = 0; + for (;;) { + i = write(fd, buf, n); + if (i > 0) { + if ((n -= i) <= 0) + return nbytes; + buf += i; + ntry = 0; + } else if (i == 0) { + if (++ntry > 10) + return nbytes - n; + } else if (errno != EINTR) { + return -1; + } } - } } /* Version of ioctl that retries after a signal is caught. */ int xioctl(int fd, unsigned long request, char *arg) { - int i; - while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); - return i; + int i; + + while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); + return i; } void cmdedit_reset_term(void) { - if (reset_term) - xioctl(fileno(stdin), TCSETA, (void *) &old_term); + if (reset_term) + xioctl(fileno(stdin), TCSETA, (void *) &old_term); } void prepareToDie(int sig) { - cmdedit_reset_term(); - fprintf(stdout, "\n"); - exit(TRUE); + cmdedit_reset_term(); + fprintf(stdout, "\n"); + exit(TRUE); } void input_home(int outputFd, int *cursor) -{ /* Command line input routines */ - while (*cursor > 0) { - xwrite(outputFd, "\b", 1); - --*cursor; - } +{ /* Command line input routines */ + while (*cursor > 0) { + xwrite(outputFd, "\b", 1); + --*cursor; + } } void input_delete(int outputFd, int cursor) { - int j = 0; - - memmove(parsenextc + cursor, parsenextc + cursor + 1, - BUFSIZ - cursor - 1); - for (j = cursor; j < (BUFSIZ - 1); j++) { - if (!*(parsenextc + j)) - break; - else - xwrite(outputFd, (parsenextc + j), 1); - } + int j = 0; + + memmove(parsenextc + cursor, parsenextc + cursor + 1, + BUFSIZ - cursor - 1); + for (j = cursor; j < (BUFSIZ - 1); j++) { + if (!*(parsenextc + j)) + break; + else + xwrite(outputFd, (parsenextc + j), 1); + } - xwrite(outputFd, " \b", 2); + xwrite(outputFd, " \b", 2); - while (j-- > cursor) - xwrite(outputFd, "\b", 1); + while (j-- > cursor) + xwrite(outputFd, "\b", 1); } void input_end(int outputFd, int *cursor, int len) { - while (*cursor < len) { - xwrite(outputFd, "\033[C", 3); - ++*cursor; - } + while (*cursor < len) { + xwrite(outputFd, "\033[C", 3); + ++*cursor; + } } void input_backspace(int outputFd, int *cursor, int *len) { - int j = 0; - - if (*cursor > 0) { - xwrite(outputFd, "\b \b", 3); - --*cursor; - memmove(parsenextc + *cursor, parsenextc + *cursor + 1, - BUFSIZ - *cursor + 1); - - for (j = *cursor; j < (BUFSIZ - 1); j++) { - if (!*(parsenextc + j)) - break; - else - xwrite(outputFd, (parsenextc + j), 1); - } + int j = 0; + + if (*cursor > 0) { + xwrite(outputFd, "\b \b", 3); + --*cursor; + memmove(parsenextc + *cursor, parsenextc + *cursor + 1, + BUFSIZ - *cursor + 1); + + for (j = *cursor; j < (BUFSIZ - 1); j++) { + if (!*(parsenextc + j)) + break; + else + xwrite(outputFd, (parsenextc + j), 1); + } - xwrite(outputFd, " \b", 2); + xwrite(outputFd, " \b", 2); - while (j-- > *cursor) - xwrite(outputFd, "\b", 1); + while (j-- > *cursor) + xwrite(outputFd, "\b", 1); - --*len; - } + --*len; + } } -char **username_completion_matches( char* matchBuf) +char** username_completion_matches(char* command, int *num_matches) { - fprintf(stderr, "\nin username_completion_matches\n"); - return ( (char**) NULL); + char **matches = (char **) NULL; + *num_matches=0; + fprintf(stderr, "\nin username_completion_matches\n"); + return (matches); } -char **command_completion_matches( char* matchBuf) +char** find_path_executable_n_cwd_matches(char* command, int *num_matches) { - 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); + char **matches = (char **) NULL; + matches = malloc(sizeof(char*)*100); + + matches[0] = malloc(sizeof(char)*50); + matches[1] = malloc(sizeof(char)*50); + + sprintf(matches[0], "Hello"); + sprintf(matches[1], "Howdy"); + *num_matches=2; + +// fprintf(stderr, "\nin find_path_executable_n_cwd_matches\n"); + return (matches); } /* @@ -211,387 +218,401 @@ char **directory_completion_matches( char* matchBuf) * TODO: implement TAB command completion. :) * */ -extern int cmdedit_read_input(int inputFd, int outputFd, - char command[BUFSIZ]) +extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, + char command[BUFSIZ]) { - int nr = 0; - int len = 0; - int j = 0; - 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; - - memset(command, 0, sizeof(command)); - parsenextc = command; - if (!reset_term) { - xioctl(inputFd, TCGETA, (void *) &old_term); - memcpy(&new_term, &old_term, sizeof(struct termio)); - new_term.c_cc[VMIN] = 1; - new_term.c_cc[VTIME] = 0; - new_term.c_lflag &= ~ICANON; /* unbuffered input */ - new_term.c_lflag &= ~ECHO; - xioctl(inputFd, TCSETA, (void *) &new_term); - reset_term = 1; - } else { - xioctl(inputFd, TCSETA, (void *) &new_term); - } - - memset(parsenextc, 0, BUFSIZ); - - while (1) { - - if ((ret = read(inputFd, &c, 1)) < 1) - return ret; - - switch (c) { - case 1: - /* Control-a -- Beginning of line */ - input_home(outputFd, &cursor); - case 5: - /* Control-e -- End of line */ - input_end(outputFd, &cursor, len); - break; - 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 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'; - - fprintf(stderr, "matchBuf='%s'\n", matchBuf); - - /* 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 */ - *(parsenextc + len++ + 1) = c; - xwrite(outputFd, &c, 1); - break_out = 1; - break; - 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': - /* Up Arrow -- Get previous command */ - if (hp && hp->p) { - free( hp->s); - hp->s = strdup(parsenextc); - hp = hp->p; - goto hop; - } - break; - case 'B': - /* 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; - - /* This is where we rewrite the line - * using the selected history item */ - hop: - len = strlen(parsenextc); - - /* return to begining of line */ - for (; cursor > 0; cursor--) - xwrite(outputFd, "\b", 1); - xwrite(outputFd, parsenextc, len); + int nr = 0; + int len = 0; + int j = 0; + int cursor = 0; + int break_out = 0; + int ret = 0; + int lastWasTab = FALSE; + char c = 0; + struct history *hp = his_end; + + memset(command, 0, sizeof(command)); + parsenextc = command; + if (!reset_term) { + xioctl(inputFd, TCGETA, (void *) &old_term); + memcpy(&new_term, &old_term, sizeof(struct termio)); + + new_term.c_cc[VMIN] = 1; + new_term.c_cc[VTIME] = 0; + new_term.c_lflag &= ~ICANON; /* unbuffered input */ + new_term.c_lflag &= ~ECHO; + xioctl(inputFd, TCSETA, (void *) &new_term); + reset_term = 1; + } else { + xioctl(inputFd, TCSETA, (void *) &new_term); + } - /* erase old command */ - for (j = 0; j < len; j++) - xwrite(outputFd, " ", 1); + memset(parsenextc, 0, BUFSIZ); - /* return to begining of line */ - for (j = len; j > 0; j--) - xwrite(outputFd, "\b", 1); + while (1) { - 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 Arrow -- Move forward one character */ - if (cursor < len) { - xwrite(outputFd, "\033[C", 3); - cursor++; - } - break; - case 'D': - /* Left Arrow -- Move back one character */ - if (cursor > 0) { - xwrite(outputFd, "\033[D", 3); - cursor--; - } - break; - case '3': - /* Delete */ - if (cursor != len) { - input_delete(outputFd, cursor); - len--; - } - break; - case '1': - /* Home (Ctrl-A) */ - input_home(outputFd, &cursor); - break; - case '4': - /* End (Ctrl-E) */ - input_end(outputFd, &cursor, len); - break; - } - if (c == '1' || c == '3' || c == '4') - if ((ret = read(inputFd, &c, 1)) < 1) - return ret; /* read 126 (~) */ - } - if (c == 'O') { - /* 79 */ if ((ret = read(inputFd, &c, 1)) < 1) - return ret; + return ret; + switch (c) { - case 'H': - /* Home (xterm) */ - input_home(outputFd, &cursor); - break; - case 'F': - /* End (xterm) */ - input_end(outputFd, &cursor, len); - break; - } - } - c = 0; - break; - } + case 1: + /* Control-a -- Beginning of line */ + input_home(outputFd, &cursor); + case 5: + /* Control-e -- End of line */ + input_end(outputFd, &cursor, len); + break; + 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 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 */ + static int num_matches=0; + static char **matches = (char **) NULL; + int pos = cursor; + + + if (lastWasTab == FALSE) { + char *tmp, *tmp1, *matchBuf; + + /* For now, we will not bother with trying to distinguish + * whether the cursor is in/at a command extression -- we + * will always try all possable matches. If you don't like + * that, feel free to fix it. + */ + + /* Make a local copy of the string -- up + * to the the position of the cursor */ + matchBuf = (char *) calloc(BUFSIZ, sizeof(char)); + strncpy(matchBuf, parsenextc, cursor); + tmp=matchBuf; + + /* skip past any command seperator tokens */ + while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { + tmp=++tmp1; + /* skip any leading white space */ + while (*tmp && isspace(*tmp)) + ++tmp; + } + + /* skip any leading white space */ + while (*tmp && isspace(*tmp)) + ++tmp; + + /* Free up any memory already allocated */ + if (matches) { + free(matches); + matches = (char **) NULL; + } + + /* If the word starts in `~', and there is no slash in the word, + * then try completing this word as a username. */ + if (*tmp == '~' && !strchr(tmp, '/')) + matches = username_completion_matches(tmp, &num_matches); + + /* Try to match any executable in our patch, and everything + * in the current working directory that matches. + */ + if (!matches) + matches = find_path_executable_n_cwd_matches(tmp, &num_matches); + } else { + if ( matches && num_matches>0 ) { + int i, col; + + fprintf(stderr, "\nTabbing...\n"); + + /* Make a list of the matches */ + col += xwrite(outputFd, "\n", 1); + for (i=0,col=0; i<num_matches; i++) { + col += xwrite(outputFd, prompt, strlen(matches[i])); + if (col > 60 && matches[i+1] != NULL) { + xwrite(outputFd, "\n", 1); + col = 0; + } + } + xwrite(outputFd, "\n", 1); + + len+=strlen(prompt); + fprintf(stderr, "len=%d\n", len); + + /* Move to the beginning of the line */ + input_home(outputFd, &len); + + /* erase everything */ + for (j = 0; j < len; j++) + xwrite(outputFd, " ", 1); + + /* return to begining of line */ + input_home(outputFd, &cursor); + + /* Rewrite the prompt) */ + xwrite(outputFd, prompt, strlen(prompt)); + + /* Rewrite the command */ + len = strlen(parsenextc); + xwrite(outputFd, parsenextc, len); + + /* Move back to where the cursor used to be */ + for (cursor=pos; cursor > 0; cursor--) + xwrite(outputFd, "\b", 1); + cursor = pos; + + //fprintf(stderr, "\nprompt='%s'\n", prompt); + } + } + break; + } + case '\b': + case DEL: + /* Backspace */ + input_backspace(outputFd, &cursor, &len); + break; + case '\n': + /* Enter */ + *(parsenextc + len++ + 1) = c; + xwrite(outputFd, &c, 1); + break_out = 1; + break; + 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': + /* Up Arrow -- Get previous command */ + if (hp && hp->p) { + free(hp->s); + hp->s = strdup(parsenextc); + hp = hp->p; + goto hop; + } + break; + case 'B': + /* 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; + + /* This is where we rewrite the line + * using the selected history item */ + hop: + len = strlen(parsenextc); + + /* return to begining of line */ + for (; cursor > 0; cursor--) + xwrite(outputFd, "\b", 1); + xwrite(outputFd, parsenextc, len); + + /* erase old command */ + for (j = 0; j < len; j++) + xwrite(outputFd, " ", 1); + + /* return to begining of line */ + for (j = len; j > 0; j--) + xwrite(outputFd, "\b", 1); + + 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 Arrow -- Move forward one character */ + if (cursor < len) { + xwrite(outputFd, "\033[C", 3); + cursor++; + } + break; + case 'D': + /* Left Arrow -- Move back one character */ + if (cursor > 0) { + xwrite(outputFd, "\033[D", 3); + cursor--; + } + break; + case '3': + /* Delete */ + if (cursor != len) { + input_delete(outputFd, cursor); + len--; + } + break; + case '1': + /* Home (Ctrl-A) */ + input_home(outputFd, &cursor); + break; + case '4': + /* End (Ctrl-E) */ + input_end(outputFd, &cursor, len); + break; + } + if (c == '1' || c == '3' || c == '4') + if ((ret = read(inputFd, &c, 1)) < 1) + return ret; /* read 126 (~) */ + } + if (c == 'O') { + /* 79 */ + if ((ret = read(inputFd, &c, 1)) < 1) + return ret; + switch (c) { + case 'H': + /* Home (xterm) */ + input_home(outputFd, &cursor); + break; + case 'F': + /* End (xterm) */ + input_end(outputFd, &cursor, len); + break; + } + } + c = 0; + break; + } + + default: /* If it's regular input, do the normal thing */ + + if (!isprint(c)) /* Skip non-printable characters */ + break; + + if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ + break; + + len++; + + if (cursor == (len - 1)) { /* Append if at the end of the line */ + *(parsenextc + cursor) = c; + } else { /* Insert otherwise */ + memmove(parsenextc + cursor + 1, parsenextc + cursor, + len - cursor - 1); + + *(parsenextc + cursor) = c; + + for (j = cursor; j < len; j++) + xwrite(outputFd, parsenextc + j, 1); + for (; j > cursor; j--) + xwrite(outputFd, "\033[D", 3); + } - default: /* If it's regular input, do the normal thing */ + cursor++; + xwrite(outputFd, &c, 1); + break; + } + if (c == '\t') + lastWasTab = TRUE; + else + lastWasTab = FALSE; - if (!isprint(c)) /* Skip non-printable characters */ - break; + if (break_out) /* Enter is the command terminator, no more input. */ + break; + } - if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ - break; + nr = len + 1; + xioctl(inputFd, TCSETA, (void *) &old_term); + reset_term = 0; - len++; - if (cursor == (len - 1)) { /* Append if at the end of the line */ - *(parsenextc + cursor) = c; - } else { /* Insert otherwise */ - memmove(parsenextc + cursor + 1, parsenextc + cursor, - len - cursor - 1); + /* Handle command history log */ + if (*(parsenextc)) { - *(parsenextc + cursor) = c; + struct history *h = his_end; - for (j = cursor; j < len; j++) - xwrite(outputFd, parsenextc + j, 1); - for (; j > cursor; j--) - xwrite(outputFd, "\033[D", 3); - } + if (!h) { + /* No previous history */ + h = his_front = malloc(sizeof(struct history)); + h->n = malloc(sizeof(struct history)); - cursor++; - 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; - } - - nr = len + 1; - xioctl(inputFd, TCSETA, (void *) &old_term); - reset_term = 0; - - - /* Handle command history log */ - if (*(parsenextc)) { - - struct history *h = his_end; - - 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 */ - 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; - - /* After max history, remove the oldest command */ - if (history_counter >= MAX_HISTORY) { - - struct history *p = his_front->n; - - p->p = NULL; - free(his_front->s); - free(his_front); - his_front = p; - } else { - history_counter++; - } + 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 */ + 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; + + /* After max history, remove the oldest command */ + if (history_counter >= MAX_HISTORY) { + + struct history *p = his_front->n; + + p->p = NULL; + free(his_front->s); + free(his_front); + his_front = p; + } else { + history_counter++; + } + } } - } - return nr; + return nr; } extern void cmdedit_init(void) { - atexit(cmdedit_reset_term); - signal(SIGINT, prepareToDie); - signal(SIGQUIT, prepareToDie); - signal(SIGTERM, prepareToDie); + atexit(cmdedit_reset_term); + signal(SIGINT, prepareToDie); + signal(SIGQUIT, prepareToDie); + signal(SIGTERM, prepareToDie); } -#endif /* BB_FEATURE_SH_COMMAND_EDITING */ +#endif /* BB_FEATURE_SH_COMMAND_EDITING */ diff --git a/shell/cmdedit.h b/shell/cmdedit.h index e776543d6..843a74070 100644 --- a/shell/cmdedit.h +++ b/shell/cmdedit.h @@ -12,6 +12,6 @@ * */ -extern int cmdedit_read_input(int inputFd, int outputFd, char command[BUFSIZ]); +extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]); extern void cmdedit_init(void); diff --git a/shell/lash.c b/shell/lash.c index 068159697..8c1503ff4 100644 --- a/shell/lash.c +++ b/shell/lash.c @@ -39,10 +39,6 @@ #include <unistd.h> -#ifdef BB_FEATURE_SH_COMMAND_EDITING -#include "cmdedit.h" -#endif - #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" @@ -123,8 +119,7 @@ static struct builtInCommand bltins[] = { {"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", - shell_source}, + {".", "Source-in and run commands in a file", ". filename", shell_source}, {"help", "List shell built-in commands", "help", shell_help}, {NULL, NULL, NULL, NULL} }; @@ -396,11 +391,19 @@ static void checkJobs(struct jobSet *jobList) static int getCommand(FILE * source, char *command) { if (source == stdin) { - fprintf(stdout, "BBSHELL %s %s", cwd, prompt); - fflush(stdout); #ifdef BB_FEATURE_SH_COMMAND_EDITING - cmdedit_read_input(fileno(stdin), fileno(stdout), command); + int len; + char *promptStr; + len=fprintf(stdout, "BBSHELL %s %s", cwd, prompt); + fflush(stdout); + promptStr=(char*)malloc(sizeof(char)*(len+1)); + sprintf(promptStr, "BBSHELL %s %s", cwd, prompt); + cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); + free( promptStr); return 0; +#else + fprintf(stdout, "%s %s", cwd, prompt); + fflush(stdout); #endif } |