X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=shell%2Fcmdedit.c;h=8b5b2b94254929681c9f65ccd182eacacc70829d;hb=53433b3ca1fb5593167b6fd66f879d45f8f31ecc;hp=3810c230a5bcf555b8cae2845f696fa9486d0ecd;hpb=889a301a0c992cef843322ba7d2af62fcf44ab2a;p=oweals%2Fbusybox.git diff --git a/shell/cmdedit.c b/shell/cmdedit.c index 3810c230a..8b5b2b942 100644 --- a/shell/cmdedit.c +++ b/shell/cmdedit.c @@ -1,15 +1,15 @@ /* vi: set sw=4 ts=4: */ /* - * Termios command line History and Editting. + * Termios command line History and Editing. * - * Copyright (c) 1986-2001 may safely be consumed by a BSD or GPL license. + * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license. * Written by: Vladimir Oleynik * * Used ideas: * Adam Rogoyski * Dave Cinege * Jakub Jelinek (c) 1995 - * Erik Andersen (Majorly adjusted for busybox) + * Erik Andersen (Majorly adjusted for busybox) * * This code is 'as is' with no warranty. * @@ -31,6 +31,7 @@ */ +#include "busybox.h" #include #include #include @@ -41,7 +42,8 @@ #include #include -#include "busybox.h" +#include "../shell/cmdedit.h" + #ifdef CONFIG_LOCALE_SUPPORT #define Isprint(c) isprint((c)) @@ -49,11 +51,14 @@ #define Isprint(c) ( (c) >= ' ' && (c) != ((unsigned char)'\233') ) #endif -#ifndef TEST +#ifdef TEST -#define D(x) - -#else +/* pretect redefined for test */ +#undef CONFIG_FEATURE_COMMAND_EDITING +#undef CONFIG_FEATURE_COMMAND_TAB_COMPLETION +#undef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION +#undef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT +#undef CONFIG_FEATURE_CLEAN_UP #define CONFIG_FEATURE_COMMAND_EDITING #define CONFIG_FEATURE_COMMAND_TAB_COMPLETION @@ -61,9 +66,7 @@ #define CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT #define CONFIG_FEATURE_CLEAN_UP -#define D(x) x - -#endif /* TEST */ +#endif /* TEST */ #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION #include @@ -72,39 +75,31 @@ #ifdef CONFIG_FEATURE_COMMAND_EDITING -#ifndef CONFIG_FEATURE_COMMAND_TAB_COMPLETION -#undef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION -#endif - #if defined(CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION) || defined(CONFIG_FEATURE_SH_FANCY_PROMPT) #define CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR #endif #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR -# ifndef TEST -# include "pwd.h" -# else -# include -# endif /* TEST */ -#endif /* advanced FEATURES */ +#include "pwd_.h" +#endif /* advanced FEATURES */ - -struct history { - char *s; - struct history *p; - struct history *n; -}; - /* Maximum length of the linked list for the command line history */ -static const int MAX_HISTORY = 15; - -/* First element in command line list */ -static struct history *his_front = NULL; - -/* Last element in command line list */ -static struct history *his_end = NULL; +#ifndef CONFIG_FEATURE_COMMAND_HISTORY +#define MAX_HISTORY 15 +#else +#define MAX_HISTORY CONFIG_FEATURE_COMMAND_HISTORY +#endif +#if MAX_HISTORY < 1 +#warning cmdedit: You set MAX_HISTORY < 1. The history algorithm switched off. +#else +static char *history[MAX_HISTORY+1]; /* history + current */ +/* saved history lines */ +static int n_history; +/* current pointer to history line */ +static int cur_history; +#endif #include #define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp) @@ -115,31 +110,30 @@ static struct termios initial_settings, new_settings; static -volatile int cmdedit_termw = 80; /* actual terminal width */ -static int history_counter = 0; /* Number of commands in history list */ +volatile int cmdedit_termw = 80; /* actual terminal width */ static -volatile int handlers_sets = 0; /* Set next bites: */ +volatile int handlers_sets = 0; /* Set next bites: */ enum { - SET_ATEXIT = 1, /* when atexit() has been called + SET_ATEXIT = 1, /* when atexit() has been called and get euid,uid,gid to fast compare */ SET_WCHG_HANDLERS = 2, /* winchg signal handler */ SET_RESET_TERM = 4, /* if the terminal needs to be reset upon exit */ }; -static int cmdedit_x; /* real x terminal position */ -static int cmdedit_y; /* pseudoreal y terminal position */ -static int cmdedit_prmt_len; /* lenght prompt without colores string */ +static int cmdedit_x; /* real x terminal position */ +static int cmdedit_y; /* pseudoreal y terminal position */ +static int cmdedit_prmt_len; /* lenght prompt without colores string */ -static int cursor; /* required global for signal handler */ -static int len; /* --- "" - - "" - -"- --""-- --""--- */ -static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */ +static int cursor; /* required global for signal handler */ +static int len; /* --- "" - - "" - -"- --""-- --""--- */ +static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */ static #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT const #endif -char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */ +char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */ #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR static char *user_buf = ""; @@ -148,7 +142,7 @@ static int my_euid; #endif #ifdef CONFIG_FEATURE_SH_FANCY_PROMPT -static char *hostname_buf = ""; +static char *hostname_buf; static int num_ok_lines = 1; #endif @@ -162,43 +156,36 @@ static int my_euid; static int my_uid; static int my_gid; -#endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */ - -/* It seems that libc5 doesn't know what a sighandler_t is... */ -#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1) -typedef void (*sighandler_t) (int); -#endif +#endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */ static void cmdedit_setwidth(int w, int redraw_flg); static void win_changed(int nsig) { - struct winsize win = { 0, 0, 0, 0 }; - static sighandler_t previous_SIGWINCH_handler; /* for reset */ + static sighandler_t previous_SIGWINCH_handler; /* for reset */ /* emulate || signal call */ if (nsig == -SIGWINCH || nsig == SIGWINCH) { - ioctl(0, TIOCGWINSZ, &win); - if (win.ws_col > 0) { - cmdedit_setwidth(win.ws_col, nsig == SIGWINCH); - } + int width = 0; + get_terminal_width_height(0, &width, NULL); + cmdedit_setwidth(width, nsig == SIGWINCH); } /* Unix not all standart in recall signal */ - if (nsig == -SIGWINCH) /* save previous handler */ + if (nsig == -SIGWINCH) /* save previous handler */ previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); - else if (nsig == SIGWINCH) /* signaled called handler */ - signal(SIGWINCH, win_changed); /* set for next call */ - else /* nsig == 0 */ + else if (nsig == SIGWINCH) /* signaled called handler */ + signal(SIGWINCH, win_changed); /* set for next call */ + else /* nsig == 0 */ /* set previous handler */ - signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */ + signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */ } static void cmdedit_reset_term(void) { if ((handlers_sets & SET_RESET_TERM) != 0) { /* sparc and other have broken termios support: use old termio handling. */ - setTermSettings(fileno(stdin), (void *) &initial_settings); + setTermSettings(STDIN_FILENO, (void *) &initial_settings); handlers_sets &= ~SET_RESET_TERM; } if ((handlers_sets & SET_WCHG_HANDLERS) != 0) { @@ -207,18 +194,6 @@ static void cmdedit_reset_term(void) handlers_sets &= ~SET_WCHG_HANDLERS; } fflush(stdout); -#ifdef CONFIG_FEATURE_CLEAN_UP - if (his_front) { - struct history *n; - - while (his_front != his_end) { - n = his_front->n; - free(his_front->s); - free(his_front); - his_front = n; - } - } -#endif } @@ -229,9 +204,9 @@ static void cmdedit_set_out_char(int next_char) int c = (int)((unsigned char) command_ps[cursor]); if (c == 0) - c = ' '; /* destroy end char? */ + c = ' '; /* destroy end char? */ #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT - if (!Isprint(c)) { /* Inverse put non-printable characters */ + if (!Isprint(c)) { /* Inverse put non-printable characters */ if (c >= 128) c -= 128; if (c < ' ') @@ -274,22 +249,24 @@ static void goto_new_line(void) static inline void out1str(const char *s) { - fputs(s, stdout); + if ( s ) + fputs(s, stdout); } + static inline void beep(void) { putchar('\007'); } -/* Move back one charactor */ +/* Move back one character */ /* special for slow terminal */ static void input_backward(int num) { if (num > cursor) num = cursor; - cursor -= num; /* new cursor (in command, not terminal) */ + cursor -= num; /* new cursor (in command, not terminal) */ - if (cmdedit_x >= num) { /* no to up line */ + if (cmdedit_x >= num) { /* no to up line */ cmdedit_x -= num; if (num < 4) while (num-- > 0) @@ -301,22 +278,22 @@ static void input_backward(int num) int count_y; if (cmdedit_x) { - putchar('\r'); /* back to first terminal pos. */ - num -= cmdedit_x; /* set previous backward */ + putchar('\r'); /* back to first terminal pos. */ + num -= cmdedit_x; /* set previous backward */ } count_y = 1 + num / cmdedit_termw; printf("\033[%dA", count_y); cmdedit_y -= count_y; /* require forward after uping */ cmdedit_x = cmdedit_termw * count_y - num; - printf("\033[%dC", cmdedit_x); /* set term cursor */ + printf("\033[%dC", cmdedit_x); /* set term cursor */ } } static void put_prompt(void) { out1str(cmdedit_prompt); - cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */ + cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */ cursor = 0; cmdedit_y = 0; /* new quasireal y */ } @@ -332,7 +309,7 @@ static void parse_prompt(const char *prmt_ptr) static void parse_prompt(const char *prmt_ptr) { int prmt_len = 0; - int sub_len = 0; + size_t cur_prmt_len = 0; char flg_not_length = '['; char *prmt_mem_ptr = xcalloc(1, 1); char *pwd_buf = xgetcwd(0); @@ -342,7 +319,7 @@ static void parse_prompt(const char *prmt_ptr) char *pbuf; if (!pwd_buf) { - pwd_buf=(char *)unknown; + pwd_buf=(char *)bb_msg_unknown; } while (*prmt_ptr) { @@ -352,8 +329,8 @@ static void parse_prompt(const char *prmt_ptr) if (c == '\\') { const char *cp = prmt_ptr; int l; - - c = process_escape_sequence(&prmt_ptr); + + c = bb_process_escape_sequence(&prmt_ptr); if(prmt_ptr==cp) { if (*cp == 0) break; @@ -363,10 +340,10 @@ static void parse_prompt(const char *prmt_ptr) case 'u': pbuf = user_buf; break; -#endif +#endif case 'h': pbuf = hostname_buf; - if (*pbuf == 0) { + if (pbuf == 0) { pbuf = xcalloc(256, 1); if (gethostname(pbuf, 255) < 0) { strcpy(pbuf, "?"); @@ -395,7 +372,7 @@ static void parse_prompt(const char *prmt_ptr) strcpy(pbuf+1, pwd_buf+l); } break; -#endif +#endif case 'W': pbuf = pwd_buf; cp = strrchr(pbuf,'/'); @@ -408,7 +385,7 @@ static void parse_prompt(const char *prmt_ptr) case 'e': case 'E': /* \e \E = \033 */ c = '\033'; break; - case 'x': case 'X': + case 'x': case 'X': for (l = 0; l < 3;) { int h; buf2[l++] = *prmt_ptr; @@ -433,69 +410,106 @@ static void parse_prompt(const char *prmt_ptr) } break; } - } + } } if(pbuf == buf) *pbuf = c; - prmt_len += strlen(pbuf); + cur_prmt_len = strlen(pbuf); + prmt_len += cur_prmt_len; + if (flg_not_length != ']') + cmdedit_prmt_len += cur_prmt_len; prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf); - if (flg_not_length == ']') - sub_len++; } - if(pwd_buf!=(char *)unknown) + if(pwd_buf!=(char *)bb_msg_unknown) free(pwd_buf); cmdedit_prompt = prmt_mem_ptr; - cmdedit_prmt_len = prmt_len - sub_len; put_prompt(); } #endif -/* draw promt, editor line, and clear tail */ +/* draw prompt, editor line, and clear tail */ static void redraw(int y, int back_cursor) { - if (y > 0) /* up to start y */ + if (y > 0) /* up to start y */ printf("\033[%dA", y); putchar('\r'); put_prompt(); - input_end(); /* rewrite */ - printf("\033[J"); /* destroy tail after cursor */ + input_end(); /* rewrite */ + printf("\033[J"); /* destroy tail after cursor */ input_backward(back_cursor); } -/* Delete the char in front of the cursor */ -static void input_delete(void) +#ifdef CONFIG_FEATURE_COMMAND_EDITING_VI +#define DELBUFSIZ 128 +static char *delbuf; /* a (malloced) place to store deleted characters */ +static char *delp; +static char newdelflag; /* whether delbuf should be reused yet */ +#endif + +/* Delete the char in front of the cursor, optionally saving it + * for later putback */ +static void input_delete(int save) { int j = cursor; if (j == len) return; +#ifdef CONFIG_FEATURE_COMMAND_EDITING_VI + if (save) { + if (newdelflag) { + if (!delbuf) + delbuf = malloc(DELBUFSIZ); + /* safe if malloc fails */ + delp = delbuf; + newdelflag = 0; + } + if (delbuf && (delp - delbuf < DELBUFSIZ)) + *delp++ = command_ps[j]; + } +#endif + strcpy(command_ps + j, command_ps + j + 1); len--; - input_end(); /* rewtite new line */ - cmdedit_set_out_char(0); /* destroy end char */ - input_backward(cursor - j); /* back to old pos cursor */ + input_end(); /* rewrite new line */ + cmdedit_set_out_char(0); /* destroy end char */ + input_backward(cursor - j); /* back to old pos cursor */ +} + +#ifdef CONFIG_FEATURE_COMMAND_EDITING_VI +static void put(void) +{ + int ocursor, j = delp - delbuf; + if (j == 0) + return; + ocursor = cursor; + /* open hole and then fill it */ + memmove(command_ps + cursor + j, command_ps + cursor, len - cursor + 1); + strncpy(command_ps + cursor, delbuf, j); + len += j; + input_end(); /* rewrite new line */ + input_backward(cursor-ocursor-j+1); /* at end of new text */ } +#endif /* Delete the char in back of the cursor */ static void input_backspace(void) { if (cursor > 0) { input_backward(1); - input_delete(); + input_delete(0); } } -/* Move forward one charactor */ +/* Move forward one character */ static void input_forward(void) { if (cursor < len) cmdedit_set_out_char(command_ps[cursor + 1]); } - static void cmdedit_setwidth(int w, int redraw_flg) { cmdedit_termw = cmdedit_prmt_len + 2; @@ -513,7 +527,7 @@ static void cmdedit_setwidth(int w, int redraw_flg) redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor); fflush(stdout); } - } + } } static void cmdedit_init(void) @@ -532,8 +546,8 @@ static void cmdedit_init(void) my_euid = geteuid(); entry = getpwuid(my_euid); if (entry) { - user_buf = xstrdup(entry->pw_name); - home_pwd_buf = xstrdup(entry->pw_dir); + user_buf = bb_xstrdup(entry->pw_name); + home_pwd_buf = bb_xstrdup(entry->pw_dir); } #endif @@ -544,14 +558,30 @@ static void cmdedit_init(void) #endif my_uid = getuid(); my_gid = getgid(); -#endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */ +#endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */ handlers_sets |= SET_ATEXIT; - atexit(cmdedit_reset_term); /* be sure to do this only once */ + atexit(cmdedit_reset_term); /* be sure to do this only once */ } } #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION +static char **matches; +static int num_matches; +static char *add_char_to_match; + +static void add_match(char *matched, int add_char) +{ + int nm = num_matches; + int nm1 = nm + 1; + + matches = xrealloc(matches, nm1 * sizeof(char *)); + add_char_to_match = xrealloc(add_char_to_match, nm1); + matches[nm] = matched; + add_char_to_match[nm] = (char)add_char; + num_matches++; +} + static int is_execute(const struct stat *st) { if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) || @@ -563,67 +593,55 @@ static int is_execute(const struct stat *st) #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION -static char **username_tab_completion(char *ud, int *num_matches) +static void username_tab_completion(char *ud, char *with_shash_flg) { struct passwd *entry; int userlen; - char *temp; - - ud++; /* ~user/... to user/... */ + ud++; /* ~user/... to user/... */ userlen = strlen(ud); - if (num_matches == 0) { /* "~/..." or "~user/..." */ + if (with_shash_flg) { /* "~/..." or "~user/..." */ char *sav_ud = ud - 1; char *home = 0; + char *temp; - if (*ud == '/') { /* "~/..." */ + if (*ud == '/') { /* "~/..." */ home = home_pwd_buf; } else { /* "~user/..." */ temp = strchr(ud, '/'); - *temp = 0; /* ~user\0 */ + *temp = 0; /* ~user\0 */ entry = getpwnam(ud); - *temp = '/'; /* restore ~user/... */ + *temp = '/'; /* restore ~user/... */ ud = temp; if (entry) home = entry->pw_dir; } if (home) { if ((userlen + strlen(home) + 1) < BUFSIZ) { - char temp2[BUFSIZ]; /* argument size */ + char temp2[BUFSIZ]; /* argument size */ /* /home/user/... */ sprintf(temp2, "%s%s", home, ud); strcpy(sav_ud, temp2); } } - return 0; /* void, result save to argument :-) */ } else { /* "~[^/]*" */ - char **matches = (char **) NULL; - int nm = 0; - setpwent(); while ((entry = getpwent()) != NULL) { /* Null usernames should result in all users as possible completions. */ if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) { - - temp = xmalloc(3 + strlen(entry->pw_name)); - sprintf(temp, "~%s/", entry->pw_name); - matches = xrealloc(matches, (nm + 1) * sizeof(char *)); - - matches[nm++] = temp; + add_match(bb_xasprintf("~%s", entry->pw_name), '/'); } } endpwent(); - (*num_matches) = nm; - return (matches); } } -#endif /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */ +#endif /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */ enum { FIND_EXE_ONLY = 0, @@ -631,14 +649,20 @@ enum { FIND_FILE_ONLY = 2, }; +#ifdef CONFIG_ASH +const char *cmdedit_path_lookup; +#else +#define cmdedit_path_lookup getenv("PATH") +#endif + static int path_parse(char ***p, int flags) { int npth; - char *tmp; - char *pth; + const char *tmp; + const char *pth; /* if not setenv PATH variable, to search cur dir "." */ - if (flags != FIND_EXE_ONLY || (pth = getenv("PATH")) == 0 || + if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 || /* PATH= or PATH=: */ *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) { return 1; @@ -648,11 +672,11 @@ static int path_parse(char ***p, int flags) npth = 0; for (;;) { - npth++; /* count words is + 1 count ':' */ + npth++; /* count words is + 1 count ':' */ tmp = strchr(tmp, ':'); if (tmp) { if (*++tmp == 0) - break; /* : */ + break; /* : */ } else break; } @@ -660,24 +684,24 @@ static int path_parse(char ***p, int flags) *p = xmalloc(npth * sizeof(char *)); tmp = pth; - (*p)[0] = xstrdup(tmp); - npth = 1; /* count words is + 1 count ':' */ + (*p)[0] = bb_xstrdup(tmp); + npth = 1; /* count words is + 1 count ':' */ for (;;) { tmp = strchr(tmp, ':'); if (tmp) { - (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */ + (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */ if (*++tmp == 0) - break; /* : */ + break; /* : */ } else break; - (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */ + (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */ } return npth; } -static char *add_quote_for_spec_chars(char *found) +static char *add_quote_for_spec_chars(char *found, int add) { int l = 0; char *s = xmalloc((strlen(found) + 1) * 2); @@ -687,19 +711,17 @@ static char *add_quote_for_spec_chars(char *found) s[l++] = '\\'; s[l++] = *found++; } + if(add) + s[l++] = (char)add; s[l] = 0; return s; } -static char **exe_n_cwd_tab_completion(char *command, int *num_matches, - int type) +static void exe_n_cwd_tab_completion(char *command, int type) { - - char **matches = 0; DIR *dir; struct dirent *next; char dirbuf[BUFSIZ]; - int nm = *num_matches; struct stat st; char *path1[1]; char **paths = path1; @@ -721,24 +743,25 @@ static char **exe_n_cwd_tab_completion(char *command, int *num_matches, /* set dir only */ dirbuf[(pfind - command) + 1] = 0; #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION - if (dirbuf[0] == '~') /* ~/... or ~user/... */ - username_tab_completion(dirbuf, 0); + if (dirbuf[0] == '~') /* ~/... or ~user/... */ + username_tab_completion(dirbuf, dirbuf); #endif /* "strip" dirname in command */ pfind++; paths[0] = dirbuf; - npaths = 1; /* only 1 dir */ + npaths = 1; /* only 1 dir */ } for (i = 0; i < npaths; i++) { dir = opendir(paths[i]); - if (!dir) /* Don't print an error */ + if (!dir) /* Don't print an error */ continue; while ((next = readdir(dir)) != NULL) { char *str_found = next->d_name; + int add_chr = 0; /* matched ? */ if (strncmp(str_found, pfind, strlen(pfind))) @@ -746,61 +769,52 @@ static char **exe_n_cwd_tab_completion(char *command, int *num_matches, /* not see .name without .match */ if (*str_found == '.' && *pfind == 0) { if (*paths[i] == '/' && paths[i][1] == 0 - && str_found[1] == 0) str_found = ""; /* only "/" */ + && str_found[1] == 0) str_found = ""; /* only "/" */ else continue; } found = concat_path_file(paths[i], str_found); /* hmm, remover in progress? */ - if (stat(found, &st) < 0) + if (stat(found, &st) < 0) goto cont; /* find with dirs ? */ if (paths[i] != dirbuf) - strcpy(found, next->d_name); /* only name */ + strcpy(found, next->d_name); /* only name */ if (S_ISDIR(st.st_mode)) { /* name is directory */ - str_found = found; - found = concat_path_file(found, ""); - free(str_found); - str_found = add_quote_for_spec_chars(found); + char *e = found + strlen(found) - 1; + + add_chr = '/'; + if(*e == '/') + *e = '\0'; } else { /* not put found file if search only dirs for cd */ - if (type == FIND_DIR_ONLY) + if (type == FIND_DIR_ONLY) goto cont; - str_found = add_quote_for_spec_chars(found); if (type == FIND_FILE_ONLY || (type == FIND_EXE_ONLY && is_execute(&st))) - strcat(str_found, " "); + add_chr = ' '; } /* Add it to the list */ - matches = xrealloc(matches, (nm + 1) * sizeof(char *)); - - matches[nm++] = str_found; + add_match(found, add_chr); + continue; cont: free(found); } closedir(dir); } if (paths != path1) { - free(paths[0]); /* allocated memory only in first member */ + free(paths[0]); /* allocated memory only in first member */ free(paths); } - *num_matches = nm; - return (matches); -} - -static int match_compare(const void *a, const void *b) -{ - return strcmp(*(char **) a, *(char **) b); } - #define QUOT (UCHAR_MAX+1) #define collapse_pos(is, in) { \ - memcpy(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \ - memcpy(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); } + memmove(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \ + memmove(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); } static int find_match(char *matchBuf, int *len_with_quotes) { @@ -814,7 +828,7 @@ static int find_match(char *matchBuf, int *len_with_quotes) for (i = 0;; i++) { int_buf[i] = (int) ((unsigned char) matchBuf[i]); if (int_buf[i] == 0) { - pos_buf[i] = -1; /* indicator end line */ + pos_buf[i] = -1; /* indicator end line */ break; } else pos_buf[i] = i; @@ -827,7 +841,7 @@ static int find_match(char *matchBuf, int *len_with_quotes) int_buf[j] |= QUOT; i++; #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT - if (matchBuf[i] == '\t') /* algorithm equivalent */ + if (matchBuf[i] == '\t') /* algorithm equivalent */ int_buf[j] = ' ' | QUOT; #endif } @@ -870,7 +884,7 @@ static int find_match(char *matchBuf, int *len_with_quotes) } if (command_mode) { collapse_pos(0, i + command_mode); - i = -1; /* hack incremet */ + i = -1; /* hack incremet */ } } /* collapse `command...` */ @@ -887,11 +901,11 @@ static int find_match(char *matchBuf, int *len_with_quotes) collapse_pos(0, i + 1); break; } else - i--; /* hack incremet */ + i--; /* hack incremet */ } /* collapse (command...(command...)...) or {command...{command...}...} */ - c = 0; /* "recursive" level */ + c = 0; /* "recursive" level */ c2 = 0; for (i = 0; int_buf[i]; i++) if (int_buf[i] == '(' || int_buf[i] == '{') { @@ -900,7 +914,7 @@ static int find_match(char *matchBuf, int *len_with_quotes) else c2++; collapse_pos(0, i + 1); - i = -1; /* hack incremet */ + i = -1; /* hack incremet */ } for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) { @@ -909,7 +923,7 @@ static int find_match(char *matchBuf, int *len_with_quotes) else c2--; collapse_pos(0, i + 1); - i = -1; /* hack incremet */ + i = -1; /* hack incremet */ } /* skip first not quote space */ @@ -945,7 +959,7 @@ static int find_match(char *matchBuf, int *len_with_quotes) /* skip first not quoted '\'' or '"' */ for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++); /* collapse quote or unquote // or /~ */ - while ((int_buf[i] & ~QUOT) == '/' && + while ((int_buf[i] & ~QUOT) == '/' && ((int_buf[i + 1] & ~QUOT) == '/' || (int_buf[i + 1] & ~QUOT) == '~')) { i++; @@ -964,31 +978,82 @@ static int find_match(char *matchBuf, int *len_with_quotes) return command_mode; } +/* + display by column original ideas from ls applet, + very optimize by my :) +*/ +static void showfiles(void) +{ + int ncols, row; + int column_width = 0; + int nfiles = num_matches; + int nrows = nfiles; + char str_add_chr[2]; + int l; + + /* find the longest file name- use that as the column width */ + for (row = 0; row < nrows; row++) { + l = strlen(matches[row]); + if(add_char_to_match[row]) + l++; + if (column_width < l) + column_width = l; + } + column_width += 2; /* min space for columns */ + ncols = cmdedit_termw / column_width; + + if (ncols > 1) { + nrows /= ncols; + if(nfiles % ncols) + nrows++; /* round up fractionals */ + } else { + ncols = 1; + } + str_add_chr[1] = 0; + for (row = 0; row < nrows; row++) { + int n = row; + int nc; + int acol; + + for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) { + str_add_chr[0] = add_char_to_match[n]; + acol = str_add_chr[0] ? column_width - 1 : column_width; + printf("%s%s", matches[n], str_add_chr); + l = strlen(matches[n]); + while(l < acol) { + putchar(' '); + l++; + } + } + str_add_chr[0] = add_char_to_match[n]; + printf("%s%s\n", matches[n], str_add_chr); + } +} + static void input_tab(int *lastWasTab) { /* Do TAB completion */ - static int num_matches; - static char **matches; - - if (lastWasTab == 0) { /* free all memory */ + if (lastWasTab == 0) { /* free all memory */ if (matches) { while (num_matches > 0) free(matches[--num_matches]); free(matches); matches = (char **) NULL; + free(add_char_to_match); + add_char_to_match = NULL; } return; } if (! *lastWasTab) { - char *tmp; + char *tmp, *tmp1; int len_found; char matchBuf[BUFSIZ]; int find_type; int recalc_pos; - *lastWasTab = TRUE; /* flop trigger */ + *lastWasTab = TRUE; /* flop trigger */ /* Make a local copy of the string -- up * to the position of the cursor */ @@ -1005,64 +1070,68 @@ static void input_tab(int *lastWasTab) * then try completing this word as a username. */ if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0) - matches = username_tab_completion(matchBuf, &num_matches); + username_tab_completion(matchBuf, NULL); + if (!matches) #endif /* Try to match any executable in our path and everything * in the current working directory that matches. */ - if (!matches) - matches = - exe_n_cwd_tab_completion(matchBuf, - &num_matches, find_type); - /* Remove duplicate found */ + exe_n_cwd_tab_completion(matchBuf, find_type); + /* Remove duplicate found and sort */ if(matches) { - int i, j; + int i, j, n, srt; /* bubble */ - for(i=0; i<(num_matches-1); i++) - for(j=i+1; j 0) { + tmp1 = matches[i]; + matches[i] = matches[j]; + matches[j] = tmp1; + srt = add_char_to_match[i]; + add_char_to_match[i] = add_char_to_match[j]; + add_char_to_match[j] = srt; + } } + j = n; + n = 0; + for(i=0; i 1) { - char *tmp1; beep(); if (!matches) - return; /* not found */ - /* sort */ - qsort(matches, num_matches, sizeof(char *), match_compare); - + return; /* not found */ /* find minimal match */ - tmp = xstrdup(matches[0]); - for (tmp1 = tmp; *tmp1; tmp1++) + tmp1 = bb_xstrdup(matches[0]); + for (tmp = tmp1; *tmp; tmp++) for (len_found = 1; len_found < num_matches; len_found++) - if (matches[len_found][(tmp1 - tmp)] != *tmp1) { - *tmp1 = 0; + if (matches[len_found][(tmp - tmp1)] != *tmp) { + *tmp = 0; break; } - if (*tmp == 0) { /* have unique */ - free(tmp); + if (*tmp1 == 0) { /* have unique */ + free(tmp1); return; } - } else { /* one match */ - tmp = matches[0]; + tmp = add_quote_for_spec_chars(tmp1, 0); + free(tmp1); + } else { /* one match */ + tmp = add_quote_for_spec_chars(matches[0], add_char_to_match[0]); /* for next completion current found */ *lastWasTab = FALSE; } - len_found = strlen(tmp); /* have space to placed match? */ if ((len_found - strlen(matchBuf) + len) < BUFSIZ) { @@ -1084,55 +1153,97 @@ static void input_tab(int *lastWasTab) /* write out the matched command */ redraw(cmdedit_y, len - recalc_pos); } - if (tmp != matches[0]) - free(tmp); + free(tmp); } else { /* Ok -- the last char was a TAB. Since they * just hit TAB again, print a list of all the * available choices... */ if (matches && num_matches > 0) { - int i, col, l; - int sav_cursor = cursor; /* change goto_new_line() */ + int sav_cursor = cursor; /* change goto_new_line() */ /* Go to the next line */ goto_new_line(); - for (i = 0, col = 0; i < num_matches; i++) { - l = strlen(matches[i]); - if (l < 14) - l = 14; - printf("%-14s ", matches[i]); - if ((l += 2) > 16) - while (l % 16) { - putchar(' '); - l++; - } - col += l; - col -= (col / cmdedit_termw) * cmdedit_termw; - if (col > 60 && matches[i + 1] != NULL) { - putchar('\n'); - col = 0; - } - } - /* Go to the next line and rewrite */ - putchar('\n'); + showfiles(); redraw(0, len - sav_cursor); } } } -#endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */ +#endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */ + +#if MAX_HISTORY >= 1 +static void get_previous_history(void) +{ + if(command_ps[0] != 0 || history[cur_history] == 0) { + free(history[cur_history]); + history[cur_history] = bb_xstrdup(command_ps); + } + cur_history--; +} + +static int get_next_history(void) +{ + int ch = cur_history; + + if (ch < n_history) { + get_previous_history(); /* save the current history line */ + return (cur_history = ch+1); + } else { + beep(); + return 0; + } +} -static void get_previous_history(struct history **hp, struct history *p) +#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY +void load_history ( const char *fromfile ) { - if ((*hp)->s) - free((*hp)->s); - (*hp)->s = xstrdup(command_ps); - *hp = p; + FILE *fp; + int hi; + + /* cleanup old */ + + for(hi = n_history; hi > 0; ) { + hi--; + free ( history [hi] ); + } + + if (( fp = fopen ( fromfile, "r" ))) { + + for ( hi = 0; hi < MAX_HISTORY; ) { + char * hl = bb_get_chomped_line_from_file(fp); + int l; + + if(!hl) + break; + l = strlen(hl); + if(l >= BUFSIZ) + hl[BUFSIZ-1] = 0; + if(l == 0 || hl[0] == ' ') { + free(hl); + continue; + } + history [hi++] = hl; + } + fclose ( fp ); + } + cur_history = n_history = hi; } -static inline void get_next_history(struct history **hp) +void save_history ( const char *tofile ) { - get_previous_history(hp, (*hp)->n); + FILE *fp = fopen ( tofile, "w" ); + + if ( fp ) { + int i; + + for ( i = 0; i < n_history; i++ ) { + fprintf(fp, "%s\n", history [i]); + } + fclose ( fp ); + } } +#endif + +#endif enum { ESC = 27, @@ -1143,7 +1254,7 @@ enum { /* * 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 string with full command editing (sort of like * a mini readline). * * The following standard commands are not implemented: @@ -1153,21 +1264,145 @@ enum { * ESC-h -- Delete forward one word * CTL-t -- Transpose two characters * - * Furthermore, the "vi" command editing keys are not implemented. + * Minimalist vi-style command line editing available if configured. + * vi mode implemented 2005 by Paul Fox * */ - + +#ifdef CONFIG_FEATURE_COMMAND_EDITING_VI +static int vi_mode; + +void setvimode ( int viflag ) +{ + vi_mode = viflag; +} + +static void +vi_Word_motion(char *command, int eat) +{ + while (cursor < len && !isspace(command[cursor])) + input_forward(); + if (eat) while (cursor < len && isspace(command[cursor])) + input_forward(); +} + +static void +vi_word_motion(char *command, int eat) +{ + if (isalnum(command[cursor]) || command[cursor] == '_') { + while (cursor < len && + (isalnum(command[cursor+1]) || + command[cursor+1] == '_')) + input_forward(); + } else if (ispunct(command[cursor])) { + while (cursor < len && + (ispunct(command[cursor+1]))) + input_forward(); + } + + if (cursor < len) + input_forward(); + + if (eat && cursor < len && isspace(command[cursor])) + while (cursor < len && isspace(command[cursor])) + input_forward(); +} + +static void +vi_End_motion(char *command) +{ + input_forward(); + while (cursor < len && isspace(command[cursor])) + input_forward(); + while (cursor < len-1 && !isspace(command[cursor+1])) + input_forward(); +} + +static void +vi_end_motion(char *command) +{ + if (cursor >= len-1) + return; + input_forward(); + while (cursor < len-1 && isspace(command[cursor])) + input_forward(); + if (cursor >= len-1) + return; + if (isalnum(command[cursor]) || command[cursor] == '_') { + while (cursor < len-1 && + (isalnum(command[cursor+1]) || + command[cursor+1] == '_')) + input_forward(); + } else if (ispunct(command[cursor])) { + while (cursor < len-1 && + (ispunct(command[cursor+1]))) + input_forward(); + } +} + +static void +vi_Back_motion(char *command) +{ + while (cursor > 0 && isspace(command[cursor-1])) + input_backward(1); + while (cursor > 0 && !isspace(command[cursor-1])) + input_backward(1); +} + +static void +vi_back_motion(char *command) +{ + if (cursor <= 0) + return; + input_backward(1); + while (cursor > 0 && isspace(command[cursor])) + input_backward(1); + if (cursor <= 0) + return; + if (isalnum(command[cursor]) || command[cursor] == '_') { + while (cursor > 0 && + (isalnum(command[cursor-1]) || + command[cursor-1] == '_')) + input_backward(1); + } else if (ispunct(command[cursor])) { + while (cursor > 0 && + (ispunct(command[cursor-1]))) + input_backward(1); + } +} +#endif + +/* + * the emacs and vi modes share much of the code in the big + * command loop. commands entered when in vi's command mode (aka + * "escape mode") get an extra bit added to distinguish them -- + * this keeps them from being self-inserted. this clutters the + * big switch a bit, but keeps all the code in one place. + */ + +#define vbit 0x100 + +/* leave out the "vi-mode"-only case labels if vi editing isn't + * configured. */ +#define vi_case(caselabel) USE_FEATURE_COMMAND_EDITING(caselabel) + +/* convert uppercase ascii to equivalent control char, for readability */ +#define CNTRL(uc_char) ((uc_char) - 0x40) + int cmdedit_read_input(char *prompt, char command[BUFSIZ]) { int break_out = 0; int lastWasTab = FALSE; - unsigned char c = 0; - struct history *hp = his_end; - + unsigned char c; + unsigned int ic; +#ifdef CONFIG_FEATURE_COMMAND_EDITING_VI + unsigned int prevc; + int vi_cmdmode = 0; +#endif /* prepare before init handlers */ - cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */ + cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */ len = 0; command_ps = command; @@ -1176,7 +1411,6 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) new_settings.c_lflag &= ~ICANON; /* unbuffered input */ /* Turn off echoing and CTRL-C, so we can trap it */ new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG); -#ifndef linux /* Hmm, in linux c_cc[] not parsed if set ~ICANON */ new_settings.c_cc[VMIN] = 1; new_settings.c_cc[VTIME] = 0; @@ -1184,8 +1418,7 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) # ifndef _POSIX_VDISABLE # define _POSIX_VDISABLE '\0' # endif - new_settings.c_cc[VINTR] = _POSIX_VDISABLE; -#endif + new_settings.c_cc[VINTR] = _POSIX_VDISABLE; command[0] = 0; setTermSettings(0, (void *) &new_settings); @@ -1198,39 +1431,60 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) while (1) { - fflush(stdout); /* buffered out to fast */ + fflush(stdout); /* buffered out to fast */ if (safe_read(0, &c, 1) < 1) /* if we can't read input then exit */ goto prepare_to_die; - switch (c) { + ic = c; + +#ifdef CONFIG_FEATURE_COMMAND_EDITING_VI + newdelflag = 1; + if (vi_cmdmode) + ic |= vbit; +#endif + switch (ic) + { case '\n': case '\r': + vi_case( case '\n'|vbit: ) + vi_case( case '\r'|vbit: ) /* Enter */ goto_new_line(); break_out = 1; break; - case 1: + case CNTRL('A'): + vi_case( case '0'|vbit: ) /* Control-a -- Beginning of line */ input_backward(cursor); break; - case 2: + case CNTRL('B'): + vi_case( case 'h'|vbit: ) + vi_case( case '\b'|vbit: ) + vi_case( case DEL|vbit: ) /* Control-b -- Move back one character */ input_backward(1); break; - case 3: + case CNTRL('C'): + vi_case( case CNTRL('C')|vbit: ) /* Control-c -- stop gathering input */ goto_new_line(); +#ifndef CONFIG_ASH command[0] = 0; len = 0; lastWasTab = FALSE; put_prompt(); +#else + len = 0; + break_out = -1; /* to control traps */ +#endif break; - case 4: + case CNTRL('D'): /* Control-d -- Delete one character, or exit * if the len=0 and no chars to delete */ if (len == 0) { + errno = 0; prepare_to_die: #if !defined(CONFIG_ASH) printf("exit"); @@ -1238,18 +1492,22 @@ prepare_to_die: /* cmdedit_reset_term() called in atexit */ exit(EXIT_SUCCESS); #else - break_out = -1; /* for control stoped jobs */ + /* to control stopped jobs */ + len = break_out = -1; break; #endif } else { - input_delete(); + input_delete(0); } break; - case 5: + case CNTRL('E'): + vi_case( case '$'|vbit: ) /* Control-e -- End of line */ input_end(); break; - case 6: + case CNTRL('F'): + vi_case( case 'l'|vbit: ) + vi_case( case ' '|vbit: ) /* Control-f -- Move forward one character */ input_forward(); break; @@ -1263,61 +1521,224 @@ prepare_to_die: input_tab(&lastWasTab); #endif break; - case 12: - /* Control-l -- clear screen - * if the len=0 and no chars in edit line */ - if (len == 0) { - printf("\033[H\033[J"); - put_prompt(); - } else { - beep(); - } + case CNTRL('K'): + /* Control-k -- clear to end of line */ + *(command + cursor) = 0; + len = cursor; + printf("\033[J"); break; - case 14: + case CNTRL('L'): + vi_case( case CNTRL('L')|vbit: ) + /* Control-l -- clear screen */ + printf("\033[H"); + redraw(0, len-cursor); + break; +#if MAX_HISTORY >= 1 + case CNTRL('N'): + vi_case( case CNTRL('N')|vbit: ) + vi_case( case 'j'|vbit: ) /* Control-n -- Get next command in history */ - if (hp && hp->n && hp->n->s) { - get_next_history(&hp); + if (get_next_history()) goto rewrite_line; - } else { - beep(); - } break; - case 16: + case CNTRL('P'): + vi_case( case CNTRL('P')|vbit: ) + vi_case( case 'k'|vbit: ) /* Control-p -- Get previous command from history */ - if (hp && hp->p) { - get_previous_history(&hp, hp->p); + if (cur_history > 0) { + get_previous_history(); goto rewrite_line; } else { beep(); } break; - case 21: +#endif + case CNTRL('U'): + vi_case( case CNTRL('U')|vbit: ) /* Control-U -- Clear line before cursor */ if (cursor) { strcpy(command, command + cursor); redraw(cmdedit_y, len -= cursor); } break; - case ESC:{ + case CNTRL('W'): + vi_case( case CNTRL('W')|vbit: ) + /* Control-W -- Remove the last word */ + while (cursor > 0 && isspace(command[cursor-1])) + input_backspace(); + while (cursor > 0 &&!isspace(command[cursor-1])) + input_backspace(); + break; +#if CONFIG_FEATURE_COMMAND_EDITING_VI + case 'i'|vbit: + vi_cmdmode = 0; + break; + case 'I'|vbit: + input_backward(cursor); + vi_cmdmode = 0; + break; + case 'a'|vbit: + input_forward(); + vi_cmdmode = 0; + break; + case 'A'|vbit: + input_end(); + vi_cmdmode = 0; + break; + case 'x'|vbit: + input_delete(1); + break; + case 'X'|vbit: + if (cursor > 0) { + input_backward(1); + input_delete(1); + } + break; + case 'W'|vbit: + vi_Word_motion(command, 1); + break; + case 'w'|vbit: + vi_word_motion(command, 1); + break; + case 'E'|vbit: + vi_End_motion(command); + break; + case 'e'|vbit: + vi_end_motion(command); + break; + case 'B'|vbit: + vi_Back_motion(command); + break; + case 'b'|vbit: + vi_back_motion(command); + break; + case 'C'|vbit: + vi_cmdmode = 0; + /* fall through */ + case 'D'|vbit: + goto clear_to_eol; + + case 'c'|vbit: + vi_cmdmode = 0; + /* fall through */ + case 'd'|vbit: + { + int nc, sc; + sc = cursor; + prevc = ic; + if (safe_read(0, &c, 1) < 1) + goto prepare_to_die; + if (c == (prevc & 0xff)) { + /* "cc", "dd" */ + input_backward(cursor); + goto clear_to_eol; + break; + } + switch(c) { + case 'w': + case 'W': + case 'e': + case 'E': + switch (c) { + case 'w': /* "dw", "cw" */ + vi_word_motion(command, vi_cmdmode); + break; + case 'W': /* 'dW', 'cW' */ + vi_Word_motion(command, vi_cmdmode); + break; + case 'e': /* 'de', 'ce' */ + vi_end_motion(command); + input_forward(); + break; + case 'E': /* 'dE', 'cE' */ + vi_End_motion(command); + input_forward(); + break; + } + nc = cursor; + input_backward(cursor - sc); + while (nc-- > cursor) + input_delete(1); + break; + case 'b': /* "db", "cb" */ + case 'B': /* implemented as B */ + if (c == 'b') + vi_back_motion(command); + else + vi_Back_motion(command); + while (sc-- > cursor) + input_delete(1); + break; + case ' ': /* "d ", "c " */ + input_delete(1); + break; + case '$': /* "d$", "c$" */ + clear_to_eol: + while (cursor < len) + input_delete(1); + break; + } + } + break; + case 'p'|vbit: + input_forward(); + /* fallthrough */ + case 'P'|vbit: + put(); + break; + case 'r'|vbit: + if (safe_read(0, &c, 1) < 1) + goto prepare_to_die; + if (c == 0) + beep(); + else { + *(command + cursor) = c; + putchar(c); + putchar('\b'); + } + break; +#endif /* CONFIG_FEATURE_COMMAND_EDITING_VI */ + + case ESC: + +#if CONFIG_FEATURE_COMMAND_EDITING_VI + if (vi_mode) { + /* ESC: insert mode --> command mode */ + vi_cmdmode = 1; + input_backward(1); + break; + } +#endif /* escape sequence follows */ if (safe_read(0, &c, 1) < 1) goto prepare_to_die; /* different vt100 emulations */ if (c == '[' || c == 'O') { + vi_case( case '['|vbit: ) + vi_case( case 'O'|vbit: ) if (safe_read(0, &c, 1) < 1) goto prepare_to_die; } + if (c >= '1' && c <= '9') { + unsigned char dummy; + + if (safe_read(0, &dummy, 1) < 1) + goto prepare_to_die; + if(dummy != '~') + c = 0; + } switch (c) { #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION - case '\t': /* Alt-Tab */ + case '\t': /* Alt-Tab */ input_tab(&lastWasTab); break; #endif +#if MAX_HISTORY >= 1 case 'A': /* Up Arrow -- Get previous command from history */ - if (hp && hp->p) { - get_previous_history(&hp, hp->p); + if (cur_history > 0) { + get_previous_history(); goto rewrite_line; } else { beep(); @@ -1325,21 +1746,20 @@ prepare_to_die: break; case 'B': /* Down Arrow -- Get next command in history */ - if (hp && hp->n && hp->n->s) { - get_next_history(&hp); - goto rewrite_line; - } else { - beep(); - } - break; - + if (!get_next_history()) + break; /* Rewrite the line with the selected history item */ - rewrite_line: +rewrite_line: /* change command */ - len = strlen(strcpy(command, hp->s)); - /* redraw and go to end line */ + len = strlen(strcpy(command, history[cur_history])); + /* redraw and go to eol (bol, in vi */ +#if CONFIG_FEATURE_COMMAND_EDITING_VI + redraw(cmdedit_y, vi_mode ? 9999:0); +#else redraw(cmdedit_y, 0); +#endif break; +#endif case 'C': /* Right Arrow -- Move forward one character */ input_forward(); @@ -1350,35 +1770,28 @@ prepare_to_die: break; case '3': /* Delete */ - input_delete(); + input_delete(0); break; case '1': case 'H': - /* Home (Ctrl-A) */ + /* */ input_backward(cursor); break; case '4': case 'F': - /* End (Ctrl-E) */ + /* */ input_end(); break; default: - if (!(c >= '1' && c <= '9')) - c = 0; + c = 0; beep(); } - if (c >= '1' && c <= '9') - do - if (safe_read(0, &c, 1) < 1) - goto prepare_to_die; - while (c != '~'); break; - } - default: /* If it's regular input, do the normal thing */ + default: /* If it's regular input, do the normal thing */ #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT /* Control-V -- Add non-printable symbol */ - if (c == 22) { + if (c == CNTRL('V')) { if (safe_read(0, &c, 1) < 1) goto prepare_to_die; if (c == 0) { @@ -1387,19 +1800,25 @@ prepare_to_die: } } else #endif - if (!Isprint(c)) /* Skip non-printable characters */ - break; + { +#if CONFIG_FEATURE_COMMAND_EDITING_VI + if (vi_cmdmode) /* don't self-insert */ + break; +#endif + if (!Isprint(c)) /* Skip non-printable characters */ + break; + } - if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ + if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ break; len++; - if (cursor == (len - 1)) { /* Append if at the end of the line */ + if (cursor == (len - 1)) { /* Append if at the end of the line */ *(command + cursor) = c; *(command + cursor + 1) = 0; cmdedit_set_out_char(0); - } else { /* Insert otherwise */ + } else { /* Insert otherwise */ int sc = cursor; memmove(command + sc + 1, command + sc, len - sc); @@ -1413,7 +1832,7 @@ prepare_to_die: break; } - if (break_out) /* Enter is the command terminator, no more input. */ + if (break_out) /* Enter is the command terminator, no more input. */ break; if (c != '\t') @@ -1423,59 +1842,40 @@ prepare_to_die: setTermSettings(0, (void *) &initial_settings); handlers_sets &= ~SET_RESET_TERM; +#if MAX_HISTORY >= 1 /* Handle command history log */ - if (len) { /* no put empty line */ - - struct history *h = his_end; - char *ss; - - ss = xstrdup(command); /* duplicate */ - - if (h == 0) { - /* No previous history -- this memory is never freed */ - h = his_front = xmalloc(sizeof(struct history)); - h->n = xmalloc(sizeof(struct history)); - - h->p = NULL; - h->s = ss; - h->n->p = h; - h->n->n = NULL; - h->n->s = NULL; - his_end = h->n; - history_counter++; - } else { - /* Add a new history command -- this memory is never freed */ - h->n = xmalloc(sizeof(struct history)); - - h->n->p = h; - h->n->n = NULL; - h->n->s = NULL; - h->s = ss; - his_end = h->n; + /* cleanup may be saved current command line */ + if (len> 0) { /* no put empty line */ + int i = n_history; + free(history[MAX_HISTORY]); + history[MAX_HISTORY] = 0; /* 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++; - } + if (i >= MAX_HISTORY) { + free(history[0]); + for(i = 0; i < (MAX_HISTORY-1); i++) + history[i] = history[i+1]; } + history[i++] = bb_xstrdup(command); + cur_history = i; + n_history = i; #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT) num_ok_lines++; #endif } - if(break_out>0) { - command[len++] = '\n'; /* set '\n' */ - command[len] = 0; +#else /* MAX_HISTORY < 1 */ +#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT) + if (len > 0) { /* no put empty line */ + num_ok_lines++; + } +#endif +#endif /* MAX_HISTORY >= 1 */ + if (break_out > 0) { + command[len++] = '\n'; /* set '\n' */ + command[len] = 0; } #if defined(CONFIG_FEATURE_CLEAN_UP) && defined(CONFIG_FEATURE_COMMAND_TAB_COMPLETION) - input_tab(0); /* strong free */ + input_tab(0); /* strong free */ #endif #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT) free(cmdedit_prompt); @@ -1486,13 +1886,12 @@ prepare_to_die: -#endif /* CONFIG_FEATURE_COMMAND_EDITING */ +#endif /* CONFIG_FEATURE_COMMAND_EDITING */ #ifdef TEST -const char *applet_name = "debug stuff usage"; -const char *memory_exhausted = "Memory exhausted"; +const char *bb_applet_name = "debug stuff usage"; #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT #include @@ -1515,16 +1914,16 @@ int main(int argc, char **argv) #endif while(1) { int l; - cmdedit_read_input(prompt, buff); - l = strlen(buff); - if(l==0) - break; - if(l > 0 && buff[l-1] == '\n') + l = cmdedit_read_input(prompt, buff); + if(l > 0 && buff[l-1] == '\n') { buff[l-1] = 0; - printf("*** cmdedit_read_input() returned line =%s=\n", buff); + printf("*** cmdedit_read_input() returned line =%s=\n", buff); + } else { + break; + } } - printf("*** cmdedit_read_input() detect ^C\n"); + printf("*** cmdedit_read_input() detect ^D\n"); return 0; } -#endif /* TEST */ +#endif /* TEST */