📄 cmdedit.c
字号:
}#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETIONstatic int is_execute(const struct stat *st){ if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) || (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) || (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) || (st->st_mode & S_IXOTH)) return TRUE; return FALSE;}#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETIONstatic char **username_tab_completion(char *ud, int *num_matches){ struct passwd *entry; int userlen; char *temp; ud++; /* ~user/... to user/... */ userlen = strlen(ud); if (num_matches == 0) { /* "~/..." or "~user/..." */ char *sav_ud = ud - 1; char *home = 0; if (*ud == '/') { /* "~/..." */ home = home_pwd_buf; } else { /* "~user/..." */ 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) < BUFSIZ) { 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)) { bb_xasprintf(&temp, "~%s/", entry->pw_name); matches = xrealloc(matches, (nm + 1) * sizeof(char *)); matches[nm++] = temp; } } endpwent(); (*num_matches) = nm; return (matches); }}#endif /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */enum { FIND_EXE_ONLY = 0, FIND_DIR_ONLY = 1, FIND_FILE_ONLY = 2,};#ifdef CONFIG_ASHconst char *cmdedit_path_lookup;#else#define cmdedit_path_lookup getenv("PATH")#endifstatic int path_parse(char ***p, int flags){ int npth; const char *tmp; const char *pth; /* if not setenv PATH variable, to search cur dir "." */ if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 || /* PATH=<empty> or PATH=:<empty> */ *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) { return 1; } tmp = pth; npth = 0; for (;;) { npth++; /* count words is + 1 count ':' */ tmp = strchr(tmp, ':'); if (tmp) { if (*++tmp == 0) break; /* :<empty> */ } else break; } *p = xmalloc(npth * sizeof(char *)); tmp = pth; (*p)[0] = bb_xstrdup(tmp); npth = 1; /* count words is + 1 count ':' */ for (;;) { tmp = strchr(tmp, ':'); if (tmp) { (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */ if (*++tmp == 0) break; /* :<empty> */ } else break; (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */ } return npth;}static char *add_quote_for_spec_chars(char *found){ int l = 0; char *s = xmalloc((strlen(found) + 1) * 2); while (*found) { if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found)) s[l++] = '\\'; s[l++] = *found++; } s[l] = 0; return s;}static char **exe_n_cwd_tab_completion(char *command, int *num_matches, 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; int npaths; int i; char *found; char *pfind = strrchr(command, '/'); path1[0] = "."; if (pfind == NULL) { /* no dir, if flags==EXE_ONLY - get paths, else "." */ npaths = path_parse(&paths, type); pfind = command; } else { /* with dir */ /* save for change */ strcpy(dirbuf, command); /* set dir only */ dirbuf[(pfind - command) + 1] = 0;#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION if (dirbuf[0] == '~') /* ~/... or ~user/... */ username_tab_completion(dirbuf, 0);#endif /* "strip" dirname in command */ pfind++; paths[0] = dirbuf; npaths = 1; /* only 1 dir */ } for (i = 0; i < npaths; i++) { dir = opendir(paths[i]); if (!dir) /* Don't print an error */ continue; while ((next = readdir(dir)) != NULL) { char *str_found = next->d_name; /* matched ? */ if (strncmp(str_found, pfind, strlen(pfind))) continue; /* not see .name without .match */ if (*str_found == '.' && *pfind == 0) { if (*paths[i] == '/' && paths[i][1] == 0 && 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) goto cont; /* find with dirs ? */ if (paths[i] != dirbuf) 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); } else { /* not put found file if search only dirs for cd */ 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 it to the list */ matches = xrealloc(matches, (nm + 1) * sizeof(char *)); matches[nm++] = str_found;cont: free(found); } closedir(dir); } if (paths != path1) { 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)); }static int find_match(char *matchBuf, int *len_with_quotes){ int i, j; int command_mode; int c, c2; int int_buf[BUFSIZ + 1]; int pos_buf[BUFSIZ + 1]; /* set to integer dimension characters and own positions */ for (i = 0;; i++) { int_buf[i] = (int) ((unsigned char) matchBuf[i]); if (int_buf[i] == 0) { pos_buf[i] = -1; /* indicator end line */ break; } else pos_buf[i] = i; } /* mask \+symbol and convert '\t' to ' ' */ for (i = j = 0; matchBuf[i]; i++, j++) if (matchBuf[i] == '\\') { collapse_pos(j, j + 1); int_buf[j] |= QUOT; i++;#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT if (matchBuf[i] == '\t') /* algorithm equivalent */ int_buf[j] = ' ' | QUOT;#endif }#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT else if (matchBuf[i] == '\t') int_buf[j] = ' ';#endif /* mask "symbols" or 'symbols' */ 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; else int_buf[i] |= QUOT; } } else if (c2 != 0 && c != '$') int_buf[i] |= QUOT; } /* skip commands with arguments if line have commands delimiters */ /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */ for (i = 0; int_buf[i]; i++) { c = int_buf[i]; c2 = int_buf[i + 1]; j = i ? int_buf[i - 1] : -1; command_mode = 0; if (c == ';' || c == '&' || c == '|') { command_mode = 1 + (c == c2); if (c == '&') { if (j == '>' || j == '<') command_mode = 0; } else if (c == '|' && j == '>') command_mode = 0; } if (command_mode) { collapse_pos(0, i + command_mode); i = -1; /* hack incremet */ } } /* collapse `command...` */ 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; } if (j) { /* not found close ` - command mode, collapse all previous */ collapse_pos(0, i + 1); break; } else i--; /* hack incremet */ } /* collapse (command...(command...)...) or {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 incremet */ } 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 incremet */ } /* skip first not quote space */ for (i = 0; int_buf[i]; i++) if (int_buf[i] != ' ') break; if (i) collapse_pos(0, i); /* set find mode for completion */ 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 && matchBuf[pos_buf[0]]=='c' && matchBuf[pos_buf[1]]=='d' ) command_mode = FIND_DIR_ONLY; else { command_mode = FIND_FILE_ONLY; break; } } /* "strlen" */ for (i = 0; int_buf[i]; i++); /* find last word */ for (--i; i >= 0; i--) { c = int_buf[i]; if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') { collapse_pos(0, i + 1); break; } } /* 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) == '/' && ((int_buf[i + 1] & ~QUOT) == '/' || (int_buf[i + 1] & ~QUOT) == '~')) { 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; } matchBuf[c] = 0; /* old lenght matchBuf with quotes symbols */ *len_with_quotes = j ? j - pos_buf[0] : 0; return command_mode;}/* display by column original ideas from ls applet, very optimize by my :)*/static void showfiles(char **matches, int nfiles){ int ncols, row; int column_width = 0; int nrows = nfiles; /* find the longest file name- use that as the column width */ for (row = 0; row < nrows; row++) { int l = strlen(matches[row]); 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 */ column_width = -column_width; /* for printf("%-Ns", ...); */ } else { ncols = 1; } for (row = 0; row < nrows; row++) { int n = row; int nc; for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) printf("%*s", column_width, matches[n]); printf("%s\n", matches[n]); }}static void input_tab(int *lastWasTab){ /* Do TAB completion */ static int num_matches; static char **matches; if (lastWasTab == 0) { /* free all memory */ if (matches) { while (num_matches > 0) free(matches[--num_matches]); free(matches); matches = (char **) NULL; } return; } if (! *lastWasTab) { char *tmp; int len_found; char matchBuf[BUFSIZ]; int find_type; int recalc_pos; *lastWasTab = TRUE; /* flop trigger */ /* Make a local copy of the string -- up * to the position of the cursor */ tmp = strncpy(matchBuf, command_ps, cursor); tmp[cursor] = 0; find_type = find_match(matchBuf, &recalc_pos); /* Free up any memory already allocated */ input_tab(0);#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION /* If the word starts with `~' and there is no slash in the word, * then try completing this word as a username. */ if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0) matches = username_tab_completion(matchBuf, &num_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 */ if(matches) { int i, j; /* bubble */ for(i=0; i<(num_matches-1); i++) for(j=i+1; j<num_matches; j++) if(matches[i]!=0 && matches[j]!=0 && strcmp(matches[i], matches[j])==0) { free(matches[j]); matches[j]=0; } j=num_matches; num_matches = 0; for(i=0; i<j; i++) if(matches[i]) { if(!strcmp(matches[i], "./")) matches[i][1]=0; else if(!strcmp(matches[i], "../")) matches[i][2]=0; matches[num_matches++]=matches[i];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -