sanitize cmdedit a bit (group functions by task etc)
No binary (build failed)
—
(log yok)
| @@ -28,19 +28,11 @@ | |
| - not true viewing if length prompt less terminal width | |
| */ | |
| - | #include "busybox.h" |
| #include <sys/ioctl.h> | |
| - | |
| + | #include "busybox.h" |
| #include "cmdedit.h" | |
| - | #if ENABLE_LOCALE_SUPPORT |
| - | #define Isprint(c) isprint(c) |
| - | #else |
| - | #define Isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233')) |
| - | #endif |
| - | |
| - | |
| /* FIXME: obsolete CONFIG item? */ | |
| #define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 | |
| @@ -56,38 +48,34 @@ | |
| #endif /* TEST */ | |
| + | /* Entire file (except TESTing part) sits inside this #if */ |
| #if ENABLE_FEATURE_COMMAND_EDITING | |
| + | |
| + | #if ENABLE_LOCALE_SUPPORT |
| + | #define Isprint(c) isprint(c) |
| + | #else |
| + | #define Isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233')) |
| + | #endif |
| + | |
| #define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \ | |
| (ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION || ENABLE_FEATURE_SH_FANCY_PROMPT) | |
| - | /* Maximum length of the linked list for the command line history */ |
| + | /* Maximum length of command line history */ |
| #if !ENABLE_FEATURE_COMMAND_HISTORY | |
| #define MAX_HISTORY 15 | |
| #else | |
| #define MAX_HISTORY (CONFIG_FEATURE_COMMAND_HISTORY + 0) | |
| #endif | |
| - | #if MAX_HISTORY > 0 |
| - | static char *history[MAX_HISTORY+1]; /* history + current */ |
| - | /* saved history lines */ |
| - | static int n_history; |
| - | /* current pointer to history line */ |
| - | static int cur_history; |
| - | #endif |
| - | |
| - | #define setTermSettings(fd,argp) tcsetattr(fd, TCSANOW, argp) |
| - | #define getTermSettings(fd,argp) tcgetattr(fd, argp); |
| - | /* Current termio and the previous termio before starting sh */ |
| + | /* Current termios and the previous termios before starting sh */ |
| static struct termios initial_settings, new_settings; | |
| - | |
| static | |
| - | volatile int cmdedit_termw = 80; /* actual terminal width */ |
| + | volatile unsigned cmdedit_termw = 80; /* actual terminal width */ |
| static | |
| - | volatile int handlers_sets = 0; /* Set next bites: */ |
| - | |
| + | volatile int handlers_sets = 0; /* Set next bits: */ |
| enum { | |
| SET_ATEXIT = 1, /* when atexit() has been called | |
| and get euid,uid,gid to fast compare */ | |
| @@ -98,84 +86,46 @@ enum { | |
| static int cmdedit_x; /* real x terminal position */ | |
| static int cmdedit_y; /* pseudoreal y terminal position */ | |
| - | static int cmdedit_prmt_len; /* lenght prompt without colores string */ |
| + | static int cmdedit_prmt_len; /* length of prompt without colores string */ |
| - | static int cursor; /* required globals for signal handler */ |
| - | static int len; /* --- "" - - "" -- -"- --""-- --""--- */ |
| - | static char *command_ps; /* --- "" - - "" -- -"- --""-- --""--- */ |
| - | static SKIP_FEATURE_SH_FANCY_PROMPT(const) char *cmdedit_prompt; /* -- */ |
| - | |
| - | #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
| - | static char *user_buf = ""; |
| - | static char *home_pwd_buf = ""; |
| - | static int my_euid; |
| - | #endif |
| + | static int cursor; |
| + | static int len; |
| + | static char *command_ps; |
| + | static SKIP_FEATURE_SH_FANCY_PROMPT(const) char *cmdedit_prompt; |
| #if ENABLE_FEATURE_SH_FANCY_PROMPT | |
| static char *hostname_buf; | |
| static int num_ok_lines = 1; | |
| #endif | |
| + | #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
| + | static char *user_buf = ""; |
| + | static char *home_pwd_buf = ""; |
| + | #endif |
| - | #if ENABLE_FEATURE_COMMAND_TAB_COMPLETION |
| - | |
| - | #if !ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
| + | #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR || ENABLE_FEATURE_COMMAND_TAB_COMPLETION |
| static int my_euid; | |
| #endif | |
| + | #if ENABLE_FEATURE_COMMAND_TAB_COMPLETION |
| static int my_uid; | |
| static int my_gid; | |
| + | #endif |
| - | #endif /* FEATURE_COMMAND_TAB_COMPLETION */ |
| - | |
| - | static void cmdedit_setwidth(int w, int redraw_flg); |
| - | |
| - | static void win_changed(int nsig) |
| - | { |
| - | static sighandler_t previous_SIGWINCH_handler; /* for reset */ |
| - | |
| - | /* emulate || signal call */ |
| - | if (nsig == -SIGWINCH || nsig == SIGWINCH) { |
| - | int width = 0; |
| - | get_terminal_width_height(0, &width, NULL); |
| - | cmdedit_setwidth(width, nsig == SIGWINCH); |
| - | } |
| - | /* Unix not all standard in recall signal */ |
| - | |
| - | if (nsig == -SIGWINCH) /* save previous handler */ |
| - | previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); |
| - | else if (nsig == SIGWINCH) /* signaled called handler */ |
| - | signal(SIGWINCH, win_changed); /* set for next call */ |
| - | else /* nsig == 0 */ |
| - | /* set previous handler */ |
| - | signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */ |
| - | } |
| - | |
| - | static void cmdedit_reset_term(void) |
| - | { |
| - | if (handlers_sets & SET_RESET_TERM) { |
| - | /* sparc and other have broken termios support: use old termio handling. */ |
| - | setTermSettings(STDIN_FILENO, (void *) &initial_settings); |
| - | handlers_sets &= ~SET_RESET_TERM; |
| - | } |
| - | if (handlers_sets & SET_WCHG_HANDLERS) { |
| - | /* reset SIGWINCH handler to previous (default) */ |
| - | win_changed(0); |
| - | handlers_sets &= ~SET_WCHG_HANDLERS; |
| - | } |
| - | fflush(stdout); |
| - | } |
| - | |
| - | |
| - | /* special for recount position for scroll and remove terminal margin effect */ |
| + | /* Put 'command_ps[cursor]', cursor++. |
| + | * Advance cursor on screen. If we reached right margin, scroll text up |
| + | * and remove terminal margin effect by printing 'next_char' */ |
| static void cmdedit_set_out_char(int next_char) | |
| { | |
| int c = (unsigned char)command_ps[cursor]; | |
| - | if (c == 0) |
| - | c = ' '; /* destroy end char? */ |
| + | if (c == '\0') { |
| + | /* erase character after end of input string */ |
| + | c = ' '; |
| + | } |
| #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT | |
| - | if (!Isprint(c)) { /* Inverse put non-printable characters */ |
| + | /* Display non-printable characters in reverse */ |
| + | if (!Isprint(c)) { |
| if (c >= 128) | |
| c -= 128; | |
| if (c < ' ') | |
| @@ -193,21 +143,19 @@ static void cmdedit_set_out_char(int next_char) | |
| /* terminal is scrolled down */ | |
| cmdedit_y++; | |
| cmdedit_x = 0; | |
| - | |
| - | if (!next_char) |
| - | next_char = ' '; |
| /* destroy "(auto)margin" */ | |
| putchar(next_char); | |
| putchar('\b'); | |
| } | |
| + | // Huh? What if command_ps[cursor] == '\0' (we are at the end already?) |
| cursor++; | |
| } | |
| - | /* Move to end line. Bonus: rewrite line from cursor */ |
| + | /* Move to end of line (by printing all chars till the end) */ |
| static void input_end(void) | |
| { | |
| while (cursor < len) | |
| - | cmdedit_set_out_char(0); |
| + | cmdedit_set_out_char(' '); |
| } | |
| /* Go to the next line */ | |
| @@ -238,26 +186,32 @@ static void input_backward(int num) | |
| num = cursor; | |
| cursor -= num; /* new cursor (in command, not terminal) */ | |
| - | if (cmdedit_x >= num) { /* no to up line */ |
| + | if (cmdedit_x >= num) { |
| cmdedit_x -= num; | |
| - | if (num < 4) |
| - | while (num-- > 0) |
| + | if (num <= 4) |
| + | while (num > 0) { |
| putchar('\b'); | |
| + | num--; |
| + | } |
| else | |
| printf("\033[%dD", num); | |
| } else { | |
| + | /* Need to go one or more lines up */ |
| int count_y; | |
| - | if (cmdedit_x) { |
| - | putchar('\r'); /* back to first terminal pos. */ |
| - | num -= cmdedit_x; /* set previous backward */ |
| - | } |
| + | //if (cmdedit_x) { |
| + | // /* back to first column */ |
| + | // putchar('\r'); |
| + | // num -= cmdedit_x; |
| + | //} |
| + | num -= cmdedit_x;// |
| count_y = 1 + num / cmdedit_termw; | |
| - | printf("\033[%dA", count_y); |
| + | //printf("\033[%dA", count_y); |
| cmdedit_y -= count_y; | |
| - | /* require forward after uping */ |
| cmdedit_x = cmdedit_termw * count_y - num; | |
| - | printf("\033[%dC", cmdedit_x); /* set term cursor */ |
| + | //printf("\033[%dC", cmdedit_x); |
| + | /* go to 1st col; go up; go to correct column */ |
| + | printf("\r" "\033[%dA" "\033[%dC", count_y, cmdedit_x);// |
| } | |
| } | |
| @@ -269,136 +223,6 @@ static void put_prompt(void) | |
| cmdedit_y = 0; /* new quasireal y */ | |
| } | |
| - | #if !ENABLE_FEATURE_SH_FANCY_PROMPT |
| - | static void parse_prompt(const char *prmt_ptr) |
| - | { |
| - | cmdedit_prompt = prmt_ptr; |
| - | cmdedit_prmt_len = strlen(prmt_ptr); |
| - | put_prompt(); |
| - | } |
| - | #else |
| - | static void parse_prompt(const char *prmt_ptr) |
| - | { |
| - | int prmt_len = 0; |
| - | size_t cur_prmt_len = 0; |
| - | char flg_not_length = '['; |
| - | char *prmt_mem_ptr = xzalloc(1); |
| - | char *pwd_buf = xgetcwd(0); |
| - | char buf2[PATH_MAX + 1]; |
| - | char buf[2]; |
| - | char c; |
| - | char *pbuf; |
| - | |
| - | if (!pwd_buf) { |
| - | pwd_buf = (char *)bb_msg_unknown; |
| - | } |
| - | |
| - | while (*prmt_ptr) { |
| - | pbuf = buf; |
| - | pbuf[1] = 0; |
| - | c = *prmt_ptr++; |
| - | if (c == '\\') { |
| - | const char *cp = prmt_ptr; |
| - | int l; |
| - | |
| - | c = bb_process_escape_sequence(&prmt_ptr); |
| - | if (prmt_ptr == cp) { |
| - | if (*cp == 0) |
| - | break; |
| - | c = *prmt_ptr++; |
| - | switch (c) { |
| - | #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
| - | case 'u': |
| - | pbuf = user_buf; |
| - | break; |
| - | #endif |
| - | case 'h': |
| - | pbuf = hostname_buf; |
| - | if (pbuf == 0) { |
| - | pbuf = xzalloc(256); |
| - | if (gethostname(pbuf, 255) < 0) { |
| - | strcpy(pbuf, "?"); |
| - | } else { |
| - | char *s = strchr(pbuf, '.'); |
| - | if (s) |
| - | *s = 0; |
| - | } |
| - | hostname_buf = pbuf; |
| - | } |
| - | break; |
| - | case '$': |
| - | c = (my_euid == 0 ? '#' : '$'); |
| - | break; |
| - | #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
| - | case 'w': |
| - | pbuf = pwd_buf; |
| - | l = strlen(home_pwd_buf); |
| - | if (home_pwd_buf[0] != 0 |
| - | && strncmp(home_pwd_buf, pbuf, l) == 0 |
| - | && (pbuf[l]=='/' || pbuf[l]=='\0') |
| - | && strlen(pwd_buf+l)<PATH_MAX |
| - | ) { |
| - | pbuf = buf2; |
| - | *pbuf = '~'; |
| - | strcpy(pbuf+1, pwd_buf+l); |
| - | } |
| - | break; |
| - | #endif |
| - | case 'W': |
| - | pbuf = pwd_buf; |
| - | cp = strrchr(pbuf,'/'); |
| - | if (cp != NULL && cp != pbuf) |
| - | pbuf += (cp-pbuf) + 1; |
| - | break; |
| - | case '!': |
| - | snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines); |
| - | break; |
| - | case 'e': case 'E': /* \e \E = \033 */ |
| - | c = '\033'; |
| - | break; |
| - | case 'x': case 'X': |
| - | for (l = 0; l < 3;) { |
| - | int h; |
| - | buf2[l++] = *prmt_ptr; |
| - | buf2[l] = 0; |
| - | h = strtol(buf2, &pbuf, 16); |
| - | if (h > UCHAR_MAX || (pbuf - buf2) < l) { |
| - | l--; |
| - | break; |
| - | } |
| - | prmt_ptr++; |
| - | } |
| - | buf2[l] = 0; |
| - | c = (char)strtol(buf2, 0, 16); |
| - | if (c == 0) |
| - | c = '?'; |
| - | pbuf = buf; |
| - | break; |
| - | case '[': case ']': |
| - | if (c == flg_not_length) { |
| - | flg_not_length = flg_not_length == '[' ? ']' : '['; |
| - | continue; |
| - | } |
| - | break; |
| - | } |
| - | } |
| - | } |
| - | if (pbuf == buf) |
| - | *pbuf = c; |
| - | cur_prmt_len = strlen(pbuf); |
| - | prmt_len += cur_prmt_len; |
| - | if (flg_not_length != ']') |
| - | cmdedit_prmt_len += cur_prmt_len; |
| - | prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf); |
| - | } |
| - | if (pwd_buf!=(char *)bb_msg_unknown) |
| - | free(pwd_buf); |
| - | cmdedit_prompt = prmt_mem_ptr; |
| - | put_prompt(); |
| - | } |
| - | #endif |
| - | |
| - | |
| /* draw prompt, editor line, and clear tail */ | |
| static void redraw(int y, int back_cursor) | |
| { | |
| @@ -407,7 +231,7 @@ static void redraw(int y, int back_cursor) | |
| putchar('\r'); | |
| put_prompt(); | |
| input_end(); /* rewrite */ | |
| - | printf("\033[J"); /* destroy tail after cursor */ |
| + | printf("\033[J"); /* erase after cursor */ |
| input_backward(back_cursor); | |
| } | |
| @@ -444,14 +268,16 @@ static void input_delete(int save) | |
| strcpy(command_ps + j, command_ps + j + 1); | |
| len--; | |
| input_end(); /* rewrite new line */ | |
| - | cmdedit_set_out_char(0); /* destroy end char */ |
| + | cmdedit_set_out_char(' '); /* erase char */ |
| input_backward(cursor - j); /* back to old pos cursor */ | |
| } | |
| #if ENABLE_FEATURE_COMMAND_EDITING_VI | |
| static void put(void) | |
| { | |
| - | int ocursor, j = delp - delbuf; |
| + | int ocursor; |
| + | int j = delp - delbuf; |
| + | |
| if (j == 0) | |
| return; | |
| ocursor = cursor; | |
| @@ -481,60 +307,6 @@ static void input_forward(void) | |
| cmdedit_set_out_char(command_ps[cursor + 1]); | |
| } | |
| - | static void cmdedit_setwidth(int w, int redraw_flg) |
| - | { |
| - | cmdedit_termw = cmdedit_prmt_len + 2; |
| - | if (w <= cmdedit_termw) { |
| - | cmdedit_termw = cmdedit_termw % w; |
| - | } |
| - | if (w > cmdedit_termw) { |
| - | cmdedit_termw = w; |
| - | |
| - | if (redraw_flg) { |
| - | /* new y for current cursor */ |
| - | int new_y = (cursor + cmdedit_prmt_len) / w; |
| - | |
| - | /* redraw */ |
| - | redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor); |
| - | fflush(stdout); |
| - | } |
| - | } |
| - | } |
| - | |
| - | static void cmdedit_init(void) |
| - | { |
| - | cmdedit_prmt_len = 0; |
| - | if (!(handlers_sets & SET_WCHG_HANDLERS)) { |
| - | /* emulate usage handler to set handler and call yours work */ |
| - | win_changed(-SIGWINCH); |
| - | handlers_sets |= SET_WCHG_HANDLERS; |
| - | } |
| - | |
| - | if (!(handlers_sets & SET_ATEXIT)) { |
| - | #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
| - | struct passwd *entry; |
| - | |
| - | my_euid = geteuid(); |
| - | entry = getpwuid(my_euid); |
| - | if (entry) { |
| - | user_buf = xstrdup(entry->pw_name); |
| - | home_pwd_buf = xstrdup(entry->pw_dir); |
| - | } |
| - | #endif |
| - | |
| - | #if ENABLE_FEATURE_COMMAND_TAB_COMPLETION |
| - | |
| - | #if !ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
| - | my_euid = geteuid(); |
| - | #endif |
| - | my_uid = getuid(); |
| - | my_gid = getgid(); |
| - | #endif /* FEATURE_COMMAND_TAB_COMPLETION */ |
| - | handlers_sets |= SET_ATEXIT; |
| - | atexit(cmdedit_reset_term); /* be sure to do this only once */ |
| - | } |
| - | } |
| - | |
| #if ENABLE_FEATURE_COMMAND_TAB_COMPLETION | |
| static char **matches; | |
| @@ -550,20 +322,6 @@ static void add_match(char *matched) | |
| num_matches++; | |
| } | |
| - | /* |
| - | static 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; |
| - | } |
| - | */ |
| - | |
| #if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION | |
| static void username_tab_completion(char *ud, char *with_shash_flg) | |
| @@ -624,15 +382,16 @@ enum { | |
| #if ENABLE_ASH | |
| const char *cmdedit_path_lookup; | |
| - | #else |
| - | #define cmdedit_path_lookup getenv("PATH") |
| #endif | |
| - | |
| static int path_parse(char ***p, int flags) | |
| { | |
| int npth; | |
| const char *tmp; | |
| + | #if ENABLE_ASH |
| const char *pth = cmdedit_path_lookup; | |
| + | #else |
| + | const char *pth = getenv("PATH") |
| + | #endif |
| /* if not setenv PATH variable, to search cur dir "." */ | |
| if (flags != FIND_EXE_ONLY) | |
| @@ -649,7 +408,7 @@ static int path_parse(char ***p, int flags) | |
| tmp = strchr(tmp, ':'); | |
| if (!tmp) | |
| break; | |
| - | if (*++tmp == 0) |
| + | if (*++tmp == '\0') |
| break; /* :<empty> */ | |
| } | |
| @@ -672,20 +431,6 @@ static int path_parse(char ***p, int flags) | |
| 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 void exe_n_cwd_tab_completion(char *command, int type) | |
| { | |
| DIR *dir; | |
| @@ -699,6 +444,7 @@ static void exe_n_cwd_tab_completion(char *command, int type) | |
| char *found; | |
| char *pfind = strrchr(command, '/'); | |
| + | npaths = 1; |
| path1[0] = "."; | |
| if (pfind == NULL) { | |
| @@ -706,20 +452,15 @@ static void exe_n_cwd_tab_completion(char *command, int type) | |
| npaths = path_parse(&paths, type); | |
| pfind = command; | |
| } else { | |
| - | /* with dir */ |
| - | /* save for change */ |
| - | strcpy(dirbuf, command); |
| - | /* set dir only */ |
| - | dirbuf[(pfind - command) + 1] = 0; |
| + | /* dirbuf = ".../.../.../" */ |
| + | safe_strncpy(dirbuf, command, (pfind - command) + 2); |
| #if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION | |
| if (dirbuf[0] == '~') /* ~/... or ~user/... */ | |
| username_tab_completion(dirbuf, dirbuf); | |
| #endif | |
| - | /* "strip" dirname in command */ |
| - | pfind++; |
| - | |
| paths[0] = dirbuf; | |
| - | npaths = 1; /* only 1 dir */ |
| + | /* point to 'l' in "..../last_component" */ |
| + | pfind++; |
| } | |
| for (i = 0; i < npaths; i++) { | |
| @@ -994,6 +735,20 @@ static int match_compare(const void *a, const void *b) | |
| return strcmp(*(char**)a, *(char**)b); | |
| } | |
| + | 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 void input_tab(int *lastWasTab) | |
| { | |
| /* Do TAB completion */ | |
| @@ -1121,7 +876,15 @@ static void input_tab(int *lastWasTab) | |
| } | |
| #endif /* FEATURE_COMMAND_TAB_COMPLETION */ | |
| + | |
| #if MAX_HISTORY > 0 | |
| + | |
| + | static char *history[MAX_HISTORY+1]; /* history + current */ |
| + | /* saved history lines */ |
| + | static int n_history; |
| + | /* current pointer to history line */ |
| + | static int cur_history; |
| + | |
| static void get_previous_history(void) | |
| { | |
| if (command_ps[0] != 0 || history[cur_history] == 0) { | |
| @@ -1193,14 +956,9 @@ void save_history (const char *tofile) | |
| fclose(fp); | |
| } | |
| } | |
| - | #endif |
| + | #endif /* FEATURE_COMMAND_SAVEHISTORY */ |
| - | #endif |
| - | |
| - | enum { |
| - | ESC = 27, |
| - | DEL = 127, |
| - | }; |
| + | #endif /* MAX_HISTORY > 0 */ |
| /* | |
| @@ -1217,14 +975,13 @@ enum { | |
| * CTL-t -- Transpose two characters | |
| * | |
| * Minimalist vi-style command line editing available if configured. | |
| - | * vi mode implemented 2005 by Paul Fox <[email protected]> |
| - | * |
| + | * vi mode implemented 2005 by Paul Fox <[email protected]> |
| */ | |
| #if ENABLE_FEATURE_COMMAND_EDITING_VI | |
| static int vi_mode; | |
| - | void setvimode ( int viflag ) |
| + | void setvimode(int viflag) |
| { | |
| vi_mode = viflag; | |
| } | |
| @@ -1320,6 +1077,225 @@ vi_back_motion(char *command) | |
| input_backward(1); | |
| } | |
| } | |
| + | #else |
| + | enum { vi_mode = 0 }; |
| + | #endif |
| + | |
| + | |
| + | /* |
| + | * cmdedit_read_input and its helpers |
| + | */ |
| + | |
| + | #define setTermSettings(fd, argp) tcsetattr(fd, TCSANOW, argp) |
| + | #define getTermSettings(fd, argp) tcgetattr(fd, argp); |
| + | |
| + | static sighandler_t previous_SIGWINCH_handler; |
| + | |
| + | static void cmdedit_reset_term(void) |
| + | { |
| + | if (handlers_sets & SET_RESET_TERM) { |
| + | setTermSettings(STDIN_FILENO, (void *) &initial_settings); |
| + | handlers_sets &= ~SET_RESET_TERM; |
| + | } |
| + | if (handlers_sets & SET_WCHG_HANDLERS) { |
| + | /* restore SIGWINCH handler */ |
| + | signal(SIGWINCH, previous_SIGWINCH_handler); |
| + | handlers_sets &= ~SET_WCHG_HANDLERS; |
| + | } |
| + | fflush(stdout); |
| + | } |
| + | |
| + | static void cmdedit_setwidth(int w, int redraw_flg) |
| + | { |
| + | cmdedit_termw = cmdedit_prmt_len + 2; |
| + | if (w <= cmdedit_termw) { |
| + | cmdedit_termw = cmdedit_termw % w; |
| + | } |
| + | if (w > cmdedit_termw) { |
| + | cmdedit_termw = w; |
| + | |
| + | if (redraw_flg) { |
| + | /* new y for current cursor */ |
| + | int new_y = (cursor + cmdedit_prmt_len) / w; |
| + | |
| + | /* redraw */ |
| + | redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor); |
| + | fflush(stdout); |
| + | } |
| + | } |
| + | } |
| + | |
| + | static void win_changed(int nsig) |
| + | { |
| + | int width; |
| + | get_terminal_width_height(0, &width, NULL); |
| + | cmdedit_setwidth(width, nsig /* - just a yes/no flag */); |
| + | if (nsig == SIGWINCH) |
| + | signal(SIGWINCH, win_changed); /* rearm ourself */ |
| + | } |
| + | |
| + | static void cmdedit_init(void) |
| + | { |
| + | cmdedit_prmt_len = 0; |
| + | if (!(handlers_sets & SET_WCHG_HANDLERS)) { |
| + | previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); |
| + | win_changed(0); /* do initial resizing */ |
| + | handlers_sets |= SET_WCHG_HANDLERS; |
| + | } |
| + | |
| + | if (!(handlers_sets & SET_ATEXIT)) { |
| + | #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
| + | struct passwd *entry; |
| + | |
| + | my_euid = geteuid(); |
| + | entry = getpwuid(my_euid); |
| + | if (entry) { |
| + | user_buf = xstrdup(entry->pw_name); |
| + | home_pwd_buf = xstrdup(entry->pw_dir); |
| + | } |
| + | #endif |
| + | |
| + | #if ENABLE_FEATURE_COMMAND_TAB_COMPLETION |
| + | |
| + | #if !ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
| + | my_euid = geteuid(); |
| + | #endif |
| + | my_uid = getuid(); |
| + | my_gid = getgid(); |
| + | #endif /* FEATURE_COMMAND_TAB_COMPLETION */ |
| + | handlers_sets |= SET_ATEXIT; |
| + | // Crap. We should be able to do it without atexit. |
| + | atexit(cmdedit_reset_term); /* be sure to do this only once */ |
| + | } |
| + | } |
| + | |
| + | #if !ENABLE_FEATURE_SH_FANCY_PROMPT |
| + | static void parse_prompt(const char *prmt_ptr) |
| + | { |
| + | cmdedit_prompt = prmt_ptr; |
| + | cmdedit_prmt_len = strlen(prmt_ptr); |
| + | put_prompt(); |
| + | } |
| + | #else |
| + | static void parse_prompt(const char *prmt_ptr) |
| + | { |
| + | int prmt_len = 0; |
| + | size_t cur_prmt_len = 0; |
| + | char flg_not_length = '['; |
| + | char *prmt_mem_ptr = xzalloc(1); |
| + | char *pwd_buf = xgetcwd(0); |
| + | char buf2[PATH_MAX + 1]; |
| + | char buf[2]; |
| + | char c; |
| + | char *pbuf; |
| + | |
| + | if (!pwd_buf) { |
| + | pwd_buf = (char *)bb_msg_unknown; |
| + | } |
| + | |
| + | while (*prmt_ptr) { |
| + | pbuf = buf; |
| + | pbuf[1] = 0; |
| + | c = *prmt_ptr++; |
| + | if (c == '\\') { |
| + | const char *cp = prmt_ptr; |
| + | int l; |
| + | |
| + | c = bb_process_escape_sequence(&prmt_ptr); |
| + | if (prmt_ptr == cp) { |
| + | if (*cp == 0) |
| + | break; |
| + | c = *prmt_ptr++; |
| + | switch (c) { |
| + | #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
| + | case 'u': |
| + | pbuf = user_buf; |
| + | break; |
| + | #endif |
| + | case 'h': |
| + | pbuf = hostname_buf; |
| + | if (!pbuf) { |
| + | pbuf = xzalloc(256); |
| + | if (gethostname(pbuf, 255) < 0) { |
| + | strcpy(pbuf, "?"); |
| + | } else { |
| + | char *s = strchr(pbuf, '.'); |
| + | if (s) |
| + | *s = '\0'; |
| + | } |
| + | hostname_buf = pbuf; |
| + | } |
| + | break; |
| + | case '$': |
| + | c = (my_euid == 0 ? '#' : '$'); |
| + | break; |
| + | #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR |
| + | case 'w': |
| + | pbuf = pwd_buf; |
| + | l = strlen(home_pwd_buf); |
| + | if (home_pwd_buf[0] != 0 |
| + | && strncmp(home_pwd_buf, pbuf, l) == 0 |
| + | && (pbuf[l]=='/' || pbuf[l]=='\0') |
| + | && strlen(pwd_buf+l)<PATH_MAX |
| + | ) { |
| + | pbuf = buf2; |
| + | *pbuf = '~'; |
| + | strcpy(pbuf+1, pwd_buf+l); |
| + | } |
| + | break; |
| + | #endif |
| + | case 'W': |
| + | pbuf = pwd_buf; |
| + | cp = strrchr(pbuf,'/'); |
| + | if (cp != NULL && cp != pbuf) |
| + | pbuf += (cp-pbuf) + 1; |
| + | break; |
| + | case '!': |
| + | snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines); |
| + | break; |
| + | case 'e': case 'E': /* \e \E = \033 */ |
| + | c = '\033'; |
| + | break; |
| + | case 'x': case 'X': |
| + | for (l = 0; l < 3;) { |
| + | int h; |
| + | buf2[l++] = *prmt_ptr; |
| + | buf2[l] = 0; |
| + | h = strtol(buf2, &pbuf, 16); |
| + | if (h > UCHAR_MAX || (pbuf - buf2) < l) { |
| + | l--; |
| + | break; |
| + | } |
| + | prmt_ptr++; |
| + | } |
| + | buf2[l] = 0; |
| + | c = (char)strtol(buf2, NULL, 16); |
| + | if (c == 0) |
| + | c = '?'; |
| + | pbuf = buf; |
| + | break; |
| + | case '[': case ']': |
| + | if (c == flg_not_length) { |
| + | flg_not_length = flg_not_length == '[' ? ']' : '['; |
| + | continue; |
| + | } |
| + | break; |
| + | } |
| + | } |
| + | } |
| + | if (pbuf == buf) |
| + | *pbuf = c; |
| + | cur_prmt_len = strlen(pbuf); |
| + | prmt_len += cur_prmt_len; |
| + | if (flg_not_length != ']') |
| + | cmdedit_prmt_len += cur_prmt_len; |
| + | prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf); |
| + | } |
| + | if (pwd_buf!=(char *)bb_msg_unknown) |
| + | free(pwd_buf); |
| + | cmdedit_prompt = prmt_mem_ptr; |
| + | put_prompt(); |
| + | } |
| #endif | |
| /* | |
| @@ -1337,18 +1313,19 @@ vi_back_motion(char *command) | |
| #define vi_case(caselabel) USE_FEATURE_COMMAND_EDITING(caselabel) | |
| /* convert uppercase ascii to equivalent control char, for readability */ | |
| - | #define CNTRL(uc_char) ((uc_char) - 0x40) |
| + | #undef CTRL |
| + | #define CTRL(a) ((a) & ~0x40) |
| int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |
| { | |
| - | int break_out = 0; |
| int lastWasTab = FALSE; | |
| - | unsigned char c; |
| unsigned int ic; | |
| + | unsigned char c; |
| + | smallint break_out = 0; |
| #if ENABLE_FEATURE_COMMAND_EDITING_VI | |
| - | unsigned int prevc; |
| - | int vi_cmdmode = 0; |
| + | smallint vi_cmdmode = 0; |
| + | smalluint prevc; |
| #endif | |
| /* prepare before init handlers */ | |
| cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */ | |
| @@ -1401,20 +1378,20 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |
| goto_new_line(); | |
| break_out = 1; | |
| break; | |
| - | case CNTRL('A'): |
| + | case CTRL('A'): |
| vi_case( case '0'|vbit: ) | |
| /* Control-a -- Beginning of line */ | |
| input_backward(cursor); | |
| break; | |
| - | case CNTRL('B'): |
| + | case CTRL('B'): |
| vi_case( case 'h'|vbit: ) | |
| vi_case( case '\b'|vbit: ) | |
| - | vi_case( case DEL|vbit: ) |
| + | vi_case( case '\x7f'|vbit: ) /* DEL */ |
| /* Control-b -- Move back one character */ | |
| input_backward(1); | |
| break; | |
| - | case CNTRL('C'): |
| - | vi_case( case CNTRL('C')|vbit: ) |
| + | case CTRL('C'): |
| + | vi_case( case CTRL('C')|vbit: ) |
| /* Control-c -- stop gathering input */ | |
| goto_new_line(); | |
| #if !ENABLE_ASH | |
| @@ -1427,39 +1404,41 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |
| break_out = -1; /* to control traps */ | |
| #endif | |
| break; | |
| - | case CNTRL('D'): |
| + | case CTRL('D'): |
| /* Control-d -- Delete one character, or exit | |
| * if the len=0 and no chars to delete */ | |
| if (len == 0) { | |
| errno = 0; | |
| prepare_to_die: | |
| + | // So, our API depends on whether we have ash compiled in or not? Crap... |
| #if !ENABLE_ASH | |
| printf("exit"); | |
| goto_new_line(); | |
| /* cmdedit_reset_term() called in atexit */ | |
| + | // FIXME. this is definitely not good |
| exit(EXIT_SUCCESS); | |
| #else | |
| /* to control stopped jobs */ | |
| - | len = break_out = -1; |
| + | break_out = len = -1; |
| break; | |
| #endif | |
| } else { | |
| input_delete(0); | |
| } | |
| break; | |
| - | case CNTRL('E'): |
| + | case CTRL('E'): |
| vi_case( case '$'|vbit: ) | |
| /* Control-e -- End of line */ | |
| input_end(); | |
| break; | |
| - | case CNTRL('F'): |
| + | case CTRL('F'): |
| vi_case( case 'l'|vbit: ) | |
| vi_case( case ' '|vbit: ) | |
| /* Control-f -- Move forward one character */ | |
| input_forward(); | |
| break; | |
| case '\b': | |
| - | case DEL: |
| + | case '\x7f': /* DEL */ |
| /* Control-h and DEL */ | |
| input_backspace(); | |
| break; | |
| @@ -1468,28 +1447,28 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |
| input_tab(&lastWasTab); | |
| #endif | |
| break; | |
| - | case CNTRL('K'): |
| + | case CTRL('K'): |
| /* Control-k -- clear to end of line */ | |
| command[cursor] = 0; | |
| len = cursor; | |
| printf("\033[J"); | |
| break; | |
| - | case CNTRL('L'): |
| - | vi_case( case CNTRL('L')|vbit: ) |
| + | case CTRL('L'): |
| + | vi_case( case CTRL('L')|vbit: ) |
| /* Control-l -- clear screen */ | |
| printf("\033[H"); | |
| redraw(0, len - cursor); | |
| break; | |
| #if MAX_HISTORY > 0 | |
| - | case CNTRL('N'): |
| - | vi_case( case CNTRL('N')|vbit: ) |
| + | case CTRL('N'): |
| + | vi_case( case CTRL('N')|vbit: ) |
| vi_case( case 'j'|vbit: ) | |
| /* Control-n -- Get next command in history */ | |
| if (get_next_history()) | |
| goto rewrite_line; | |
| break; | |
| - | case CNTRL('P'): |
| - | vi_case( case CNTRL('P')|vbit: ) |
| + | case CTRL('P'): |
| + | vi_case( case CTRL('P')|vbit: ) |
| vi_case( case 'k'|vbit: ) | |
| /* Control-p -- Get previous command from history */ | |
| if (cur_history > 0) { | |
| @@ -1500,16 +1479,16 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |
| } | |
| break; | |
| #endif | |
| - | case CNTRL('U'): |
| - | vi_case( case CNTRL('U')|vbit: ) |
| + | case CTRL('U'): |
| + | vi_case( case CTRL('U')|vbit: ) |
| /* Control-U -- Clear line before cursor */ | |
| if (cursor) { | |
| strcpy(command, command + cursor); | |
| redraw(cmdedit_y, len -= cursor); | |
| } | |
| break; | |
| - | case CNTRL('W'): |
| - | vi_case( case CNTRL('W')|vbit: ) |
| + | case CTRL('W'): |
| + | vi_case( case CTRL('W')|vbit: ) |
| /* Control-W -- Remove the last word */ | |
| while (cursor > 0 && isspace(command[cursor-1])) | |
| input_backspace(); | |
| @@ -1645,7 +1624,7 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |
| break; | |
| #endif /* FEATURE_COMMAND_EDITING_VI */ | |
| - | case ESC: |
| + | case '\x1b': /* ESC */ |
| #if ENABLE_FEATURE_COMMAND_EDITING_VI | |
| if (vi_mode) { | |
| @@ -1676,7 +1655,6 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |
| switch (c) { | |
| #if ENABLE_FEATURE_COMMAND_TAB_COMPLETION | |
| case '\t': /* Alt-Tab */ | |
| - | |
| input_tab(&lastWasTab); | |
| break; | |
| #endif | |
| @@ -1686,24 +1664,19 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |
| if (cur_history > 0) { | |
| get_previous_history(); | |
| goto rewrite_line; | |
| - | } else { |
| - | beep(); |
| } | |
| + | beep(); |
| break; | |
| case 'B': | |
| /* Down Arrow -- Get next command in history */ | |
| if (!get_next_history()) | |
| break; | |
| /* Rewrite the line with the selected history item */ | |
| - | rewrite_line: |
| + | rewrite_line: |
| /* change command */ | |
| len = strlen(strcpy(command, history[cur_history])); | |
| /* redraw and go to eol (bol, in vi */ | |
| - | #if ENABLE_FEATURE_COMMAND_EDITING_VI |
| - | redraw(cmdedit_y, vi_mode ? 9999:0); |
| - | #else |
| - | redraw(cmdedit_y, 0); |
| - | #endif |
| + | redraw(cmdedit_y, vi_mode ? 9999 : 0); |
| break; | |
| #endif | |
| case 'C': | |
| @@ -1737,7 +1710,7 @@ rewrite_line: | |
| default: /* If it's regular input, do the normal thing */ | |
| #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT | |
| /* Control-V -- Add non-printable symbol */ | |
| - | if (c == CNTRL('V')) { |
| + | if (c == CTRL('V')) { |
| if (safe_read(0, &c, 1) < 1) | |
| goto prepare_to_die; | |
| if (c == 0) { | |
| @@ -1763,7 +1736,7 @@ rewrite_line: | |
| if (cursor == (len - 1)) { /* Append if at the end of the line */ | |
| *(command + cursor) = c; | |
| *(command + cursor + 1) = 0; | |
| - | cmdedit_set_out_char(0); |
| + | cmdedit_set_out_char(' '); |
| } else { /* Insert otherwise */ | |
| int sc = cursor; | |
| @@ -1796,7 +1769,7 @@ rewrite_line: | |
| free(history[MAX_HISTORY]); | |
| history[MAX_HISTORY] = 0; | |
| - | /* After max history, remove the oldest command */ |
| + | /* After max history, remove the oldest command */ |
| if (i >= MAX_HISTORY) { | |
| free(history[0]); | |
| for (i = 0; i < MAX_HISTORY-1; i++) | |
| @@ -1805,24 +1778,22 @@ rewrite_line: | |
| history[i++] = xstrdup(command); | |
| cur_history = i; | |
| n_history = i; | |
| - | #if ENABLE_FEATURE_SH_FANCY_PROMPT |
| - | num_ok_lines++; |
| - | #endif |
| - | } |
| - | #else /* MAX_HISTORY == 0 */ |
| - | #if ENABLE_FEATURE_SH_FANCY_PROMPT |
| - | if (len > 0) { /* no put empty line */ |
| - | num_ok_lines++; |
| + | USE_FEATURE_SH_FANCY_PROMPT(num_ok_lines++;) |
| } | |
| - | #endif |
| - | #endif /* MAX_HISTORY > 0 */ |
| + | #else /* MAX_HISTORY == 0 */ |
| + | /* dont put empty line */ |
| + | USE_FEATURE_SH_FANCY_PROMPT(if (len > 0) num_ok_lines++;) |
| + | #endif /* MAX_HISTORY */ |
| + | |
| if (break_out > 0) { | |
| - | command[len++] = '\n'; /* set '\n' */ |
| - | command[len] = 0; |
| + | command[len++] = '\n'; |
| + | command[len] = '\0'; |
| } | |
| + | |
| #if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_COMMAND_TAB_COMPLETION | |
| - | input_tab(0); /* strong free */ |
| + | input_tab(0); |
| #endif | |
| + | |
| #if ENABLE_FEATURE_SH_FANCY_PROMPT | |
| free(cmdedit_prompt); | |
| #endif | |
| @@ -1833,13 +1804,15 @@ rewrite_line: | |
| #endif /* FEATURE_COMMAND_EDITING */ | |
| - | #ifdef TEST |
| + | /* |
| + | * Testing |
| + | */ |
| - | const char *applet_name = "debug stuff usage"; |
| + | #ifdef TEST |
| - | #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT |
| #include <locale.h> | |
| - | #endif |
| + | |
| + | const char *applet_name = "debug stuff usage"; |
| int main(int argc, char **argv) | |
| { | |
| @@ -4,11 +4,11 @@ | |
| int cmdedit_read_input(char* promptStr, char* command); | |
| - | #ifdef CONFIG_ASH |
| + | #if ENABLE_ASH |
| extern const char *cmdedit_path_lookup; | |
| #endif | |
| - | #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY |
| + | #if ENABLE_FEATURE_COMMAND_SAVEHISTORY |
| void load_history(const char *fromfile); | |
| void save_history(const char *tofile); | |
| #endif | |