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(-) 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