From 7aa63042d04d320b84c207996086bd41b5bc268f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 1 Sep 2010 16:04:24 +0200 Subject: libbb: xfunc_error_retval can be a byte function old new delta xfunc_die 55 60 +5 log_and_exit 43 45 +2 hush_main 961 963 +2 xfunc_error_retval 4 1 -3 tty_main 87 84 -3 sv_main 1183 1180 -3 sort_main 804 801 -3 run_applet_no_and_exit 440 437 -3 puts_maybe_newline 198 195 -3 nohup_main 228 225 -3 httpd_main 753 750 -3 get_header_tar 1496 1493 -3 fsck_minix_main 2972 2969 -3 expr_main 110 107 -3 cmp_main 641 638 -3 chat_main 1359 1356 -3 builtin_exit 48 45 -3 arping_main 1587 1584 -3 BB_EXECVP_or_die 47 44 -3 send_cgi_and_exit 901 895 -6 diff_main 1197 1191 -6 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/18 up/down: 9/-60) Total: -51 bytes Signed-off-by: Denys Vlasenko --- libbb/default_error_retval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libbb') diff --git a/libbb/default_error_retval.c b/libbb/default_error_retval.c index 4da5b3efb..4f6395fa2 100644 --- a/libbb/default_error_retval.c +++ b/libbb/default_error_retval.c @@ -15,4 +15,4 @@ #include "libbb.h" -int xfunc_error_retval = EXIT_FAILURE; +uint8_t xfunc_error_retval = EXIT_FAILURE; -- cgit v1.2.3-55-g6feb From b068bd7a4189f86d55b22242ec65e2ad56a9d719 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 2 Sep 2010 12:01:11 +0200 Subject: lineedit: preparatory cleanup patch for Unicode completion fix Some logic changes and function renames. The fix will follow this patch, to mkae it distinct from cleanup. function old new delta build_match_prefix - 892 +892 username_completion - 121 +121 read_line_input 4902 4966 +64 username_tab_completion 235 - -235 find_match 892 - -892 ------------------------------------------------------------------------------ (add/remove: 2/2 grow/shrink: 1/0 up/down: 1077/-1127) Total: -50 bytes Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 231 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 118 insertions(+), 113 deletions(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 6df556f4e..de7042491 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -154,7 +154,6 @@ struct lineedit_statics { /* Formerly these were big buffers on stack: */ #if ENABLE_FEATURE_TAB_COMPLETION - char exe_n_cwd_tab_completion__dirbuf[MAX_LINELEN]; char input_tab__matchBuf[MAX_LINELEN]; int16_t find_match__int_buf[MAX_LINELEN + 1]; /* need to have 9 bits at least */ int16_t find_match__pos_buf[MAX_LINELEN + 1]; @@ -233,6 +232,8 @@ static unsigned save_string(char *dst, unsigned maxsize) while (dstpos < maxsize) { wchar_t wc; int n = srcpos; + + /* Convert up to 1st invalid byte (or up to end) */ while ((wc = command_ps[srcpos]) != 0 && !unicode_is_raw_byte(wc) ) { @@ -245,6 +246,7 @@ static unsigned save_string(char *dst, unsigned maxsize) dstpos += n; if (wc == 0) /* usually is */ break; + /* We do have invalid byte here! */ command_ps[srcpos] = wc; /* restore it */ srcpos++; @@ -606,53 +608,56 @@ static void add_match(char *matched) } #if ENABLE_FEATURE_USERNAME_COMPLETION -static void username_tab_completion(char *ud, char *with_shash_flg) +/* Replace "~user/..." with "/homedir/...". + * The parameter is malloced, free it or return it + * unchanged if no user is matched. + */ +static char *username_path_completion(char *ud) { struct passwd *entry; + char *tilde_name = ud; + char *home = NULL; + + ud++; /* skip ~ */ + if (*ud == '/') { /* "~/..." */ + home = home_pwd_buf; + } else { + /* "~user/..." */ + ud = strchr(ud, '/'); + *ud = '\0'; /* "~user" */ + entry = getpwnam(tilde_name + 1); + *ud = '/'; /* restore "~user/..." */ + if (entry) + home = entry->pw_dir; + } + if (home) { + ud = concat_path_file(home, ud); + free(tilde_name); + tilde_name = ud; + } + return tilde_name; +} + +/* ~use - find all users with this prefix */ +static NOINLINE void username_completion(const char *ud) +{ + /* Using _r function to avoid pulling in static buffers */ + char line_buff[256]; + struct passwd pwd; + struct passwd *result; int userlen; - ud++; /* ~user/... to user/... */ + ud++; /* skip ~ */ userlen = strlen(ud); - if (with_shash_flg) { /* "~/..." or "~user/..." */ - char *sav_ud = ud - 1; - char *home = NULL; - - if (*ud == '/') { /* "~/..." */ - home = home_pwd_buf; - } else { - /* "~user/..." */ - char *temp; - temp = strchr(ud, '/'); - *temp = '\0'; /* ~user\0 */ - entry = getpwnam(ud); - *temp = '/'; /* restore ~user/... */ - ud = temp; - if (entry) - home = entry->pw_dir; - } - if (home) { - if ((userlen + strlen(home) + 1) < MAX_LINELEN) { - /* /home/user/... */ - sprintf(sav_ud, "%s%s", home, ud); - } + setpwent(); + while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { + /* Null usernames should result in all users as possible completions. */ + if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { + add_match(xasprintf("~%s/", pwd.pw_name)); } - } else { - /* "~[^/]*" */ - /* Using _r function to avoid pulling in static buffers */ - char line_buff[256]; - struct passwd pwd; - struct passwd *result; - - setpwent(); - while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { - /* Null usernames should result in all users as possible completions. */ - if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { - add_match(xasprintf("~%s/", pwd.pw_name)); - } - } - endpwent(); } + endpwent(); } #endif /* FEATURE_COMMAND_USERNAME_COMPLETION */ @@ -662,22 +667,19 @@ enum { FIND_FILE_ONLY = 2, }; -static int path_parse(char ***p, int flags) +static int path_parse(char ***p) { int npth; const char *pth; char *tmp; char **res; - /* if not setenv PATH variable, to search cur dir "." */ - if (flags != FIND_EXE_ONLY) - return 1; - if (state->flags & WITH_PATH_LOOKUP) pth = state->path_lookup; else pth = getenv("PATH"); - /* PATH= or PATH=: */ + + /* PATH="" or PATH=":"? */ if (!pth || !pth[0] || LONE_CHAR(pth, ':')) return 1; @@ -687,12 +689,13 @@ static int path_parse(char ***p, int flags) tmp = strchr(tmp, ':'); if (!tmp) break; - if (*++tmp == '\0') + tmp++; + if (*tmp == '\0') break; /* : */ npth++; } - res = xmalloc(npth * sizeof(char*)); + *p = res = xmalloc(npth * sizeof(res[0])); res[0] = tmp = xstrdup(pth); npth = 1; while (1) { @@ -704,100 +707,96 @@ static int path_parse(char ***p, int flags) break; /* : */ res[npth++] = tmp; } - *p = res; return npth; } static void exe_n_cwd_tab_completion(char *command, int type) { - DIR *dir; - struct dirent *next; - struct stat st; char *path1[1]; char **paths = path1; int npaths; int i; - char *found; - char *pfind = strrchr(command, '/'); -/* char dirbuf[MAX_LINELEN]; */ -#define dirbuf (S.exe_n_cwd_tab_completion__dirbuf) + char *pfind; + char *dirbuf = NULL; npaths = 1; path1[0] = (char*)"."; - if (pfind == NULL) { - /* no dir, if flags==EXE_ONLY - get paths, else "." */ - npaths = path_parse(&paths, type); + pfind = strrchr(command, '/'); + if (!pfind) { + if (type == FIND_EXE_ONLY) + npaths = path_parse(&paths); pfind = command; } else { + /* point to 'l' in "..../last_component" */ + pfind++; /* dirbuf = ".../.../.../" */ - safe_strncpy(dirbuf, command, (pfind - command) + 2); + dirbuf = xstrndup(command, pfind - command); #if ENABLE_FEATURE_USERNAME_COMPLETION if (dirbuf[0] == '~') /* ~/... or ~user/... */ - username_tab_completion(dirbuf, dirbuf); + dirbuf = username_path_completion(dirbuf); #endif paths[0] = dirbuf; - /* point to 'l' in "..../last_component" */ - pfind++; } for (i = 0; i < npaths; i++) { + DIR *dir; + struct dirent *next; + struct stat st; + char *found; + dir = opendir(paths[i]); if (!dir) continue; /* don't print an error */ while ((next = readdir(dir)) != NULL) { - int len1; - const char *str_found = next->d_name; + const char *name_found = next->d_name; - /* matched? */ - if (strncmp(str_found, pfind, strlen(pfind))) + /* .../: bash 3.2.0 shows dotfiles, but not . and .. */ + if (!pfind[0] && DOT_OR_DOTDOT(name_found)) continue; - /* not see .name without .match */ - if (*str_found == '.' && *pfind == '\0') { - if (NOT_LONE_CHAR(paths[i], '/') || str_found[1]) - continue; - str_found = ""; /* only "/" */ - } - found = concat_path_file(paths[i], str_found); - /* hmm, remove in progress? */ + /* match? */ + if (strncmp(name_found, pfind, strlen(pfind)) != 0) + continue; /* no */ + + found = concat_path_file(paths[i], name_found); /* NB: stat() first so that we see is it a directory; * but if that fails, use lstat() so that * we still match dangling links */ if (stat(found, &st) && lstat(found, &st)) - goto cont; - /* find with dirs? */ - if (paths[i] != dirbuf) - strcpy(found, next->d_name); /* only name */ + goto cont; /* hmm, remove in progress? */ - len1 = strlen(found); - found = xrealloc(found, len1 + 2); - found[len1] = '\0'; - found[len1+1] = '\0'; + /* save only name if we scan PATH */ + if (paths[i] != dirbuf) + strcpy(found, name_found); if (S_ISDIR(st.st_mode)) { + unsigned len1 = strlen(found); /* name is a directory */ if (found[len1-1] != '/') { + found = xrealloc(found, len1 + 2); found[len1] = '/'; + found[len1 + 1] = '\0'; } } else { - /* not put found file if search only dirs for cd */ + /* skip files if looking for dirs only (example: cd) */ if (type == FIND_DIR_ONLY) goto cont; } - /* Add it to the list */ + /* add it to the list */ add_match(found); continue; cont: free(found); } closedir(dir); - } + } /* for every path */ + if (paths != path1) { free(paths[0]); /* allocated memory is only in first member */ free(paths); } -#undef dirbuf + free(dirbuf); } /* QUOT is used on elements of int_buf[], which are bytes, @@ -810,15 +809,20 @@ static void exe_n_cwd_tab_completion(char *command, int type) /* is must be <= in */ static void collapse_pos(int is, int in) { - memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in)*sizeof(int_buf[0])); - memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in)*sizeof(pos_buf[0])); + memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in) * sizeof(int_buf[0])); + memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in) * sizeof(pos_buf[0])); } -static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) +/* On entry, matchBuf contains everything up to cursor at the moment + * was pressed. This function looks at it, figures out what part of it + * constitutes the command/file/directory prefix to use for completion, + * and rewrites matchBuf to contain only that part. + */ +static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) { int i, j; int command_mode; int c, c2; -/* Were local, but it uses too much stack */ +/* Were local, but it used too much stack */ /* int16_t int_buf[MAX_LINELEN + 1]; */ /* int16_t pos_buf[MAX_LINELEN + 1]; */ @@ -858,22 +862,23 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) } /* skip commands with arguments if line has commands delimiters */ - /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */ + /* ';' ';;' '&' '|' '&&' '||' but '>&' '<&' '>|' */ for (i = 0; int_buf[i]; i++) { + int n; c = int_buf[i]; c2 = int_buf[i + 1]; j = i ? int_buf[i - 1] : -1; - command_mode = 0; + n = 0; if (c == ';' || c == '&' || c == '|') { - command_mode = 1 + (c == c2); + n = 1 + (c == c2); if (c == '&') { if (j == '>' || j == '<') - command_mode = 0; + n = 0; } else if (c == '|' && j == '>') - command_mode = 0; + n = 0; } - if (command_mode) { - collapse_pos(0, i + command_mode); + if (n) { + collapse_pos(0, i + n); i = -1; /* hack incremet */ } } @@ -941,8 +946,8 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) } } } - for (i = 0; int_buf[i]; i++) - /* "strlen" */; + for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */ + continue; /* find last word */ for (--i; i >= 0; i--) { c = int_buf[i]; @@ -953,7 +958,7 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) } /* skip first not quoted '\'' or '"' */ for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++) - /*skip*/; + continue; /* collapse quote or unquote // or /~ */ while ((int_buf[i] & ~QUOT) == '/' && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~') @@ -1062,7 +1067,7 @@ static void input_tab(smallint *lastWasTab) #endif tmp = matchBuf; - find_type = find_match(matchBuf, &recalc_pos); + find_type = build_match_prefix(matchBuf, &recalc_pos); /* Free up any memory already allocated */ free_tab_completion_data(); @@ -1072,7 +1077,7 @@ static void input_tab(smallint *lastWasTab) * then try completing this word as a username. */ if (state->flags & USERNAME_COMPLETION) if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) - username_tab_completion(matchBuf, NULL); + username_completion(matchBuf); #endif /* Try to match any executable in our path and everything * in the current working directory */ @@ -1081,7 +1086,7 @@ static void input_tab(smallint *lastWasTab) /* Sort, then remove any duplicates found */ if (matches) { unsigned i; - int n = 0; + unsigned n = 0; qsort_string_vector(matches, num_matches); for (i = 0; i < num_matches - 1; ++i) { if (matches[i] && matches[i+1]) { /* paranoia */ @@ -1093,14 +1098,14 @@ static void input_tab(smallint *lastWasTab) } } } - matches[n] = matches[i]; - num_matches = n + 1; + matches[n++] = matches[i]; + num_matches = n; } /* Did we find exactly one match? */ - if (!matches || num_matches > 1) { /* no */ + if (num_matches != 1) { /* no */ beep(); if (!matches) - return; /* not found */ + return; /* no matches at all */ /* find minimal match */ tmp1 = xstrdup(matches[0]); for (tmp = tmp1; *tmp; tmp++) { @@ -1111,13 +1116,14 @@ static void input_tab(smallint *lastWasTab) } } } - if (*tmp1 == '\0') { /* have unique */ - free(tmp1); + if (*tmp1 == '\0') { /* have unique pfx? */ + free(tmp1); /* no */ return; } tmp = add_quote_for_spec_chars(tmp1); free(tmp1); - } else { /* one match */ + len_found = strlen(tmp); + } else { /* exactly one match */ tmp = add_quote_for_spec_chars(matches[0]); /* for next completion current found */ *lastWasTab = FALSE; @@ -1125,11 +1131,10 @@ static void input_tab(smallint *lastWasTab) len_found = strlen(tmp); if (tmp[len_found-1] != '/') { tmp[len_found] = ' '; - tmp[len_found+1] = '\0'; + tmp[++len_found] = '\0'; } } - len_found = strlen(tmp); #if !ENABLE_UNICODE_SUPPORT /* have space to place the match? */ /* The result consists of three parts with these lengths: */ -- cgit v1.2.3-55-g6feb From 61a36af45d9342b65d16a3d29818cf4c70ec992d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 2 Sep 2010 12:03:11 +0200 Subject: lineedit: fix completion with Unicode chars function old new delta read_line_input 4966 5002 +36 bb_wcstombs 170 159 -11 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 36/-11) Total: 25 bytes Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 16 +++++++++++++--- libbb/unicode.c | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index de7042491..381203a22 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -1061,9 +1061,16 @@ static void input_tab(smallint *lastWasTab) /* Make a local copy of the string -- * up to the position of the cursor */ +#if !ENABLE_UNICODE_SUPPORT save_string(matchBuf, cursor + 1); -#if ENABLE_UNICODE_SUPPORT - cursor_mb = strlen(matchBuf); +#else + { + CHAR_T wc = command_ps[cursor]; + command_ps[cursor] = 0; + save_string(matchBuf, MAX_LINELEN); + command_ps[cursor] = wc; + cursor_mb = strlen(matchBuf); + } #endif tmp = matchBuf; @@ -1167,7 +1174,10 @@ static void input_tab(smallint *lastWasTab) sprintf(&command[cursor_mb - recalc_pos], "%s%s", tmp, matchBuf); command_len = load_string(command, S.maxsize); /* write out the matched command */ - redraw(cmdedit_y, command_len - len); + /* paranoia: load_string can return 0 on conv error, + * prevent passing len = (0 - 12) to redraw */ + len = command_len - len; + redraw(cmdedit_y, len >= 0 ? len : 0); } } #endif diff --git a/libbb/unicode.c b/libbb/unicode.c index c4b5f86ee..70c6abe00 100644 --- a/libbb/unicode.c +++ b/libbb/unicode.c @@ -131,7 +131,7 @@ size_t FAST_FUNC wcstombs(char *dest, const wchar_t *src, size_t n) size_t len = wcrtomb_internal(tbuf, wc); if (len > n) - len = n; + break; memcpy(dest, tbuf, len); if (wc == L'\0') return org_n - n; -- cgit v1.2.3-55-g6feb From 7063e86d0d6e0be7f8eed4f7b435a7be3f26ecdd Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 2 Sep 2010 12:44:39 +0200 Subject: lineedit: small readability improvement (same code) Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 381203a22..35f1d0031 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -736,7 +736,7 @@ static void exe_n_cwd_tab_completion(char *command, int type) if (dirbuf[0] == '~') /* ~/... or ~user/... */ dirbuf = username_path_completion(dirbuf); #endif - paths[0] = dirbuf; + path1[0] = dirbuf; } for (i = 0; i < npaths; i++) { -- cgit v1.2.3-55-g6feb From 57ea9b488b096e17418a808d20f263daea468974 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Sep 2010 12:51:36 +0200 Subject: preparatory cleanup patch for tab completion fixes renames and deinlines a few functions function old new delta input_tab - 1041 +1041 complete_cmd_dir_file - 699 +699 complete_username - 121 +121 username_completion 121 - -121 read_line_input 5002 3313 -1689 ------------------------------------------------------------------------------ (add/remove: 3/1 grow/shrink: 0/1 up/down: 1861/-1810) Total: 51 bytes Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 126 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 65 insertions(+), 61 deletions(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 35f1d0031..7bb709f9d 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -1,6 +1,6 @@ /* vi: set sw=4 ts=4: */ /* - * Termios command line History and Editing. + * Command line editing. * * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license. * Written by: Vladimir Oleynik @@ -194,7 +194,7 @@ static void deinit_S(void) { #if ENABLE_FEATURE_EDITING_FANCY_PROMPT /* This one is allocated only if FANCY_PROMPT is on - * (otherwise it points to verbatim prompt (NOT malloced) */ + * (otherwise it points to verbatim prompt (NOT malloced)) */ free((char*)cmdedit_prompt); #endif #if ENABLE_USERNAME_OR_HOMEDIR @@ -213,7 +213,7 @@ static size_t load_string(const char *src, int maxsize) ssize_t len = mbstowcs(command_ps, src, maxsize - 1); if (len < 0) len = 0; - command_ps[len] = 0; + command_ps[len] = BB_NUL; return len; } static unsigned save_string(char *dst, unsigned maxsize) @@ -234,17 +234,17 @@ static unsigned save_string(char *dst, unsigned maxsize) int n = srcpos; /* Convert up to 1st invalid byte (or up to end) */ - while ((wc = command_ps[srcpos]) != 0 + while ((wc = command_ps[srcpos]) != BB_NUL && !unicode_is_raw_byte(wc) ) { srcpos++; } - command_ps[srcpos] = 0; + command_ps[srcpos] = BB_NUL; n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos); if (n < 0) /* should not happen */ break; dstpos += n; - if (wc == 0) /* usually is */ + if (wc == BB_NUL) /* usually is */ break; /* We do have invalid byte here! */ @@ -639,7 +639,7 @@ static char *username_path_completion(char *ud) } /* ~use - find all users with this prefix */ -static NOINLINE void username_completion(const char *ud) +static NOINLINE void complete_username(const char *ud) { /* Using _r function to avoid pulling in static buffers */ char line_buff[256]; @@ -659,7 +659,7 @@ static NOINLINE void username_completion(const char *ud) } endpwent(); } -#endif /* FEATURE_COMMAND_USERNAME_COMPLETION */ +#endif /* FEATURE_USERNAME_COMPLETION */ enum { FIND_EXE_ONLY = 0, @@ -710,7 +710,7 @@ static int path_parse(char ***p) return npth; } -static void exe_n_cwd_tab_completion(char *command, int type) +static NOINLINE void complete_cmd_dir_file(char *command, int type) { char *path1[1]; char **paths = path1; @@ -799,24 +799,25 @@ static void exe_n_cwd_tab_completion(char *command, int type) free(dirbuf); } +/* build_match_prefix: + * On entry, matchBuf contains everything up to cursor at the moment + * was pressed. This function looks at it, figures out what part of it + * constitutes the command/file/directory prefix to use for completion, + * and rewrites matchBuf to contain only that part. + */ +/* Helpers: */ /* QUOT is used on elements of int_buf[], which are bytes, * not Unicode chars. Therefore it works correctly even in Unicode mode. */ #define QUOT (UCHAR_MAX+1) - #define int_buf (S.find_match__int_buf) #define pos_buf (S.find_match__pos_buf) -/* is must be <= in */ -static void collapse_pos(int is, int in) +static void collapse_pos(int beg, int end) { - memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in) * sizeof(int_buf[0])); - memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in) * sizeof(pos_buf[0])); + /* beg must be <= end */ + memmove(int_buf+beg, int_buf+end, (MAX_LINELEN+1-end) * sizeof(int_buf[0])); + memmove(pos_buf+beg, pos_buf+end, (MAX_LINELEN+1-end) * sizeof(pos_buf[0])); } -/* On entry, matchBuf contains everything up to cursor at the moment - * was pressed. This function looks at it, figures out what part of it - * constitutes the command/file/directory prefix to use for completion, - * and rewrites matchBuf to contain only that part. - */ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) { int i, j; @@ -826,17 +827,16 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) /* int16_t int_buf[MAX_LINELEN + 1]; */ /* int16_t pos_buf[MAX_LINELEN + 1]; */ - /* set to integer dimension characters and own positions */ for (i = 0;; i++) { int_buf[i] = (unsigned char)matchBuf[i]; if (int_buf[i] == 0) { - pos_buf[i] = -1; /* end-fo-line indicator */ + pos_buf[i] = -1; /* end-of-line indicator */ break; } pos_buf[i] = i; } - /* mask \+symbol and convert '\t' to ' ' */ + /* Mark every \c as "quoted c" */ for (i = j = 0; matchBuf[i]; i++, j++) { if (matchBuf[i] == '\\') { collapse_pos(j, j + 1); @@ -844,7 +844,7 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) i++; } } - /* mask "symbols" or 'symbols' */ + /* Quote-mark "chars" and 'chars' */ c2 = 0; for (i = 0; int_buf[i]; i++) { c = int_buf[i]; @@ -861,8 +861,10 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) int_buf[i] |= QUOT; } - /* skip commands with arguments if line has commands delimiters */ - /* ';' ';;' '&' '|' '&&' '||' but '>&' '<&' '>|' */ + /* Remove everything up to command delimiters: + * ';' ';;' '&' '|' '&&' '||', + * but careful with '>&' '<&' '>|' + */ for (i = 0; int_buf[i]; i++) { int n; c = int_buf[i]; @@ -879,28 +881,29 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) } if (n) { collapse_pos(0, i + n); - i = -1; /* hack incremet */ + i = -1; /* hack increment */ } } - /* collapse `command...` */ + /* Remove all `cmd` */ for (i = 0; int_buf[i]; i++) { if (int_buf[i] == '`') { - for (j = i + 1; int_buf[j]; j++) + for (j = i + 1; int_buf[j]; j++) { if (int_buf[j] == '`') { collapse_pos(i, j + 1); j = 0; break; } + } if (j) { - /* not found closing ` - command mode, collapse all previous */ + /* No closing ` - command mode, remove all up to ` */ collapse_pos(0, i + 1); break; - } else - i--; /* hack incremet */ + } + i--; /* hack increment */ } } - /* collapse (command...(command...)...) or {command...{command...}...} */ + /* Remove (command...(command...)...) and {command...{command...}...} */ c = 0; /* "recursive" level */ c2 = 0; for (i = 0; int_buf[i]; i++) { @@ -910,7 +913,7 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) else c2++; collapse_pos(0, i + 1); - i = -1; /* hack incremet */ + i = -1; /* hack increment */ } } for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) { @@ -920,22 +923,23 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) else c2--; collapse_pos(0, i + 1); - i = -1; /* hack incremet */ + i = -1; /* hack increment */ } } - /* skip first not quote space */ + /* Remove leading unquoted spaces */ for (i = 0; int_buf[i]; i++) if (int_buf[i] != ' ') break; if (i) collapse_pos(0, i); - /* set find mode for completion */ + /* Determine completion mode */ command_mode = FIND_EXE_ONLY; for (i = 0; int_buf[i]; i++) { if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { - if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY + if (int_buf[i] == ' ' + && command_mode == FIND_EXE_ONLY && matchBuf[pos_buf[0]] == 'c' && matchBuf[pos_buf[1]] == 'd' ) { @@ -946,9 +950,10 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) } } } + + /* Remove everything except last word */ for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */ continue; - /* find last word */ for (--i; i >= 0; i--) { c = int_buf[i]; if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') { @@ -956,7 +961,7 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) break; } } - /* skip first not quoted '\'' or '"' */ + /* Skip all leading unquoted ' or " */ for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++) continue; /* collapse quote or unquote // or /~ */ @@ -982,8 +987,8 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) #undef pos_buf /* - * display by column (original idea from ls applet, - * very optimized by me :) + * Display by column (original idea from ls applet, + * very optimized by me [Vladimir] :) */ static void showfiles(void) { @@ -1040,7 +1045,7 @@ static char *add_quote_for_spec_chars(char *found) } /* Do TAB completion */ -static void input_tab(smallint *lastWasTab) +static NOINLINE void input_tab(smallint *lastWasTab) { if (!(state->flags & TAB_COMPLETION)) return; @@ -1057,7 +1062,7 @@ static void input_tab(smallint *lastWasTab) int cursor_mb; #endif - *lastWasTab = TRUE; /* flop trigger */ + *lastWasTab = 1; /* Make a local copy of the string -- * up to the position of the cursor */ @@ -1066,7 +1071,7 @@ static void input_tab(smallint *lastWasTab) #else { CHAR_T wc = command_ps[cursor]; - command_ps[cursor] = 0; + command_ps[cursor] = BB_NUL; save_string(matchBuf, MAX_LINELEN); command_ps[cursor] = wc; cursor_mb = strlen(matchBuf); @@ -1084,26 +1089,25 @@ static void input_tab(smallint *lastWasTab) * then try completing this word as a username. */ if (state->flags & USERNAME_COMPLETION) if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) - username_completion(matchBuf); + complete_username(matchBuf); #endif - /* Try to match any executable in our path and everything - * in the current working directory */ + /* Try to match a command in $PATH, or a directory, or a file */ if (!matches) - exe_n_cwd_tab_completion(matchBuf, find_type); - /* Sort, then remove any duplicates found */ + complete_cmd_dir_file(matchBuf, find_type); + /* Remove duplicates */ if (matches) { unsigned i; unsigned n = 0; qsort_string_vector(matches, num_matches); for (i = 0; i < num_matches - 1; ++i) { - if (matches[i] && matches[i+1]) { /* paranoia */ + //if (matches[i] && matches[i+1]) { /* paranoia */ if (strcmp(matches[i], matches[i+1]) == 0) { free(matches[i]); - matches[i] = NULL; /* paranoia */ + //matches[i] = NULL; /* paranoia */ } else { matches[n++] = matches[i]; } - } + //} } matches[n++] = matches[i]; num_matches = n; @@ -1113,7 +1117,7 @@ static void input_tab(smallint *lastWasTab) beep(); if (!matches) return; /* no matches at all */ - /* find minimal match */ + /* Find common prefix */ tmp1 = xstrdup(matches[0]); for (tmp = tmp1; *tmp; tmp++) { for (len_found = 1; len_found < num_matches; len_found++) { @@ -1123,7 +1127,7 @@ static void input_tab(smallint *lastWasTab) } } } - if (*tmp1 == '\0') { /* have unique pfx? */ + if (*tmp1 == '\0') { /* have unique prefix? */ free(tmp1); /* no */ return; } @@ -1131,10 +1135,10 @@ static void input_tab(smallint *lastWasTab) free(tmp1); len_found = strlen(tmp); } else { /* exactly one match */ - tmp = add_quote_for_spec_chars(matches[0]); - /* for next completion current found */ - *lastWasTab = FALSE; + /* Next is not a double-tab */ + *lastWasTab = 0; + tmp = add_quote_for_spec_chars(matches[0]); len_found = strlen(tmp); if (tmp[len_found-1] != '/') { tmp[len_found] = ' '; @@ -1199,7 +1203,7 @@ static void input_tab(smallint *lastWasTab) } } -#endif /* FEATURE_COMMAND_TAB_COMPLETION */ +#endif /* FEATURE_TAB_COMPLETION */ line_input_t* FAST_FUNC new_line_input_t(int flags) @@ -1899,7 +1903,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li { int len; #if ENABLE_FEATURE_TAB_COMPLETION - smallint lastWasTab = FALSE; + smallint lastWasTab = 0; #endif smallint break_out = 0; #if ENABLE_FEATURE_EDITING_VI @@ -2375,7 +2379,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li #if ENABLE_FEATURE_TAB_COMPLETION if (ic_raw != '\t') - lastWasTab = FALSE; + lastWasTab = 0; #endif } /* while (1) */ @@ -2469,7 +2473,7 @@ int main(int argc, char **argv) l = read_line_input(prompt, buff); if (l <= 0 || buff[l-1] != '\n') break; - buff[l-1] = 0; + buff[l-1] = '\0'; printf("*** read_line_input() returned line =%s=\n", buff); } printf("*** read_line_input() detect ^D\n"); -- cgit v1.2.3-55-g6feb From 2679e3c8cccbf3ae4cac8e735e5430ebbe714b00 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Sep 2010 12:53:15 +0200 Subject: lineedit: clean up tab completion code (variable reuse, comments) Noted bugs in behavior. Added debugging machinery. Decoupled variables reused for unrelated purposes: apparently, when not forced to use liveness analysis, gcc fares better. function old new delta complete_cmd_dir_file 699 705 +6 collapse_pos 75 79 +4 build_match_prefix 892 838 -54 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 10/-54) Total: -44 bytes Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 154 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 85 insertions(+), 69 deletions(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 7bb709f9d..dd9d85b28 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -716,6 +716,7 @@ static NOINLINE void complete_cmd_dir_file(char *command, int type) char **paths = path1; int npaths; int i; + unsigned pf_len; char *pfind; char *dirbuf = NULL; @@ -738,6 +739,7 @@ static NOINLINE void complete_cmd_dir_file(char *command, int type) #endif path1[0] = dirbuf; } + pf_len = strlen(pfind); for (i = 0; i < npaths; i++) { DIR *dir; @@ -756,7 +758,7 @@ static NOINLINE void complete_cmd_dir_file(char *command, int type) if (!pfind[0] && DOT_OR_DOTDOT(name_found)) continue; /* match? */ - if (strncmp(name_found, pfind, strlen(pfind)) != 0) + if (strncmp(name_found, pfind, pf_len) != 0) continue; /* no */ found = concat_path_file(paths[i], name_found); @@ -812,21 +814,31 @@ static NOINLINE void complete_cmd_dir_file(char *command, int type) #define QUOT (UCHAR_MAX+1) #define int_buf (S.find_match__int_buf) #define pos_buf (S.find_match__pos_buf) +#define dbg_bmp 0 static void collapse_pos(int beg, int end) { /* beg must be <= end */ + if (beg == end) + return; memmove(int_buf+beg, int_buf+end, (MAX_LINELEN+1-end) * sizeof(int_buf[0])); memmove(pos_buf+beg, pos_buf+end, (MAX_LINELEN+1-end) * sizeof(pos_buf[0])); + if (dbg_bmp) { + int i; + for (i = 0; int_buf[i]; i++) + bb_putchar((unsigned char)int_buf[i]); + bb_putchar('\n'); + } } static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) { int i, j; int command_mode; - int c, c2; /* Were local, but it used too much stack */ /* int16_t int_buf[MAX_LINELEN + 1]; */ /* int16_t pos_buf[MAX_LINELEN + 1]; */ + if (dbg_bmp) printf("\n%s\n", matchBuf); + for (i = 0;; i++) { int_buf[i] = (unsigned char)matchBuf[i]; if (int_buf[i] == 0) { @@ -845,20 +857,21 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) } } /* Quote-mark "chars" and 'chars' */ - c2 = 0; - for (i = 0; int_buf[i]; i++) { - c = int_buf[i]; - if (c == '\'' || c == '"') { - if (c2 == 0) - c2 = c; - else { - if (c == c2) - c2 = 0; + { + int in_quote = 0; + for (i = 0; int_buf[i]; i++) { + int cur = int_buf[i]; + if (cur == '\'' || cur == '"') { + if (!in_quote) + in_quote = cur; + else if (cur == in_quote) + in_quote = 0; else int_buf[i] |= QUOT; + } else if (in_quote && cur != '$') { + int_buf[i] |= QUOT; } - } else if (c2 != 0 && c != '$') - int_buf[i] |= QUOT; + } } /* Remove everything up to command delimiters: @@ -866,64 +879,61 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) * but careful with '>&' '<&' '>|' */ for (i = 0; int_buf[i]; i++) { - int n; - c = int_buf[i]; - c2 = int_buf[i + 1]; - j = i ? int_buf[i - 1] : -1; - n = 0; - if (c == ';' || c == '&' || c == '|') { - n = 1 + (c == c2); - if (c == '&') { - if (j == '>' || j == '<') - n = 0; - } else if (c == '|' && j == '>') - n = 0; - } - if (n) { - collapse_pos(0, i + n); - i = -1; /* hack increment */ + int cur = int_buf[i]; + if (cur == ';' || cur == '&' || cur == '|') { + int prev = i ? int_buf[i - 1] : 0; + if (cur == '&' && (prev == '>' || prev == '<')) { + continue; + } else if (cur == '|' && prev == '>') { + continue; + } + collapse_pos(0, i + 1 + (cur == int_buf[i + 1])); + i = -1; /* back to square 1 */ } } /* Remove all `cmd` */ +//BUG: `cmd` should count as a word: `cmd` c should search for files c*, not commands c* for (i = 0; int_buf[i]; i++) { if (int_buf[i] == '`') { for (j = i + 1; int_buf[j]; j++) { if (int_buf[j] == '`') { collapse_pos(i, j + 1); - j = 0; - break; + goto next; } } - if (j) { - /* No closing ` - command mode, remove all up to ` */ - collapse_pos(0, i + 1); - break; - } + /* No closing ` - command mode, remove all up to ` */ + collapse_pos(0, i + 1); + break; + next: i--; /* hack increment */ } } /* Remove (command...(command...)...) and {command...{command...}...} */ - c = 0; /* "recursive" level */ - c2 = 0; - for (i = 0; int_buf[i]; i++) { - if (int_buf[i] == '(' || int_buf[i] == '{') { - if (int_buf[i] == '(') - c++; - else - c2++; - collapse_pos(0, i + 1); - i = -1; /* hack increment */ + { + int paren_lvl = 0; + int curly_lvl = 0; + for (i = 0; int_buf[i]; i++) { + if (int_buf[i] == '(' || int_buf[i] == '{') { + if (int_buf[i] == '(') + paren_lvl++; + else + curly_lvl++; + collapse_pos(0, i + 1); + i = -1; /* hack increment */ + } } - } - for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) { - if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) { - if (int_buf[i] == ')') - c--; - else - c2--; - collapse_pos(0, i + 1); - i = -1; /* hack increment */ + for (i = 0; pos_buf[i] >= 0 && (paren_lvl > 0 || curly_lvl > 0); i++) { + if ((int_buf[i] == ')' && paren_lvl > 0) + || (int_buf[i] == '}' && curly_lvl > 0) + ) { + if (int_buf[i] == ')') + paren_lvl--; + else + curly_lvl--; + collapse_pos(0, i + 1); + i = -1; /* hack increment */ + } } } @@ -931,8 +941,7 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) for (i = 0; int_buf[i]; i++) if (int_buf[i] != ' ') break; - if (i) - collapse_pos(0, i); + collapse_pos(0, i); /* Determine completion mode */ command_mode = FIND_EXE_ONLY; @@ -942,6 +951,7 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) && command_mode == FIND_EXE_ONLY && matchBuf[pos_buf[0]] == 'c' && matchBuf[pos_buf[1]] == 'd' +//BUG: must check "cd ", not "cd" ) { command_mode = FIND_DIR_ONLY; } else { @@ -950,36 +960,42 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) } } } + if (dbg_bmp) printf("command_mode(0:exe/1:dir/2:file):%d\n", command_mode); /* Remove everything except last word */ for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */ continue; for (--i; i >= 0; i--) { - c = int_buf[i]; - if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') { + int cur = int_buf[i]; + if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') { collapse_pos(0, i + 1); break; } } /* Skip all leading unquoted ' or " */ +//BUG: bash doesn't do this for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++) continue; - /* collapse quote or unquote // or /~ */ - while ((int_buf[i] & ~QUOT) == '/' - && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~') + /* Skip quoted or unquoted // or /~ */ +//BUG: bash doesn't do this + while ((char)int_buf[i] == '/' + && ((char)int_buf[i+1] == '/' || (char)int_buf[i+1] == '~') ) { i++; } /* set only match and destroy quotes */ - j = 0; - for (c = 0; pos_buf[i] >= 0; i++) { - matchBuf[c++] = matchBuf[pos_buf[i]]; - j = pos_buf[i] + 1; + { + int pos = 0; + for (j = 0; pos_buf[i] >= 0; i++) { + matchBuf[j++] = matchBuf[pos_buf[i]]; + pos = pos_buf[i] + 1; + } + matchBuf[j] = '\0'; + /* old length matchBuf with quotes symbols */ + *len_with_quotes = pos ? pos - pos_buf[0] : 0; + if (dbg_bmp) printf("len_with_quotes:%d\n", *len_with_quotes); } - matchBuf[c] = '\0'; - /* old length matchBuf with quotes symbols */ - *len_with_quotes = j ? j - pos_buf[0] : 0; return command_mode; } -- cgit v1.2.3-55-g6feb From 3c460b005c3c422fe73dc464902e0626fc57938a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Sep 2010 12:56:36 +0200 Subject: lineedit: return prefix len from tab completion helpers This kills horrific logic which deletes and re-adds prefix (!) function old new delta complete_cmd_dir_file 705 731 +26 complete_username 121 124 +3 input_tab 1041 1016 -25 build_match_prefix 838 804 -34 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/2 up/down: 29/-59) Total: -30 bytes Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 86 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 36 deletions(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index dd9d85b28..e40917f62 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -638,14 +638,16 @@ static char *username_path_completion(char *ud) return tilde_name; } -/* ~use - find all users with this prefix */ -static NOINLINE void complete_username(const char *ud) +/* ~use - find all users with this prefix. + * Return the length of the prefix used for matching. + */ +static NOINLINE unsigned complete_username(const char *ud) { /* Using _r function to avoid pulling in static buffers */ char line_buff[256]; struct passwd pwd; struct passwd *result; - int userlen; + unsigned userlen; ud++; /* skip ~ */ userlen = strlen(ud); @@ -658,6 +660,8 @@ static NOINLINE void complete_username(const char *ud) } } endpwent(); + + return 1 + userlen; } #endif /* FEATURE_USERNAME_COMPLETION */ @@ -710,14 +714,17 @@ static int path_parse(char ***p) return npth; } -static NOINLINE void complete_cmd_dir_file(char *command, int type) +/* Complete command, directory or file name. + * Return the length of the prefix used for matching. + */ +static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) { char *path1[1]; char **paths = path1; int npaths; int i; unsigned pf_len; - char *pfind; + const char *pfind; char *dirbuf = NULL; npaths = 1; @@ -797,8 +804,12 @@ static NOINLINE void complete_cmd_dir_file(char *command, int type) if (paths != path1) { free(paths[0]); /* allocated memory is only in first member */ free(paths); + } else if (dirbuf) { + pf_len += strlen(dirbuf); + free(dirbuf); } - free(dirbuf); + + return pf_len; } /* build_match_prefix: @@ -829,7 +840,7 @@ static void collapse_pos(int beg, int end) bb_putchar('\n'); } } -static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) +static NOINLINE int build_match_prefix(char *matchBuf) { int i, j; int command_mode; @@ -992,9 +1003,6 @@ static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) pos = pos_buf[i] + 1; } matchBuf[j] = '\0'; - /* old length matchBuf with quotes symbols */ - *len_with_quotes = pos ? pos - pos_buf[0] : 0; - if (dbg_bmp) printf("len_with_quotes:%d\n", *len_with_quotes); } return command_mode; @@ -1067,12 +1075,13 @@ static NOINLINE void input_tab(smallint *lastWasTab) return; if (!*lastWasTab) { - char *tmp, *tmp1; + char *tmp; size_t len_found; /* char matchBuf[MAX_LINELEN]; */ #define matchBuf (S.input_tab__matchBuf) + /* Length of string used for matching */ + unsigned match_pfx_len = match_pfx_len; int find_type; - int recalc_pos; #if ENABLE_UNICODE_SUPPORT /* cursor pos in command converted to multibyte form */ int cursor_mb; @@ -1095,7 +1104,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) #endif tmp = matchBuf; - find_type = build_match_prefix(matchBuf, &recalc_pos); + find_type = build_match_prefix(matchBuf); /* Free up any memory already allocated */ free_tab_completion_data(); @@ -1105,11 +1114,11 @@ static NOINLINE void input_tab(smallint *lastWasTab) * then try completing this word as a username. */ if (state->flags & USERNAME_COMPLETION) if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) - complete_username(matchBuf); + match_pfx_len = complete_username(matchBuf); #endif /* Try to match a command in $PATH, or a directory, or a file */ if (!matches) - complete_cmd_dir_file(matchBuf, find_type); + match_pfx_len = complete_cmd_dir_file(matchBuf, find_type); /* Remove duplicates */ if (matches) { unsigned i; @@ -1130,23 +1139,26 @@ static NOINLINE void input_tab(smallint *lastWasTab) } /* Did we find exactly one match? */ if (num_matches != 1) { /* no */ + char *tmp1; beep(); if (!matches) return; /* no matches at all */ /* Find common prefix */ tmp1 = xstrdup(matches[0]); for (tmp = tmp1; *tmp; tmp++) { - for (len_found = 1; len_found < num_matches; len_found++) { - if (matches[len_found][tmp - tmp1] != *tmp) { - *tmp = '\0'; - break; + unsigned n; + for (n = 1; n < num_matches; n++) { + if (matches[n][tmp - tmp1] != *tmp) { + goto stop; } } } - if (*tmp1 == '\0') { /* have unique prefix? */ + stop: + if (tmp1 == tmp) { /* have unique prefix? */ free(tmp1); /* no */ return; } + *tmp = '\0'; tmp = add_quote_for_spec_chars(tmp1); free(tmp1); len_found = strlen(tmp); @@ -1163,41 +1175,43 @@ static NOINLINE void input_tab(smallint *lastWasTab) } #if !ENABLE_UNICODE_SUPPORT - /* have space to place the match? */ + /* Have space to place the match? */ /* The result consists of three parts with these lengths: */ - /* (cursor - recalc_pos) + len_found + (command_len - cursor) */ + /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */ /* it simplifies into: */ - if ((int)(len_found + command_len - recalc_pos) < S.maxsize) { + if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) { + int pos; /* save tail */ - strcpy(matchBuf, command_ps + cursor); + strcpy(matchBuf, &command_ps[cursor]); /* add match and tail */ - sprintf(&command_ps[cursor - recalc_pos], "%s%s", tmp, matchBuf); + sprintf(&command_ps[cursor], "%s%s", tmp + match_pfx_len, matchBuf); command_len = strlen(command_ps); /* new pos */ - recalc_pos = cursor - recalc_pos + len_found; + pos = cursor + len_found - match_pfx_len; /* write out the matched command */ - redraw(cmdedit_y, command_len - recalc_pos); + redraw(cmdedit_y, command_len - pos); } #else { char command[MAX_LINELEN]; int len = save_string(command, sizeof(command)); - /* have space to place the match? */ - /* (cursor_mb - recalc_pos) + len_found + (len - cursor_mb) */ - if ((int)(len_found + len - recalc_pos) < MAX_LINELEN) { + /* Have space to place the match? */ + /* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */ + if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) { + int pos; /* save tail */ - strcpy(matchBuf, command + cursor_mb); + strcpy(matchBuf, &command[cursor_mb]); /* where do we want to have cursor after all? */ - strcpy(&command[cursor_mb - recalc_pos], tmp); + strcpy(&command[cursor_mb], tmp + match_pfx_len); len = load_string(command, S.maxsize); /* add match and tail */ - sprintf(&command[cursor_mb - recalc_pos], "%s%s", tmp, matchBuf); + sprintf(&command[cursor_mb], "%s%s", tmp + match_pfx_len, matchBuf); command_len = load_string(command, S.maxsize); /* write out the matched command */ /* paranoia: load_string can return 0 on conv error, - * prevent passing len = (0 - 12) to redraw */ - len = command_len - len; - redraw(cmdedit_y, len >= 0 ? len : 0); + * prevent passing pos = (0 - 12) to redraw */ + pos = command_len - len; + redraw(cmdedit_y, pos >= 0 ? pos : 0); } } #endif -- cgit v1.2.3-55-g6feb From 81254ed3875f7be81b98266866de7f990bcd06c8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Sep 2010 12:59:15 +0200 Subject: lineedit: remove pos_buf[] array (up to 16k!); fix compat bugs pos_buf is a strange hack, easy to do without it. This also allows lines >32k long to be handled. Also simplified match prefix generations and made behavior more like bash. function old new delta remove_chunk - 43 +43 collapse_pos 79 - -79 build_match_prefix 804 579 -225 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 0/1 up/down: 43/-304) Total: -261 bytes Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 132 +++++++++++++++++++++---------------------------------- 1 file changed, 49 insertions(+), 83 deletions(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index e40917f62..fff5c1a89 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -99,7 +99,6 @@ static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); } enum { - /* We use int16_t for positions, need to limit line len */ MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0 ? CONFIG_FEATURE_EDITING_MAX_LEN : 0x7ff0 @@ -156,7 +155,6 @@ struct lineedit_statics { #if ENABLE_FEATURE_TAB_COMPLETION char input_tab__matchBuf[MAX_LINELEN]; int16_t find_match__int_buf[MAX_LINELEN + 1]; /* need to have 9 bits at least */ - int16_t find_match__pos_buf[MAX_LINELEN + 1]; #endif }; @@ -824,15 +822,16 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) */ #define QUOT (UCHAR_MAX+1) #define int_buf (S.find_match__int_buf) -#define pos_buf (S.find_match__pos_buf) #define dbg_bmp 0 -static void collapse_pos(int beg, int end) +static void remove_chunk(int beg, int end) { /* beg must be <= end */ if (beg == end) return; - memmove(int_buf+beg, int_buf+end, (MAX_LINELEN+1-end) * sizeof(int_buf[0])); - memmove(pos_buf+beg, pos_buf+end, (MAX_LINELEN+1-end) * sizeof(pos_buf[0])); + + while ((int_buf[beg] = int_buf[end]) != 0) + beg++, end++; + if (dbg_bmp) { int i; for (i = 0; int_buf[i]; i++) @@ -846,42 +845,39 @@ static NOINLINE int build_match_prefix(char *matchBuf) int command_mode; /* Were local, but it used too much stack */ /* int16_t int_buf[MAX_LINELEN + 1]; */ -/* int16_t pos_buf[MAX_LINELEN + 1]; */ if (dbg_bmp) printf("\n%s\n", matchBuf); - for (i = 0;; i++) { - int_buf[i] = (unsigned char)matchBuf[i]; - if (int_buf[i] == 0) { - pos_buf[i] = -1; /* end-of-line indicator */ - break; - } - pos_buf[i] = i; - } + i = 0; + while ((int_buf[i] = (unsigned char)matchBuf[i]) != '\0') + i++; /* Mark every \c as "quoted c" */ for (i = j = 0; matchBuf[i]; i++, j++) { if (matchBuf[i] == '\\') { - collapse_pos(j, j + 1); + remove_chunk(j, j + 1); int_buf[j] |= QUOT; i++; } } - /* Quote-mark "chars" and 'chars' */ + /* Quote-mark "chars" and 'chars', drop delimiters */ { int in_quote = 0; - for (i = 0; int_buf[i]; i++) { + i = 0; + while (int_buf[i]) { int cur = int_buf[i]; + if (!cur) + break; if (cur == '\'' || cur == '"') { - if (!in_quote) - in_quote = cur; - else if (cur == in_quote) - in_quote = 0; - else - int_buf[i] |= QUOT; - } else if (in_quote && cur != '$') { - int_buf[i] |= QUOT; + if (!in_quote || (cur == in_quote)) { + in_quote ^= cur; + remove_chunk(i, i + 1); + continue; + } } + if (in_quote) + int_buf[i] = cur | QUOT; + i++; } } @@ -898,53 +894,39 @@ static NOINLINE int build_match_prefix(char *matchBuf) } else if (cur == '|' && prev == '>') { continue; } - collapse_pos(0, i + 1 + (cur == int_buf[i + 1])); + remove_chunk(0, i + 1 + (cur == int_buf[i + 1])); i = -1; /* back to square 1 */ } } /* Remove all `cmd` */ -//BUG: `cmd` should count as a word: `cmd` c should search for files c*, not commands c* for (i = 0; int_buf[i]; i++) { if (int_buf[i] == '`') { for (j = i + 1; int_buf[j]; j++) { if (int_buf[j] == '`') { - collapse_pos(i, j + 1); + /* `cmd` should count as a word: + * `cmd` c should search for files c*, + * not commands c*. Therefore we don't drop + * `cmd` entirely, we replace it with single `. + */ + remove_chunk(i, j); goto next; } } /* No closing ` - command mode, remove all up to ` */ - collapse_pos(0, i + 1); + remove_chunk(0, i + 1); break; - next: - i--; /* hack increment */ + next: ; } } - /* Remove (command...(command...)...) and {command...{command...}...} */ - { - int paren_lvl = 0; - int curly_lvl = 0; - for (i = 0; int_buf[i]; i++) { - if (int_buf[i] == '(' || int_buf[i] == '{') { - if (int_buf[i] == '(') - paren_lvl++; - else - curly_lvl++; - collapse_pos(0, i + 1); - i = -1; /* hack increment */ - } - } - for (i = 0; pos_buf[i] >= 0 && (paren_lvl > 0 || curly_lvl > 0); i++) { - if ((int_buf[i] == ')' && paren_lvl > 0) - || (int_buf[i] == '}' && curly_lvl > 0) - ) { - if (int_buf[i] == ')') - paren_lvl--; - else - curly_lvl--; - collapse_pos(0, i + 1); - i = -1; /* hack increment */ - } + /* Remove "cmd (" and "cmd {" + * Example: "if { c" + * In this example, c should be matched as command pfx. + */ + for (i = 0; int_buf[i]; i++) { + if (int_buf[i] == '(' || int_buf[i] == '{') { + remove_chunk(0, i + 1); + i = -1; /* hack increment */ } } @@ -952,7 +934,7 @@ static NOINLINE int build_match_prefix(char *matchBuf) for (i = 0; int_buf[i]; i++) if (int_buf[i] != ' ') break; - collapse_pos(0, i); + remove_chunk(0, i); /* Determine completion mode */ command_mode = FIND_EXE_ONLY; @@ -960,9 +942,9 @@ static NOINLINE int build_match_prefix(char *matchBuf) if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY - && matchBuf[pos_buf[0]] == 'c' - && matchBuf[pos_buf[1]] == 'd' -//BUG: must check "cd ", not "cd" + && (char)int_buf[0] == 'c' + && (char)int_buf[1] == 'd' + && i == 2 /* -> int_buf[2] == ' ' */ ) { command_mode = FIND_DIR_ONLY; } else { @@ -979,36 +961,20 @@ static NOINLINE int build_match_prefix(char *matchBuf) for (--i; i >= 0; i--) { int cur = int_buf[i]; if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') { - collapse_pos(0, i + 1); + remove_chunk(0, i + 1); break; } } - /* Skip all leading unquoted ' or " */ -//BUG: bash doesn't do this - for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++) - continue; - /* Skip quoted or unquoted // or /~ */ -//BUG: bash doesn't do this - while ((char)int_buf[i] == '/' - && ((char)int_buf[i+1] == '/' || (char)int_buf[i+1] == '~') - ) { - i++; - } - /* set only match and destroy quotes */ - { - int pos = 0; - for (j = 0; pos_buf[i] >= 0; i++) { - matchBuf[j++] = matchBuf[pos_buf[i]]; - pos = pos_buf[i] + 1; - } - matchBuf[j] = '\0'; - } + /* Store only match prefix */ + i = 0; + while ((matchBuf[i] = int_buf[i]) != '\0') + i++; + if (dbg_bmp) printf("final matchBuf:'%s'\n", matchBuf); return command_mode; } #undef int_buf -#undef pos_buf /* * Display by column (original idea from ls applet, -- cgit v1.2.3-55-g6feb From 9b56bf541667ce1b4d408cd4e23bc51a44ae9267 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Sep 2010 13:02:47 +0200 Subject: lineedit: stop using permanent int_buf[] (16k!): allocate it Now it is allocated temporarily only for the duretion of prefix generation, and also we only allocate the needed size, not maximally possible. function old new delta build_match_prefix 579 590 +11 remove_chunk 43 28 -15 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 11/-15) Total: -4 bytes Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index fff5c1a89..e212a1aa8 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -154,7 +154,6 @@ struct lineedit_statics { /* Formerly these were big buffers on stack: */ #if ENABLE_FEATURE_TAB_COMPLETION char input_tab__matchBuf[MAX_LINELEN]; - int16_t find_match__int_buf[MAX_LINELEN + 1]; /* need to have 9 bits at least */ #endif }; @@ -821,9 +820,8 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) * not Unicode chars. Therefore it works correctly even in Unicode mode. */ #define QUOT (UCHAR_MAX+1) -#define int_buf (S.find_match__int_buf) #define dbg_bmp 0 -static void remove_chunk(int beg, int end) +static void remove_chunk(int16_t *int_buf, int beg, int end) { /* beg must be <= end */ if (beg == end) @@ -843,11 +841,11 @@ static NOINLINE int build_match_prefix(char *matchBuf) { int i, j; int command_mode; -/* Were local, but it used too much stack */ -/* int16_t int_buf[MAX_LINELEN + 1]; */ + int16_t *int_buf; if (dbg_bmp) printf("\n%s\n", matchBuf); + int_buf = xmalloc(sizeof(int_buf[0]) * (strlen(matchBuf) + 1)); i = 0; while ((int_buf[i] = (unsigned char)matchBuf[i]) != '\0') i++; @@ -855,7 +853,7 @@ static NOINLINE int build_match_prefix(char *matchBuf) /* Mark every \c as "quoted c" */ for (i = j = 0; matchBuf[i]; i++, j++) { if (matchBuf[i] == '\\') { - remove_chunk(j, j + 1); + remove_chunk(int_buf, j, j + 1); int_buf[j] |= QUOT; i++; } @@ -871,7 +869,7 @@ static NOINLINE int build_match_prefix(char *matchBuf) if (cur == '\'' || cur == '"') { if (!in_quote || (cur == in_quote)) { in_quote ^= cur; - remove_chunk(i, i + 1); + remove_chunk(int_buf, i, i + 1); continue; } } @@ -894,7 +892,7 @@ static NOINLINE int build_match_prefix(char *matchBuf) } else if (cur == '|' && prev == '>') { continue; } - remove_chunk(0, i + 1 + (cur == int_buf[i + 1])); + remove_chunk(int_buf, 0, i + 1 + (cur == int_buf[i + 1])); i = -1; /* back to square 1 */ } } @@ -908,12 +906,12 @@ static NOINLINE int build_match_prefix(char *matchBuf) * not commands c*. Therefore we don't drop * `cmd` entirely, we replace it with single `. */ - remove_chunk(i, j); + remove_chunk(int_buf, i, j); goto next; } } /* No closing ` - command mode, remove all up to ` */ - remove_chunk(0, i + 1); + remove_chunk(int_buf, 0, i + 1); break; next: ; } @@ -925,7 +923,7 @@ static NOINLINE int build_match_prefix(char *matchBuf) */ for (i = 0; int_buf[i]; i++) { if (int_buf[i] == '(' || int_buf[i] == '{') { - remove_chunk(0, i + 1); + remove_chunk(int_buf, 0, i + 1); i = -1; /* hack increment */ } } @@ -934,7 +932,7 @@ static NOINLINE int build_match_prefix(char *matchBuf) for (i = 0; int_buf[i]; i++) if (int_buf[i] != ' ') break; - remove_chunk(0, i); + remove_chunk(int_buf, 0, i); /* Determine completion mode */ command_mode = FIND_EXE_ONLY; @@ -961,7 +959,7 @@ static NOINLINE int build_match_prefix(char *matchBuf) for (--i; i >= 0; i--) { int cur = int_buf[i]; if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') { - remove_chunk(0, i + 1); + remove_chunk(int_buf, 0, i + 1); break; } } @@ -970,11 +968,12 @@ static NOINLINE int build_match_prefix(char *matchBuf) i = 0; while ((matchBuf[i] = int_buf[i]) != '\0') i++; + free(int_buf); + if (dbg_bmp) printf("final matchBuf:'%s'\n", matchBuf); return command_mode; } -#undef int_buf /* * Display by column (original idea from ls applet, -- cgit v1.2.3-55-g6feb From a46e16ef52952b1ed82ab1dbc4b9da2aeb40107e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Sep 2010 13:05:51 +0200 Subject: lineedit: rename tmp -> chosen_match; small code shrink function old new delta input_tab 1016 1012 -4 Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 49 +++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index e212a1aa8..dda702455 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -1040,7 +1040,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) return; if (!*lastWasTab) { - char *tmp; + char *chosen_match; size_t len_found; /* char matchBuf[MAX_LINELEN]; */ #define matchBuf (S.input_tab__matchBuf) @@ -1067,8 +1067,6 @@ static NOINLINE void input_tab(smallint *lastWasTab) cursor_mb = strlen(matchBuf); } #endif - tmp = matchBuf; - find_type = build_match_prefix(matchBuf); /* Free up any memory already allocated */ @@ -1104,38 +1102,39 @@ static NOINLINE void input_tab(smallint *lastWasTab) } /* Did we find exactly one match? */ if (num_matches != 1) { /* no */ - char *tmp1; + char *cp; beep(); if (!matches) return; /* no matches at all */ /* Find common prefix */ - tmp1 = xstrdup(matches[0]); - for (tmp = tmp1; *tmp; tmp++) { + chosen_match = xstrdup(matches[0]); + for (cp = chosen_match; *cp; cp++) { unsigned n; for (n = 1; n < num_matches; n++) { - if (matches[n][tmp - tmp1] != *tmp) { + if (matches[n][cp - chosen_match] != *cp) { goto stop; } } } stop: - if (tmp1 == tmp) { /* have unique prefix? */ - free(tmp1); /* no */ + if (cp == chosen_match) { /* have unique prefix? */ + free(chosen_match); /* no */ return; } - *tmp = '\0'; - tmp = add_quote_for_spec_chars(tmp1); - free(tmp1); - len_found = strlen(tmp); + *cp = '\0'; + cp = add_quote_for_spec_chars(chosen_match); + free(chosen_match); + chosen_match = cp; + len_found = strlen(chosen_match); } else { /* exactly one match */ /* Next is not a double-tab */ *lastWasTab = 0; - tmp = add_quote_for_spec_chars(matches[0]); - len_found = strlen(tmp); - if (tmp[len_found-1] != '/') { - tmp[len_found] = ' '; - tmp[++len_found] = '\0'; + chosen_match = add_quote_for_spec_chars(matches[0]); + len_found = strlen(chosen_match); + if (chosen_match[len_found-1] != '/') { + chosen_match[len_found] = ' '; + chosen_match[++len_found] = '\0'; } } @@ -1149,7 +1148,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) /* save tail */ strcpy(matchBuf, &command_ps[cursor]); /* add match and tail */ - sprintf(&command_ps[cursor], "%s%s", tmp + match_pfx_len, matchBuf); + sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, matchBuf); command_len = strlen(command_ps); /* new pos */ pos = cursor + len_found - match_pfx_len; @@ -1167,10 +1166,10 @@ static NOINLINE void input_tab(smallint *lastWasTab) /* save tail */ strcpy(matchBuf, &command[cursor_mb]); /* where do we want to have cursor after all? */ - strcpy(&command[cursor_mb], tmp + match_pfx_len); + strcpy(&command[cursor_mb], chosen_match + match_pfx_len); len = load_string(command, S.maxsize); /* add match and tail */ - sprintf(&command[cursor_mb], "%s%s", tmp + match_pfx_len, matchBuf); + sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, matchBuf); command_len = load_string(command, S.maxsize); /* write out the matched command */ /* paranoia: load_string can return 0 on conv error, @@ -1180,17 +1179,15 @@ static NOINLINE void input_tab(smallint *lastWasTab) } } #endif - free(tmp); + free(chosen_match); #undef matchBuf } 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) { - /* changed by goto_new_line() */ + if (num_matches > 0) { + /* cursor will be changed by goto_new_line() */ int sav_cursor = cursor; - - /* Go to the next line */ goto_new_line(); showfiles(); redraw(0, command_len - sav_cursor); -- cgit v1.2.3-55-g6feb From ba0e103a662f640060041c5b56c2afdce09408b3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Sep 2010 14:08:24 +0200 Subject: lineedit: de-indent large block in input_tab. No logic changes. Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 262 +++++++++++++++++++++++++++---------------------------- 1 file changed, 131 insertions(+), 131 deletions(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index dda702455..ff5d28408 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -924,7 +924,7 @@ static NOINLINE int build_match_prefix(char *matchBuf) for (i = 0; int_buf[i]; i++) { if (int_buf[i] == '(' || int_buf[i] == '{') { remove_chunk(int_buf, 0, i + 1); - i = -1; /* hack increment */ + i = -1; /* back to square 1 */ } } @@ -1036,163 +1036,163 @@ static char *add_quote_for_spec_chars(char *found) /* Do TAB completion */ static NOINLINE void input_tab(smallint *lastWasTab) { - if (!(state->flags & TAB_COMPLETION)) - return; - - if (!*lastWasTab) { - char *chosen_match; - size_t len_found; -/* char matchBuf[MAX_LINELEN]; */ + char *chosen_match; + size_t len_found; +/* char matchBuf[MAX_LINELEN]; */ #define matchBuf (S.input_tab__matchBuf) - /* Length of string used for matching */ - unsigned match_pfx_len = match_pfx_len; - int find_type; + /* Length of string used for matching */ + unsigned match_pfx_len = match_pfx_len; + int find_type; #if ENABLE_UNICODE_SUPPORT - /* cursor pos in command converted to multibyte form */ - int cursor_mb; + /* cursor pos in command converted to multibyte form */ + int cursor_mb; #endif + if (!(state->flags & TAB_COMPLETION)) + return; - *lastWasTab = 1; + if (*lastWasTab) { + /* The last char was a TAB too. + * Print a list of all the available choices. + */ + if (num_matches > 0) { + /* cursor will be changed by goto_new_line() */ + int sav_cursor = cursor; + goto_new_line(); + showfiles(); + redraw(0, command_len - sav_cursor); + } + return; + } + + *lastWasTab = 1; - /* Make a local copy of the string -- - * up to the position of the cursor */ + /* Make a local copy of the string -- + * up to the position of the cursor */ #if !ENABLE_UNICODE_SUPPORT - save_string(matchBuf, cursor + 1); + save_string(matchBuf, cursor + 1); #else - { - CHAR_T wc = command_ps[cursor]; - command_ps[cursor] = BB_NUL; - save_string(matchBuf, MAX_LINELEN); - command_ps[cursor] = wc; - cursor_mb = strlen(matchBuf); - } + { + CHAR_T wc = command_ps[cursor]; + command_ps[cursor] = BB_NUL; + save_string(matchBuf, MAX_LINELEN); + command_ps[cursor] = wc; + cursor_mb = strlen(matchBuf); + } #endif - find_type = build_match_prefix(matchBuf); + find_type = build_match_prefix(matchBuf); - /* Free up any memory already allocated */ - free_tab_completion_data(); + /* Free up any memory already allocated */ + free_tab_completion_data(); #if ENABLE_FEATURE_USERNAME_COMPLETION - /* If the word starts with `~' and there is no slash in the word, - * then try completing this word as a username. */ - if (state->flags & USERNAME_COMPLETION) - if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) - match_pfx_len = complete_username(matchBuf); + /* If the word starts with `~' and there is no slash in the word, + * then try completing this word as a username. */ + if (state->flags & USERNAME_COMPLETION) + if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) + match_pfx_len = complete_username(matchBuf); #endif - /* Try to match a command in $PATH, or a directory, or a file */ - if (!matches) - match_pfx_len = complete_cmd_dir_file(matchBuf, find_type); - /* Remove duplicates */ - if (matches) { - unsigned i; - unsigned n = 0; - qsort_string_vector(matches, num_matches); - for (i = 0; i < num_matches - 1; ++i) { - //if (matches[i] && matches[i+1]) { /* paranoia */ - if (strcmp(matches[i], matches[i+1]) == 0) { - free(matches[i]); - //matches[i] = NULL; /* paranoia */ - } else { - matches[n++] = matches[i]; - } - //} - } - matches[n++] = matches[i]; - num_matches = n; + /* Try to match a command in $PATH, or a directory, or a file */ + if (!matches) + match_pfx_len = complete_cmd_dir_file(matchBuf, find_type); + /* Remove duplicates */ + if (matches) { + unsigned i; + unsigned n = 0; + qsort_string_vector(matches, num_matches); + for (i = 0; i < num_matches - 1; ++i) { + //if (matches[i] && matches[i+1]) { /* paranoia */ + if (strcmp(matches[i], matches[i+1]) == 0) { + free(matches[i]); + //matches[i] = NULL; /* paranoia */ + } else { + matches[n++] = matches[i]; + } + //} } - /* Did we find exactly one match? */ - if (num_matches != 1) { /* no */ - char *cp; - beep(); - if (!matches) - return; /* no matches at all */ - /* Find common prefix */ - chosen_match = xstrdup(matches[0]); - for (cp = chosen_match; *cp; cp++) { - unsigned n; - for (n = 1; n < num_matches; n++) { - if (matches[n][cp - chosen_match] != *cp) { - goto stop; - } + matches[n++] = matches[i]; + num_matches = n; + } + /* Did we find exactly one match? */ + if (num_matches != 1) { /* no */ + char *cp; + beep(); + if (!matches) + return; /* no matches at all */ + /* Find common prefix */ + chosen_match = xstrdup(matches[0]); + for (cp = chosen_match; *cp; cp++) { + unsigned n; + for (n = 1; n < num_matches; n++) { + if (matches[n][cp - chosen_match] != *cp) { + goto stop; } } + } stop: - if (cp == chosen_match) { /* have unique prefix? */ - free(chosen_match); /* no */ - return; - } - *cp = '\0'; - cp = add_quote_for_spec_chars(chosen_match); - free(chosen_match); - chosen_match = cp; - len_found = strlen(chosen_match); - } else { /* exactly one match */ - /* Next is not a double-tab */ - *lastWasTab = 0; - - chosen_match = add_quote_for_spec_chars(matches[0]); - len_found = strlen(chosen_match); - if (chosen_match[len_found-1] != '/') { - chosen_match[len_found] = ' '; - chosen_match[++len_found] = '\0'; - } + if (cp == chosen_match) { /* have unique prefix? */ + free(chosen_match); /* no */ + return; } + *cp = '\0'; + cp = add_quote_for_spec_chars(chosen_match); + free(chosen_match); + chosen_match = cp; + len_found = strlen(chosen_match); + } else { /* exactly one match */ + /* Next is not a double-tab */ + *lastWasTab = 0; + + chosen_match = add_quote_for_spec_chars(matches[0]); + len_found = strlen(chosen_match); + if (chosen_match[len_found-1] != '/') { + chosen_match[len_found] = ' '; + chosen_match[++len_found] = '\0'; + } + } #if !ENABLE_UNICODE_SUPPORT + /* Have space to place the match? */ + /* The result consists of three parts with these lengths: */ + /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */ + /* it simplifies into: */ + if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) { + int pos; + /* save tail */ + strcpy(matchBuf, &command_ps[cursor]); + /* add match and tail */ + sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, matchBuf); + command_len = strlen(command_ps); + /* new pos */ + pos = cursor + len_found - match_pfx_len; + /* write out the matched command */ + redraw(cmdedit_y, command_len - pos); + } +#else + { + char command[MAX_LINELEN]; + int len = save_string(command, sizeof(command)); /* Have space to place the match? */ - /* The result consists of three parts with these lengths: */ - /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */ - /* it simplifies into: */ - if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) { + /* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */ + if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) { int pos; /* save tail */ - strcpy(matchBuf, &command_ps[cursor]); + strcpy(matchBuf, &command[cursor_mb]); + /* where do we want to have cursor after all? */ + strcpy(&command[cursor_mb], chosen_match + match_pfx_len); + len = load_string(command, S.maxsize); /* add match and tail */ - sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, matchBuf); - command_len = strlen(command_ps); - /* new pos */ - pos = cursor + len_found - match_pfx_len; + sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, matchBuf); + command_len = load_string(command, S.maxsize); /* write out the matched command */ - redraw(cmdedit_y, command_len - pos); - } -#else - { - char command[MAX_LINELEN]; - int len = save_string(command, sizeof(command)); - /* Have space to place the match? */ - /* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */ - if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) { - int pos; - /* save tail */ - strcpy(matchBuf, &command[cursor_mb]); - /* where do we want to have cursor after all? */ - strcpy(&command[cursor_mb], chosen_match + match_pfx_len); - len = load_string(command, S.maxsize); - /* add match and tail */ - sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, matchBuf); - command_len = load_string(command, S.maxsize); - /* write out the matched command */ - /* paranoia: load_string can return 0 on conv error, - * prevent passing pos = (0 - 12) to redraw */ - pos = command_len - len; - redraw(cmdedit_y, pos >= 0 ? pos : 0); - } + /* paranoia: load_string can return 0 on conv error, + * prevent passing pos = (0 - 12) to redraw */ + pos = command_len - len; + redraw(cmdedit_y, pos >= 0 ? pos : 0); } + } #endif - free(chosen_match); + free(chosen_match); #undef matchBuf - } else { - /* Ok -- the last char was a TAB. Since they - * just hit TAB again, print a list of all the - * available choices... */ - if (num_matches > 0) { - /* cursor will be changed by goto_new_line() */ - int sav_cursor = cursor; - goto_new_line(); - showfiles(); - redraw(0, command_len - sav_cursor); - } - } } #endif /* FEATURE_TAB_COMPLETION */ -- cgit v1.2.3-55-g6feb From 76939e7b72937a5a2d00be3f62a6d2fa2757bdc9 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Sep 2010 14:09:24 +0200 Subject: lineedit: allocate matchBuf only temporarily: saves MAX_LINELEN bytes function old new delta input_tab 1027 1041 +14 build_match_prefix 590 562 -28 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 14/-28) Total: -14 bytes Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 93 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 45 deletions(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index ff5d28408..a917c5f92 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -150,11 +150,6 @@ struct lineedit_statics { #if ENABLE_FEATURE_EDITING_ASK_TERMINAL smallint sent_ESC_br6n; #endif - - /* Formerly these were big buffers on stack: */ -#if ENABLE_FEATURE_TAB_COMPLETION - char input_tab__matchBuf[MAX_LINELEN]; -#endif }; /* See lineedit_ptr_hack.c */ @@ -810,17 +805,17 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) } /* build_match_prefix: - * On entry, matchBuf contains everything up to cursor at the moment + * On entry, match_buf contains everything up to cursor at the moment * was pressed. This function looks at it, figures out what part of it * constitutes the command/file/directory prefix to use for completion, - * and rewrites matchBuf to contain only that part. + * and rewrites match_buf to contain only that part. */ +#define dbg_bmp 0 /* Helpers: */ /* QUOT is used on elements of int_buf[], which are bytes, * not Unicode chars. Therefore it works correctly even in Unicode mode. */ #define QUOT (UCHAR_MAX+1) -#define dbg_bmp 0 static void remove_chunk(int16_t *int_buf, int beg, int end) { /* beg must be <= end */ @@ -837,25 +832,29 @@ static void remove_chunk(int16_t *int_buf, int beg, int end) bb_putchar('\n'); } } -static NOINLINE int build_match_prefix(char *matchBuf) +/* Caller ensures that match_buf points to a malloced buffer + * big enough to hold strlen(match_buf)*2 + 2 + */ +static NOINLINE int build_match_prefix(char *match_buf) { int i, j; int command_mode; - int16_t *int_buf; + int16_t *int_buf = (int16_t*)match_buf; - if (dbg_bmp) printf("\n%s\n", matchBuf); + if (dbg_bmp) printf("\n%s\n", match_buf); - int_buf = xmalloc(sizeof(int_buf[0]) * (strlen(matchBuf) + 1)); - i = 0; - while ((int_buf[i] = (unsigned char)matchBuf[i]) != '\0') - i++; + /* Copy in reverse order, since they overlap */ + i = strlen(match_buf); + do { + int_buf[i] = (unsigned char)match_buf[i]; + i--; + } while (i >= 0); /* Mark every \c as "quoted c" */ - for (i = j = 0; matchBuf[i]; i++, j++) { - if (matchBuf[i] == '\\') { - remove_chunk(int_buf, j, j + 1); - int_buf[j] |= QUOT; - i++; + for (i = 0; int_buf[i]; i++) { + if (int_buf[i] == '\\') { + remove_chunk(int_buf, i, i + 1); + int_buf[i] |= QUOT; } } /* Quote-mark "chars" and 'chars', drop delimiters */ @@ -964,13 +963,12 @@ static NOINLINE int build_match_prefix(char *matchBuf) } } - /* Store only match prefix */ + /* Convert back to string of _chars_ */ i = 0; - while ((matchBuf[i] = int_buf[i]) != '\0') + while ((match_buf[i] = int_buf[i]) != '\0') i++; - free(int_buf); - if (dbg_bmp) printf("final matchBuf:'%s'\n", matchBuf); + if (dbg_bmp) printf("final match_buf:'%s'\n", match_buf); return command_mode; } @@ -1037,9 +1035,8 @@ static char *add_quote_for_spec_chars(char *found) static NOINLINE void input_tab(smallint *lastWasTab) { char *chosen_match; + char *match_buf; size_t len_found; -/* char matchBuf[MAX_LINELEN]; */ -#define matchBuf (S.input_tab__matchBuf) /* Length of string used for matching */ unsigned match_pfx_len = match_pfx_len; int find_type; @@ -1065,21 +1062,26 @@ static NOINLINE void input_tab(smallint *lastWasTab) } *lastWasTab = 1; + chosen_match = NULL; - /* Make a local copy of the string -- - * up to the position of the cursor */ + /* Make a local copy of the string up to the position of the cursor. + * build_match_prefix will expand it into int16_t's, need to allocate + * twice as much as the string_len+1. + * (we then also (ab)use this extra space later - see (**)) + */ + match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t)); #if !ENABLE_UNICODE_SUPPORT - save_string(matchBuf, cursor + 1); + save_string(match_buf, cursor + 1); /* +1 for NUL */ #else { CHAR_T wc = command_ps[cursor]; command_ps[cursor] = BB_NUL; - save_string(matchBuf, MAX_LINELEN); + save_string(match_buf, MAX_LINELEN); command_ps[cursor] = wc; - cursor_mb = strlen(matchBuf); + cursor_mb = strlen(match_buf); } #endif - find_type = build_match_prefix(matchBuf); + find_type = build_match_prefix(match_buf); /* Free up any memory already allocated */ free_tab_completion_data(); @@ -1088,12 +1090,12 @@ static NOINLINE void input_tab(smallint *lastWasTab) /* If the word starts with `~' and there is no slash in the word, * then try completing this word as a username. */ if (state->flags & USERNAME_COMPLETION) - if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) - match_pfx_len = complete_username(matchBuf); + if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL) + match_pfx_len = complete_username(match_buf); #endif /* Try to match a command in $PATH, or a directory, or a file */ if (!matches) - match_pfx_len = complete_cmd_dir_file(matchBuf, find_type); + match_pfx_len = complete_cmd_dir_file(match_buf, find_type); /* Remove duplicates */ if (matches) { unsigned i; @@ -1117,7 +1119,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) char *cp; beep(); if (!matches) - return; /* no matches at all */ + goto ret; /* no matches at all */ /* Find common prefix */ chosen_match = xstrdup(matches[0]); for (cp = chosen_match; *cp; cp++) { @@ -1130,8 +1132,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) } stop: if (cp == chosen_match) { /* have unique prefix? */ - free(chosen_match); /* no */ - return; + goto ret; /* no */ } *cp = '\0'; cp = add_quote_for_spec_chars(chosen_match); @@ -1158,9 +1159,9 @@ static NOINLINE void input_tab(smallint *lastWasTab) if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) { int pos; /* save tail */ - strcpy(matchBuf, &command_ps[cursor]); + strcpy(match_buf, &command_ps[cursor]); /* add match and tail */ - sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, matchBuf); + sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf); command_len = strlen(command_ps); /* new pos */ pos = cursor + len_found - match_pfx_len; @@ -1169,19 +1170,20 @@ static NOINLINE void input_tab(smallint *lastWasTab) } #else { - char command[MAX_LINELEN]; - int len = save_string(command, sizeof(command)); + /* Use 2nd half of match_buf as scratch space - see (**) */ + char *command = match_buf + MAX_LINELEN; + int len = save_string(command, MAX_LINELEN); /* Have space to place the match? */ /* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */ if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) { int pos; /* save tail */ - strcpy(matchBuf, &command[cursor_mb]); + strcpy(match_buf, &command[cursor_mb]); /* where do we want to have cursor after all? */ strcpy(&command[cursor_mb], chosen_match + match_pfx_len); len = load_string(command, S.maxsize); /* add match and tail */ - sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, matchBuf); + sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf); command_len = load_string(command, S.maxsize); /* write out the matched command */ /* paranoia: load_string can return 0 on conv error, @@ -1191,8 +1193,9 @@ static NOINLINE void input_tab(smallint *lastWasTab) } } #endif + ret: free(chosen_match); -#undef matchBuf + free(match_buf); } #endif /* FEATURE_TAB_COMPLETION */ -- cgit v1.2.3-55-g6feb From 3926363214bfa783db08c931d867dbf4855d31ef Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Sep 2010 14:11:08 +0200 Subject: lineedit: on tab completion, show filenames obly in all cases (bash compat) function old new delta complete_cmd_dir_file 731 730 -1 Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index a917c5f92..d2b808adf 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -751,6 +751,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) continue; /* don't print an error */ while ((next = readdir(dir)) != NULL) { + unsigned len; const char *name_found = next->d_name; /* .../: bash 3.2.0 shows dotfiles, but not . and .. */ @@ -767,18 +768,15 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) if (stat(found, &st) && lstat(found, &st)) goto cont; /* hmm, remove in progress? */ - /* save only name if we scan PATH */ - if (paths[i] != dirbuf) - strcpy(found, name_found); + /* Save only name */ + len = strlen(name_found); + found = xrealloc(found, len + 2); /* +2: for slash and NUL */ + strcpy(found, name_found); if (S_ISDIR(st.st_mode)) { - unsigned len1 = strlen(found); - /* name is a directory */ - if (found[len1-1] != '/') { - found = xrealloc(found, len1 + 2); - found[len1] = '/'; - found[len1 + 1] = '\0'; - } + /* name is a directory, add slash */ + found[len] = '/'; + found[len + 1] = '\0'; } else { /* skip files if looking for dirs only (example: cd) */ if (type == FIND_DIR_ONLY) @@ -796,10 +794,8 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) if (paths != path1) { free(paths[0]); /* allocated memory is only in first member */ free(paths); - } else if (dirbuf) { - pf_len += strlen(dirbuf); - free(dirbuf); } + free(dirbuf); return pf_len; } -- cgit v1.2.3-55-g6feb From d55f599440c65c0808c6d14b339f6175995fcf7c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 7 Sep 2010 18:40:53 +0200 Subject: lineedit: do not hang on error, but return error indicator. Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'libbb') diff --git a/libbb/lineedit.c b/libbb/lineedit.c index d2b808adf..066b569f6 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -2169,7 +2169,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li ic = lineedit_read_key(read_key_buffer); if (errno) /* error */ - goto prepare_to_die; + goto return_error_indicator; if (ic == ic_raw) { /* "cc", "dd" */ input_backward(cursor); goto clear_to_eol; @@ -2233,7 +2233,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li //FIXME: unicode case? ic = lineedit_read_key(read_key_buffer); if (errno) /* error */ - goto prepare_to_die; + goto return_error_indicator; if (ic < ' ' || ic > 255) { beep(); } else { @@ -2305,9 +2305,9 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li * or exit if len=0 and no chars to delete */ if (command_len == 0) { errno = 0; -#if ENABLE_FEATURE_EDITING_VI - prepare_to_die: -#endif + + case -1: /* error (e.g. EIO when tty is destroyed) */ + IF_FEATURE_EDITING_VI(return_error_indicator:) break_out = command_len = -1; break; } @@ -2317,7 +2317,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li // /* Control-V -- force insert of next char */ // if (c == CTRL('V')) { // if (safe_read(STDIN_FILENO, &c, 1) < 1) -// goto prepare_to_die; +// goto return_error_indicator; // if (c == 0) { // beep(); // break; -- cgit v1.2.3-55-g6feb From 819cacfd4597038840454ee743380fd70f836271 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 12 Sep 2010 16:17:37 +0200 Subject: busybox: add "busybox --list[-full]" to help text Signed-off-by: Denys Vlasenko --- libbb/appletlib.c | 1 + 1 file changed, 1 insertion(+) (limited to 'libbb') diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 8daa23309..7326f4fa5 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -655,6 +655,7 @@ static int busybox_main(char **argv) "See source distribution for full notice.\n" "\n" "Usage: busybox [function] [arguments]...\n" + " or: busybox --list[-full]\n" " or: function [arguments]...\n" "\n" "\tBusyBox is a multi-call binary that combines many common Unix\n" -- cgit v1.2.3-55-g6feb From 99862cbfad9c36b4f8f4378c3a7a9f077c239f20 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 12 Sep 2010 17:34:13 +0200 Subject: hush: optional support for history saving function old new delta hush_main 945 995 +50 Signed-off-by: Denys Vlasenko --- libbb/Config.src | 4 ++-- shell/hush.c | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'libbb') diff --git a/libbb/Config.src b/libbb/Config.src index 9b01757c6..74dc9c549 100644 --- a/libbb/Config.src +++ b/libbb/Config.src @@ -78,9 +78,9 @@ config FEATURE_EDITING_HISTORY config FEATURE_EDITING_SAVEHISTORY bool "History saving" default y - depends on ASH && FEATURE_EDITING + depends on FEATURE_EDITING help - Enable history saving in ash shell. + Enable history saving in shells. config FEATURE_TAB_COMPLETION bool "Tab completion" diff --git a/shell/hush.c b/shell/hush.c index ce7467245..752efd0c8 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -146,6 +146,13 @@ //config: from stdin just like a shell script from a file. //config: No prompt, no PS1/PS2 magic shell variables. //config: +//config:config HUSH_SAVEHISTORY +//config: bool "Save command history to .hush_history" +//config: default y +//config: depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY +//config: help +//config: Enable history saving in hush. +//config: //config:config HUSH_JOB //config: bool "Job control" //config: default y @@ -7415,7 +7422,20 @@ int hush_main(int argc, char **argv) #if ENABLE_FEATURE_EDITING G.line_input_state = new_line_input_t(FOR_SHELL); +# if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_HUSH_SAVEHISTORY + { + const char *hp = get_local_var_value("HISTFILE"); + if (!hp) { + hp = get_local_var_value("HOME"); + if (hp) { + G.line_input_state->hist_file = concat_path_file(hp, ".hush_history"); + //set_local_var(xasprintf("HISTFILE=%s", ...)); + } + } + } +# endif #endif + G.global_argc = argc; G.global_argv = argv; /* Initialize some more globals to non-zero values */ -- cgit v1.2.3-55-g6feb