Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/libbb/lineedit.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/libbb/lineedit.c
r1765 r2725 1 1 /* vi: set sw=4 ts=4: */ 2 2 /* 3 * Termios command line History and Editing.3 * Command line editing. 4 4 * 5 5 * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license. … … 16 16 17 17 /* 18 Usage and known bugs: 19 Terminal key codes are not extensive, and more will probably 20 need to be added. This version was created on Debian GNU/Linux 2.x. 21 Delete, Backspace, Home, End, and the arrow keys were tested 22 to work in an Xterm and console. Ctrl-A also works as Home. 23 Ctrl-E also works as End. 24 25 Small bugs (simple effect): 26 - not true viewing if terminal size (x*y symbols) less 27 size (prompt + editor's line + 2 symbols) 28 - not true viewing if length prompt less terminal width 18 * Usage and known bugs: 19 * Terminal key codes are not extensive, more needs to be added. 20 * This version was created on Debian GNU/Linux 2.x. 21 * Delete, Backspace, Home, End, and the arrow keys were tested 22 * to work in an Xterm and console. Ctrl-A also works as Home. 23 * Ctrl-E also works as End. 24 * 25 * The following readline-like commands are not implemented: 26 * ESC-b -- Move back one word 27 * ESC-f -- Move forward one word 28 * ESC-d -- Delete forward one word 29 * CTL-t -- Transpose two characters 30 * 31 * lineedit does not know that the terminal escape sequences do not 32 * take up space on the screen. The redisplay code assumes, unless 33 * told otherwise, that each character in the prompt is a printable 34 * character that takes up one character position on the screen. 35 * You need to tell lineedit that some sequences of characters 36 * in the prompt take up no screen space. Compatibly with readline, 37 * use the \[ escape to begin a sequence of non-printing characters, 38 * and the \] escape to signal the end of such a sequence. Example: 39 * 40 * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] ' 29 41 */ 30 31 42 #include "libbb.h" 32 33 34 /* FIXME: obsolete CONFIG item? */ 35 #define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 36 43 #include "unicode.h" 37 44 38 45 #ifdef TEST 39 40 #define ENABLE_FEATURE_EDITING 0 41 #define ENABLE_FEATURE_TAB_COMPLETION 0 42 #define ENABLE_FEATURE_USERNAME_COMPLETION 0 43 #define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0 44 #define ENABLE_FEATURE_CLEAN_UP 0 45 46 #endif /* TEST */ 46 # define ENABLE_FEATURE_EDITING 0 47 # define ENABLE_FEATURE_TAB_COMPLETION 0 48 # define ENABLE_FEATURE_USERNAME_COMPLETION 0 49 #endif 47 50 48 51 … … 50 53 #if ENABLE_FEATURE_EDITING 51 54 52 #if ENABLE_LOCALE_SUPPORT 53 #define Isprint(c) isprint(c) 55 56 #define ENABLE_USERNAME_OR_HOMEDIR \ 57 (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT) 58 #define IF_USERNAME_OR_HOMEDIR(...) 59 #if ENABLE_USERNAME_OR_HOMEDIR 60 # undef IF_USERNAME_OR_HOMEDIR 61 # define IF_USERNAME_OR_HOMEDIR(...) __VA_ARGS__ 62 #endif 63 64 65 #undef CHAR_T 66 #if ENABLE_UNICODE_SUPPORT 67 # define BB_NUL ((wchar_t)0) 68 # define CHAR_T wchar_t 69 static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); } 70 # if ENABLE_FEATURE_EDITING_VI 71 static bool BB_isalnum(CHAR_T c) { return ((unsigned)c < 256 && isalnum(c)); } 72 # endif 73 static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); } 74 # undef isspace 75 # undef isalnum 76 # undef ispunct 77 # undef isprint 78 # define isspace isspace_must_not_be_used 79 # define isalnum isalnum_must_not_be_used 80 # define ispunct ispunct_must_not_be_used 81 # define isprint isprint_must_not_be_used 54 82 #else 55 #define Isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233')) 56 #endif 57 58 #define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \ 59 (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT) 60 61 enum { MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN }; 62 63 static line_input_t *state; 64 65 static struct termios initial_settings, new_settings; 66 67 static volatile unsigned cmdedit_termw = 80; /* actual terminal width */ 68 69 static int cmdedit_x; /* real x terminal position */ 70 static int cmdedit_y; /* pseudoreal y terminal position */ 71 static int cmdedit_prmt_len; /* length of prompt (without colors etc) */ 72 73 static unsigned cursor; 74 static unsigned command_len; 75 static char *command_ps; 76 static const char *cmdedit_prompt; 77 83 # define BB_NUL '\0' 84 # define CHAR_T char 85 # define BB_isspace(c) isspace(c) 86 # define BB_isalnum(c) isalnum(c) 87 # define BB_ispunct(c) ispunct(c) 88 #endif 89 #if ENABLE_UNICODE_PRESERVE_BROKEN 90 # define unicode_mark_raw_byte(wc) ((wc) | 0x20000000) 91 # define unicode_is_raw_byte(wc) ((wc) & 0x20000000) 92 #else 93 # define unicode_is_raw_byte(wc) 0 94 #endif 95 96 97 #define ESC "\033" 98 99 #define SEQ_CLEAR_TILL_END_OF_SCREEN ESC"[J" 100 //#define SEQ_CLEAR_TILL_END_OF_LINE ESC"[K" 101 102 103 enum { 104 MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0 105 ? CONFIG_FEATURE_EDITING_MAX_LEN 106 : 0x7ff0 107 }; 108 109 #if ENABLE_USERNAME_OR_HOMEDIR 110 static const char null_str[] ALIGN1 = ""; 111 #endif 112 113 /* We try to minimize both static and stack usage. */ 114 struct lineedit_statics { 115 line_input_t *state; 116 117 volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */ 118 sighandler_t previous_SIGWINCH_handler; 119 120 unsigned cmdedit_x; /* real x (col) terminal position */ 121 unsigned cmdedit_y; /* pseudoreal y (row) terminal position */ 122 unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */ 123 124 unsigned cursor; 125 int command_len; /* must be signed */ 126 /* signed maxsize: we want x in "if (x > S.maxsize)" 127 * to _not_ be promoted to unsigned */ 128 int maxsize; 129 CHAR_T *command_ps; 130 131 const char *cmdedit_prompt; 78 132 #if ENABLE_FEATURE_EDITING_FANCY_PROMPT 79 static char *hostname_buf; 80 static int num_ok_lines = 1; 81 #endif 82 83 #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR 84 static const char null_str[] = ""; 85 static char *user_buf; 86 static char *home_pwd_buf = (char*)null_str; 87 #endif 133 int num_ok_lines; /* = 1; */ 134 #endif 135 136 #if ENABLE_USERNAME_OR_HOMEDIR 137 char *user_buf; 138 char *home_pwd_buf; /* = (char*)null_str; */ 139 #endif 140 141 #if ENABLE_FEATURE_TAB_COMPLETION 142 char **matches; 143 unsigned num_matches; 144 #endif 145 146 #if ENABLE_FEATURE_EDITING_VI 147 # define DELBUFSIZ 128 148 CHAR_T *delptr; 149 smallint newdelflag; /* whether delbuf should be reused yet */ 150 CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */ 151 #endif 152 #if ENABLE_FEATURE_EDITING_ASK_TERMINAL 153 smallint sent_ESC_br6n; 154 #endif 155 }; 156 157 /* See lineedit_ptr_hack.c */ 158 extern struct lineedit_statics *const lineedit_ptr_to_statics; 159 160 #define S (*lineedit_ptr_to_statics) 161 #define state (S.state ) 162 #define cmdedit_termw (S.cmdedit_termw ) 163 #define previous_SIGWINCH_handler (S.previous_SIGWINCH_handler) 164 #define cmdedit_x (S.cmdedit_x ) 165 #define cmdedit_y (S.cmdedit_y ) 166 #define cmdedit_prmt_len (S.cmdedit_prmt_len) 167 #define cursor (S.cursor ) 168 #define command_len (S.command_len ) 169 #define command_ps (S.command_ps ) 170 #define cmdedit_prompt (S.cmdedit_prompt ) 171 #define num_ok_lines (S.num_ok_lines ) 172 #define user_buf (S.user_buf ) 173 #define home_pwd_buf (S.home_pwd_buf ) 174 #define matches (S.matches ) 175 #define num_matches (S.num_matches ) 176 #define delptr (S.delptr ) 177 #define newdelflag (S.newdelflag ) 178 #define delbuf (S.delbuf ) 179 180 #define INIT_S() do { \ 181 (*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \ 182 barrier(); \ 183 cmdedit_termw = 80; \ 184 IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \ 185 IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \ 186 } while (0) 187 static void deinit_S(void) 188 { 189 #if ENABLE_FEATURE_EDITING_FANCY_PROMPT 190 /* This one is allocated only if FANCY_PROMPT is on 191 * (otherwise it points to verbatim prompt (NOT malloced)) */ 192 free((char*)cmdedit_prompt); 193 #endif 194 #if ENABLE_USERNAME_OR_HOMEDIR 195 free(user_buf); 196 if (home_pwd_buf != null_str) 197 free(home_pwd_buf); 198 #endif 199 free(lineedit_ptr_to_statics); 200 } 201 #define DEINIT_S() deinit_S() 202 203 204 #if ENABLE_UNICODE_SUPPORT 205 static size_t load_string(const char *src, int maxsize) 206 { 207 ssize_t len = mbstowcs(command_ps, src, maxsize - 1); 208 if (len < 0) 209 len = 0; 210 command_ps[len] = BB_NUL; 211 return len; 212 } 213 static unsigned save_string(char *dst, unsigned maxsize) 214 { 215 # if !ENABLE_UNICODE_PRESERVE_BROKEN 216 ssize_t len = wcstombs(dst, command_ps, maxsize - 1); 217 if (len < 0) 218 len = 0; 219 dst[len] = '\0'; 220 return len; 221 # else 222 unsigned dstpos = 0; 223 unsigned srcpos = 0; 224 225 maxsize--; 226 while (dstpos < maxsize) { 227 wchar_t wc; 228 int n = srcpos; 229 230 /* Convert up to 1st invalid byte (or up to end) */ 231 while ((wc = command_ps[srcpos]) != BB_NUL 232 && !unicode_is_raw_byte(wc) 233 ) { 234 srcpos++; 235 } 236 command_ps[srcpos] = BB_NUL; 237 n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos); 238 if (n < 0) /* should not happen */ 239 break; 240 dstpos += n; 241 if (wc == BB_NUL) /* usually is */ 242 break; 243 244 /* We do have invalid byte here! */ 245 command_ps[srcpos] = wc; /* restore it */ 246 srcpos++; 247 if (dstpos == maxsize) 248 break; 249 dst[dstpos++] = (char) wc; 250 } 251 dst[dstpos] = '\0'; 252 return dstpos; 253 # endif 254 } 255 /* I thought just fputwc(c, stdout) would work. But no... */ 256 static void BB_PUTCHAR(wchar_t c) 257 { 258 char buf[MB_CUR_MAX + 1]; 259 mbstate_t mbst = { 0 }; 260 ssize_t len; 261 262 len = wcrtomb(buf, c, &mbst); 263 if (len > 0) { 264 buf[len] = '\0'; 265 fputs(buf, stdout); 266 } 267 } 268 # if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS 269 static wchar_t adjust_width_and_validate_wc(unsigned *width_adj, wchar_t wc) 270 # else 271 static wchar_t adjust_width_and_validate_wc(wchar_t wc) 272 # define adjust_width_and_validate_wc(width_adj, wc) \ 273 ((*(width_adj))++, adjust_width_and_validate_wc(wc)) 274 # endif 275 { 276 int w = 1; 277 278 if (unicode_status == UNICODE_ON) { 279 if (wc > CONFIG_LAST_SUPPORTED_WCHAR) { 280 /* note: also true for unicode_is_raw_byte(wc) */ 281 goto subst; 282 } 283 w = wcwidth(wc); 284 if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0) 285 || (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0) 286 || (!ENABLE_UNICODE_WIDE_WCHARS && w > 1) 287 ) { 288 subst: 289 w = 1; 290 wc = CONFIG_SUBST_WCHAR; 291 } 292 } 293 294 # if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS 295 *width_adj += w; 296 #endif 297 return wc; 298 } 299 #else /* !UNICODE */ 300 static size_t load_string(const char *src, int maxsize) 301 { 302 safe_strncpy(command_ps, src, maxsize); 303 return strlen(command_ps); 304 } 305 # if ENABLE_FEATURE_TAB_COMPLETION 306 static void save_string(char *dst, unsigned maxsize) 307 { 308 safe_strncpy(dst, command_ps, maxsize); 309 } 310 # endif 311 # define BB_PUTCHAR(c) bb_putchar(c) 312 /* Should never be called: */ 313 int adjust_width_and_validate_wc(unsigned *width_adj, int wc); 314 #endif 315 88 316 89 317 /* Put 'command_ps[cursor]', cursor++. 90 318 * Advance cursor on screen. If we reached right margin, scroll text up 91 319 * and remove terminal margin effect by printing 'next_char' */ 92 static void cmdedit_set_out_char(int next_char) 93 { 94 int c = (unsigned char)command_ps[cursor]; 95 96 if (c == '\0') { 320 #define HACK_FOR_WRONG_WIDTH 1 321 static void put_cur_glyph_and_inc_cursor(void) 322 { 323 CHAR_T c = command_ps[cursor]; 324 unsigned width = 0; 325 int ofs_to_right; 326 327 if (c == BB_NUL) { 97 328 /* erase character after end of input string */ 98 329 c = ' '; 99 } 100 #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 101 /* Display non-printable characters in reverse */ 102 if (!Isprint(c)) { 103 if (c >= 128) 104 c -= 128; 105 if (c < ' ') 106 c += '@'; 107 if (c == 127) 108 c = '?'; 109 printf("\033[7m%c\033[0m", c); 110 } else 111 #endif 112 { 113 if (initial_settings.c_lflag & ECHO) 114 putchar(c); 115 } 116 if (++cmdedit_x >= cmdedit_termw) { 117 /* terminal is scrolled down */ 330 } else { 331 /* advance cursor only if we aren't at the end yet */ 332 cursor++; 333 if (unicode_status == UNICODE_ON) { 334 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;) 335 c = adjust_width_and_validate_wc(&cmdedit_x, c); 336 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;) 337 } else { 338 cmdedit_x++; 339 } 340 } 341 342 ofs_to_right = cmdedit_x - cmdedit_termw; 343 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) { 344 /* c fits on this line */ 345 BB_PUTCHAR(c); 346 } 347 348 if (ofs_to_right >= 0) { 349 /* we go to the next line */ 350 #if HACK_FOR_WRONG_WIDTH 351 /* This works better if our idea of term width is wrong 352 * and it is actually wider (often happens on serial lines). 353 * Printing CR,LF *forces* cursor to next line. 354 * OTOH if terminal width is correct AND terminal does NOT 355 * have automargin (IOW: it is moving cursor to next line 356 * by itself (which is wrong for VT-10x terminals)), 357 * this will break things: there will be one extra empty line */ 358 puts("\r"); /* + implicit '\n' */ 359 #else 360 /* VT-10x terminals don't wrap cursor to next line when last char 361 * on the line is printed - cursor stays "over" this char. 362 * Need to print _next_ char too (first one to appear on next line) 363 * to make cursor move down to next line. 364 */ 365 /* Works ok only if cmdedit_termw is correct. */ 366 c = command_ps[cursor]; 367 if (c == BB_NUL) 368 c = ' '; 369 BB_PUTCHAR(c); 370 bb_putchar('\b'); 371 #endif 118 372 cmdedit_y++; 119 cmdedit_x = 0; 120 /* destroy "(auto)margin" */ 121 putchar(next_char); 122 putchar('\b'); 123 } 124 // Huh? What if command_ps[cursor] == '\0' (we are at the end already?) 125 cursor++; 373 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) { 374 width = 0; 375 } else { /* ofs_to_right > 0 */ 376 /* wide char c didn't fit on prev line */ 377 BB_PUTCHAR(c); 378 } 379 cmdedit_x = width; 380 } 126 381 } 127 382 128 383 /* Move to end of line (by printing all chars till the end) */ 129 static void input_end(void)384 static void put_till_end_and_adv_cursor(void) 130 385 { 131 386 while (cursor < command_len) 132 cmdedit_set_out_char(' ');387 put_cur_glyph_and_inc_cursor(); 133 388 } 134 389 … … 136 391 static void goto_new_line(void) 137 392 { 138 input_end(); 139 if (cmdedit_x) 140 putchar('\n'); 141 } 142 143 144 static void out1str(const char *s) 145 { 146 if (s) 147 fputs(s, stdout); 393 put_till_end_and_adv_cursor(); 394 if (cmdedit_x != 0) 395 bb_putchar('\n'); 148 396 } 149 397 150 398 static void beep(void) 151 399 { 152 putchar('\007'); 400 bb_putchar('\007'); 401 } 402 403 static void put_prompt(void) 404 { 405 unsigned w; 406 407 fputs(cmdedit_prompt, stdout); 408 fflush_all(); 409 cursor = 0; 410 w = cmdedit_termw; /* read volatile var once */ 411 cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */ 412 cmdedit_x = cmdedit_prmt_len % w; 153 413 } 154 414 … … 157 417 static void input_backward(unsigned num) 158 418 { 159 int count_y;160 161 419 if (num > cursor) 162 420 num = cursor; 163 if ( !num)421 if (num == 0) 164 422 return; 165 423 cursor -= num; 424 425 if ((ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS) 426 && unicode_status == UNICODE_ON 427 ) { 428 /* correct NUM to be equal to _screen_ width */ 429 int n = num; 430 num = 0; 431 while (--n >= 0) 432 adjust_width_and_validate_wc(&num, command_ps[cursor + n]); 433 if (num == 0) 434 return; 435 } 166 436 167 437 if (cmdedit_x >= num) { 168 438 cmdedit_x -= num; 169 439 if (num <= 4) { 170 printf("\b\b\b\b" + (4-num)); 440 /* This is longer by 5 bytes on x86. 441 * Also gets miscompiled for ARM users 442 * (busybox.net/bugs/view.php?id=2274). 443 * printf(("\b\b\b\b" + 4) - num); 444 * return; 445 */ 446 do { 447 bb_putchar('\b'); 448 } while (--num); 171 449 return; 172 450 } 173 printf( "\033[%uD", num);451 printf(ESC"[%uD", num); 174 452 return; 175 453 } 176 454 177 455 /* Need to go one or more lines up */ 178 num -= cmdedit_x; 179 count_y = 1 + (num / cmdedit_termw); 180 cmdedit_y -= count_y; 181 cmdedit_x = cmdedit_termw * count_y - num; 182 /* go to 1st column; go up; go to correct column */ 183 printf("\r" "\033[%dA" "\033[%dC", count_y, cmdedit_x); 184 } 185 186 static void put_prompt(void) 187 { 188 out1str(cmdedit_prompt); 189 cmdedit_x = cmdedit_prmt_len; 190 cursor = 0; 191 // Huh? what if cmdedit_prmt_len >= width? 192 cmdedit_y = 0; /* new quasireal y */ 456 if (ENABLE_UNICODE_WIDE_WCHARS) { 457 /* With wide chars, it is hard to "backtrack" 458 * and reliably figure out where to put cursor. 459 * Example (<> is a wide char; # is an ordinary char, _ cursor): 460 * |prompt: <><> | 461 * |<><><><><><> | 462 * |_ | 463 * and user presses left arrow. num = 1, cmdedit_x = 0, 464 * We need to go up one line, and then - how do we know that 465 * we need to go *10* positions to the right? Because 466 * |prompt: <>#<>| 467 * |<><><>#<><><>| 468 * |_ | 469 * in this situation we need to go *11* positions to the right. 470 * 471 * A simpler thing to do is to redraw everything from the start 472 * up to new cursor position (which is already known): 473 */ 474 unsigned sv_cursor; 475 /* go to 1st column; go up to first line */ 476 printf("\r" ESC"[%uA", cmdedit_y); 477 cmdedit_y = 0; 478 sv_cursor = cursor; 479 put_prompt(); /* sets cursor to 0 */ 480 while (cursor < sv_cursor) 481 put_cur_glyph_and_inc_cursor(); 482 } else { 483 int lines_up; 484 unsigned width; 485 /* num = chars to go back from the beginning of current line: */ 486 num -= cmdedit_x; 487 width = cmdedit_termw; /* read volatile var once */ 488 /* num=1...w: one line up, w+1...2w: two, etc: */ 489 lines_up = 1 + (num - 1) / width; 490 cmdedit_x = (width * cmdedit_y - num) % width; 491 cmdedit_y -= lines_up; 492 /* go to 1st column; go up */ 493 printf("\r" ESC"[%uA", lines_up); 494 /* go to correct column. 495 * xterm, konsole, Linux VT interpret 0 as 1 below! wow. 496 * need to *make sure* we skip it if cmdedit_x == 0 */ 497 if (cmdedit_x) 498 printf(ESC"[%uC", cmdedit_x); 499 } 193 500 } 194 501 … … 196 503 static void redraw(int y, int back_cursor) 197 504 { 198 if (y > 0) /* up to start y*/199 printf( "\033[%dA", y);200 putchar('\r');505 if (y > 0) /* up y lines */ 506 printf(ESC"[%uA", y); 507 bb_putchar('\r'); 201 508 put_prompt(); 202 input_end(); /* rewrite */203 printf( "\033[J"); /* erase after cursor */509 put_till_end_and_adv_cursor(); 510 printf(SEQ_CLEAR_TILL_END_OF_SCREEN); 204 511 input_backward(back_cursor); 205 512 } 206 207 #if ENABLE_FEATURE_EDITING_VI208 #define DELBUFSIZ 128209 static char *delbuf; /* a (malloced) place to store deleted characters */210 static char *delp;211 static char newdelflag; /* whether delbuf should be reused yet */212 #endif213 513 214 514 /* Delete the char in front of the cursor, optionally saving it 215 515 * for later putback */ 516 #if !ENABLE_FEATURE_EDITING_VI 517 static void input_delete(void) 518 #define input_delete(save) input_delete() 519 #else 216 520 static void input_delete(int save) 521 #endif 217 522 { 218 523 int j = cursor; 219 524 220 if (j == command_len)525 if (j == (int)command_len) 221 526 return; 222 527 … … 224 529 if (save) { 225 530 if (newdelflag) { 226 if (!delbuf) 227 delbuf = malloc(DELBUFSIZ); 228 /* safe if malloc fails */ 229 delp = delbuf; 531 delptr = delbuf; 230 532 newdelflag = 0; 231 533 } 232 if (delbuf && (delp - delbuf < DELBUFSIZ)) 233 *delp++ = command_ps[j]; 234 } 235 #endif 236 237 strcpy(command_ps + j, command_ps + j + 1); 534 if ((delptr - delbuf) < DELBUFSIZ) 535 *delptr++ = command_ps[j]; 536 } 537 #endif 538 539 memmove(command_ps + j, command_ps + j + 1, 540 /* (command_len + 1 [because of NUL]) - (j + 1) 541 * simplified into (command_len - j) */ 542 (command_len - j) * sizeof(command_ps[0])); 238 543 command_len--; 239 input_end(); /* rewrite new line */ 240 cmdedit_set_out_char(' '); /* erase char */ 544 put_till_end_and_adv_cursor(); 545 /* Last char is still visible, erase it (and more) */ 546 printf(SEQ_CLEAR_TILL_END_OF_SCREEN); 241 547 input_backward(cursor - j); /* back to old pos cursor */ 242 548 } … … 246 552 { 247 553 int ocursor; 248 int j = delp - delbuf;554 int j = delptr - delbuf; 249 555 250 556 if (j == 0) … … 252 558 ocursor = cursor; 253 559 /* open hole and then fill it */ 254 memmove(command_ps + cursor + j, command_ps + cursor, command_len - cursor + 1); 255 strncpy(command_ps + cursor, delbuf, j); 560 memmove(command_ps + cursor + j, command_ps + cursor, 561 (command_len - cursor + 1) * sizeof(command_ps[0])); 562 memcpy(command_ps + cursor, delbuf, j * sizeof(command_ps[0])); 256 563 command_len += j; 257 input_end(); /* rewrite new line */564 put_till_end_and_adv_cursor(); 258 565 input_backward(cursor - ocursor - j + 1); /* at end of new text */ 259 566 } … … 273 580 { 274 581 if (cursor < command_len) 275 cmdedit_set_out_char(command_ps[cursor + 1]); 276 } 277 582 put_cur_glyph_and_inc_cursor(); 583 } 278 584 279 585 #if ENABLE_FEATURE_TAB_COMPLETION 280 586 281 static char **matches; 282 static unsigned num_matches; 587 //FIXME: 588 //needs to be more clever: currently it thinks that "foo\ b<TAB> 589 //matches the file named "foo bar", which is untrue. 590 //Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>, 591 //not "foo bar <cursor>... 283 592 284 593 static void free_tab_completion_data(void) … … 294 603 static void add_match(char *matched) 295 604 { 296 int nm = num_matches; 297 int nm1 = nm + 1; 298 299 matches = xrealloc(matches, nm1 * sizeof(char *)); 300 matches[nm] = matched; 605 matches = xrealloc_vector(matches, 4, num_matches); 606 matches[num_matches] = matched; 301 607 num_matches++; 302 608 } 303 609 304 #if ENABLE_FEATURE_USERNAME_COMPLETION 305 static void username_tab_completion(char *ud, char *with_shash_flg) 610 # if ENABLE_FEATURE_USERNAME_COMPLETION 611 /* Replace "~user/..." with "/homedir/...". 612 * The parameter is malloced, free it or return it 613 * unchanged if no user is matched. 614 */ 615 static char *username_path_completion(char *ud) 306 616 { 307 617 struct passwd *entry; 308 int userlen; 309 310 ud++; /* ~user/... to user/... */ 618 char *tilde_name = ud; 619 char *home = NULL; 620 621 ud++; /* skip ~ */ 622 if (*ud == '/') { /* "~/..." */ 623 home = home_pwd_buf; 624 } else { 625 /* "~user/..." */ 626 ud = strchr(ud, '/'); 627 *ud = '\0'; /* "~user" */ 628 entry = getpwnam(tilde_name + 1); 629 *ud = '/'; /* restore "~user/..." */ 630 if (entry) 631 home = entry->pw_dir; 632 } 633 if (home) { 634 ud = concat_path_file(home, ud); 635 free(tilde_name); 636 tilde_name = ud; 637 } 638 return tilde_name; 639 } 640 641 /* ~use<tab> - find all users with this prefix. 642 * Return the length of the prefix used for matching. 643 */ 644 static NOINLINE unsigned complete_username(const char *ud) 645 { 646 /* Using _r function to avoid pulling in static buffers */ 647 char line_buff[256]; 648 struct passwd pwd; 649 struct passwd *result; 650 unsigned userlen; 651 652 ud++; /* skip ~ */ 311 653 userlen = strlen(ud); 312 654 313 if (with_shash_flg) { /* "~/..." or "~user/..." */ 314 char *sav_ud = ud - 1; 315 char *home = NULL; 316 char *temp; 317 318 if (*ud == '/') { /* "~/..." */ 319 home = home_pwd_buf; 320 } else { 321 /* "~user/..." */ 322 temp = strchr(ud, '/'); 323 *temp = 0; /* ~user\0 */ 324 entry = getpwnam(ud); 325 *temp = '/'; /* restore ~user/... */ 326 ud = temp; 327 if (entry) 328 home = entry->pw_dir; 329 } 330 if (home) { 331 if ((userlen + strlen(home) + 1) < MAX_LINELEN) { 332 char temp2[MAX_LINELEN]; /* argument size */ 333 334 /* /home/user/... */ 335 sprintf(temp2, "%s%s", home, ud); 336 strcpy(sav_ud, temp2); 337 } 338 } 339 } else { 340 /* "~[^/]*" */ 341 /* Using _r function to avoid pulling in static buffers */ 342 char line_buff[256]; 343 struct passwd pwd; 344 struct passwd *result; 345 346 setpwent(); 347 while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { 348 /* Null usernames should result in all users as possible completions. */ 349 if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { 350 add_match(xasprintf("~%s/", pwd.pw_name)); 351 } 352 } 353 endpwent(); 354 } 355 } 356 #endif /* FEATURE_COMMAND_USERNAME_COMPLETION */ 655 setpwent(); 656 while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { 657 /* Null usernames should result in all users as possible completions. */ 658 if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { 659 add_match(xasprintf("~%s/", pwd.pw_name)); 660 } 661 } 662 endpwent(); 663 664 return 1 + userlen; 665 } 666 # endif /* FEATURE_USERNAME_COMPLETION */ 357 667 358 668 enum { … … 362 672 }; 363 673 364 static int path_parse(char ***p , int flags)674 static int path_parse(char ***p) 365 675 { 366 676 int npth; … … 369 679 char **res; 370 680 371 /* if not setenv PATH variable, to search cur dir "." */372 if (flags != FIND_EXE_ONLY)373 return 1;374 375 681 if (state->flags & WITH_PATH_LOOKUP) 376 682 pth = state->path_lookup; 377 683 else 378 684 pth = getenv("PATH"); 379 /* PATH=<empty> or PATH=:<empty> */ 685 686 /* PATH="" or PATH=":"? */ 380 687 if (!pth || !pth[0] || LONE_CHAR(pth, ':')) 381 688 return 1; … … 387 694 if (!tmp) 388 695 break; 389 if (*++tmp == '\0') 696 tmp++; 697 if (*tmp == '\0') 390 698 break; /* :<empty> */ 391 699 npth++; 392 700 } 393 701 394 res = xmalloc(npth * sizeof(char*));702 *p = res = xmalloc(npth * sizeof(res[0])); 395 703 res[0] = tmp = xstrdup(pth); 396 704 npth = 1; … … 404 712 res[npth++] = tmp; 405 713 } 406 *p = res;407 714 return npth; 408 715 } 409 716 410 static void exe_n_cwd_tab_completion(char *command, int type) 411 { 412 DIR *dir; 413 struct dirent *next; 414 char dirbuf[MAX_LINELEN]; 415 struct stat st; 717 /* Complete command, directory or file name. 718 * Return the length of the prefix used for matching. 719 */ 720 static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) 721 { 416 722 char *path1[1]; 417 723 char **paths = path1; 418 724 int npaths; 419 725 int i; 420 char *found; 421 char *pfind = strrchr(command, '/'); 726 unsigned pf_len; 727 const char *pfind; 728 char *dirbuf = NULL; 422 729 423 730 npaths = 1; 424 731 path1[0] = (char*)"."; 425 732 426 if (pfind == NULL) { 427 /* no dir, if flags==EXE_ONLY - get paths, else "." */ 428 npaths = path_parse(&paths, type); 733 pfind = strrchr(command, '/'); 734 if (!pfind) { 735 if (type == FIND_EXE_ONLY) 736 npaths = path_parse(&paths); 429 737 pfind = command; 430 738 } else { 431 /* dirbuf = ".../.../.../" */432 safe_strncpy(dirbuf, command, (pfind - command) + 2);433 #if ENABLE_FEATURE_USERNAME_COMPLETION434 if (dirbuf[0] == '~') /* ~/... or ~user/... */435 username_tab_completion(dirbuf, dirbuf);436 #endif437 paths[0] = dirbuf;438 739 /* point to 'l' in "..../last_component" */ 439 740 pfind++; 440 } 741 /* dirbuf = ".../.../.../" */ 742 dirbuf = xstrndup(command, pfind - command); 743 # if ENABLE_FEATURE_USERNAME_COMPLETION 744 if (dirbuf[0] == '~') /* ~/... or ~user/... */ 745 dirbuf = username_path_completion(dirbuf); 746 # endif 747 path1[0] = dirbuf; 748 } 749 pf_len = strlen(pfind); 441 750 442 751 for (i = 0; i < npaths; i++) { 752 DIR *dir; 753 struct dirent *next; 754 struct stat st; 755 char *found; 756 443 757 dir = opendir(paths[i]); 444 if (!dir) /* Don't print an error */445 continue; 758 if (!dir) 759 continue; /* don't print an error */ 446 760 447 761 while ((next = readdir(dir)) != NULL) { 448 int len1;449 const char * str_found = next->d_name;450 451 /* matched?*/452 if ( strncmp(str_found, pfind, strlen(pfind)))762 unsigned len; 763 const char *name_found = next->d_name; 764 765 /* .../<tab>: bash 3.2.0 shows dotfiles, but not . and .. */ 766 if (!pfind[0] && DOT_OR_DOTDOT(name_found)) 453 767 continue; 454 /* not see .name without .match */ 455 if (*str_found == '.' && *pfind == 0) { 456 if (NOT_LONE_CHAR(paths[i], '/') || str_found[1]) 457 continue; 458 str_found = ""; /* only "/" */ 459 } 460 found = concat_path_file(paths[i], str_found); 461 /* hmm, remover in progress? */ 462 if (stat(found, &st) < 0) 463 goto cont; 464 /* find with dirs? */ 465 if (paths[i] != dirbuf) 466 strcpy(found, next->d_name); /* only name */ 467 468 len1 = strlen(found); 469 found = xrealloc(found, len1 + 2); 470 found[len1] = '\0'; 471 found[len1+1] = '\0'; 768 /* match? */ 769 if (strncmp(name_found, pfind, pf_len) != 0) 770 continue; /* no */ 771 772 found = concat_path_file(paths[i], name_found); 773 /* NB: stat() first so that we see is it a directory; 774 * but if that fails, use lstat() so that 775 * we still match dangling links */ 776 if (stat(found, &st) && lstat(found, &st)) 777 goto cont; /* hmm, remove in progress? */ 778 779 /* Save only name */ 780 len = strlen(name_found); 781 found = xrealloc(found, len + 2); /* +2: for slash and NUL */ 782 strcpy(found, name_found); 472 783 473 784 if (S_ISDIR(st.st_mode)) { 474 /* name is directory */ 475 if (found[len1-1] != '/') { 476 found[len1] = '/'; 477 } 785 /* name is a directory, add slash */ 786 found[len] = '/'; 787 found[len + 1] = '\0'; 478 788 } else { 479 /* not put found file if search only dirs for cd*/789 /* skip files if looking for dirs only (example: cd) */ 480 790 if (type == FIND_DIR_ONLY) 481 791 goto cont; 482 792 } 483 /* Add it to the list */793 /* add it to the list */ 484 794 add_match(found); 485 795 continue; … … 488 798 } 489 799 closedir(dir); 490 } 800 } /* for every path */ 801 491 802 if (paths != path1) { 492 free(paths[0]); /* allocated memoryonly in first member */803 free(paths[0]); /* allocated memory is only in first member */ 493 804 free(paths); 494 805 } 495 } 496 806 free(dirbuf); 807 808 return pf_len; 809 } 810 811 /* build_match_prefix: 812 * On entry, match_buf contains everything up to cursor at the moment <tab> 813 * was pressed. This function looks at it, figures out what part of it 814 * constitutes the command/file/directory prefix to use for completion, 815 * and rewrites match_buf to contain only that part. 816 */ 817 #define dbg_bmp 0 818 /* Helpers: */ 819 /* QUOT is used on elements of int_buf[], which are bytes, 820 * not Unicode chars. Therefore it works correctly even in Unicode mode. 821 */ 497 822 #define QUOT (UCHAR_MAX+1) 498 499 #define collapse_pos(is, in) { \ 500 memmove(int_buf+(is), int_buf+(in), (MAX_LINELEN+1-(is)-(in))*sizeof(int)); \ 501 memmove(pos_buf+(is), pos_buf+(in), (MAX_LINELEN+1-(is)-(in))*sizeof(int)); } 502 503 static int find_match(char *matchBuf, int *len_with_quotes) 823 static void remove_chunk(int16_t *int_buf, int beg, int end) 824 { 825 /* beg must be <= end */ 826 if (beg == end) 827 return; 828 829 while ((int_buf[beg] = int_buf[end]) != 0) 830 beg++, end++; 831 832 if (dbg_bmp) { 833 int i; 834 for (i = 0; int_buf[i]; i++) 835 bb_putchar((unsigned char)int_buf[i]); 836 bb_putchar('\n'); 837 } 838 } 839 /* Caller ensures that match_buf points to a malloced buffer 840 * big enough to hold strlen(match_buf)*2 + 2 841 */ 842 static NOINLINE int build_match_prefix(char *match_buf) 504 843 { 505 844 int i, j; 506 845 int command_mode; 507 int c, c2; 508 int int_buf[MAX_LINELEN + 1]; 509 int pos_buf[MAX_LINELEN + 1]; 510 511 /* set to integer dimension characters and own positions */ 512 for (i = 0;; i++) { 513 int_buf[i] = (unsigned char)matchBuf[i]; 514 if (int_buf[i] == 0) { 515 pos_buf[i] = -1; /* indicator end line */ 516 break; 517 } 518 pos_buf[i] = i; 519 } 520 521 /* mask \+symbol and convert '\t' to ' ' */ 522 for (i = j = 0; matchBuf[i]; i++, j++) 523 if (matchBuf[i] == '\\') { 524 collapse_pos(j, j + 1); 525 int_buf[j] |= QUOT; 846 int16_t *int_buf = (int16_t*)match_buf; 847 848 if (dbg_bmp) printf("\n%s\n", match_buf); 849 850 /* Copy in reverse order, since they overlap */ 851 i = strlen(match_buf); 852 do { 853 int_buf[i] = (unsigned char)match_buf[i]; 854 i--; 855 } while (i >= 0); 856 857 /* Mark every \c as "quoted c" */ 858 for (i = 0; int_buf[i]; i++) { 859 if (int_buf[i] == '\\') { 860 remove_chunk(int_buf, i, i + 1); 861 int_buf[i] |= QUOT; 862 } 863 } 864 /* Quote-mark "chars" and 'chars', drop delimiters */ 865 { 866 int in_quote = 0; 867 i = 0; 868 while (int_buf[i]) { 869 int cur = int_buf[i]; 870 if (!cur) 871 break; 872 if (cur == '\'' || cur == '"') { 873 if (!in_quote || (cur == in_quote)) { 874 in_quote ^= cur; 875 remove_chunk(int_buf, i, i + 1); 876 continue; 877 } 878 } 879 if (in_quote) 880 int_buf[i] = cur | QUOT; 526 881 i++; 527 #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 528 if (matchBuf[i] == '\t') /* algorithm equivalent */ 529 int_buf[j] = ' ' | QUOT; 530 #endif 531 } 532 #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 533 else if (matchBuf[i] == '\t') 534 int_buf[j] = ' '; 535 #endif 536 537 /* mask "symbols" or 'symbols' */ 538 c2 = 0; 882 } 883 } 884 885 /* Remove everything up to command delimiters: 886 * ';' ';;' '&' '|' '&&' '||', 887 * but careful with '>&' '<&' '>|' 888 */ 539 889 for (i = 0; int_buf[i]; i++) { 540 c = int_buf[i]; 541 if (c == '\'' || c == '"') { 542 if (c2 == 0) 543 c2 = c; 544 else { 545 if (c == c2) 546 c2 = 0; 547 else 548 int_buf[i] |= QUOT; 890 int cur = int_buf[i]; 891 if (cur == ';' || cur == '&' || cur == '|') { 892 int prev = i ? int_buf[i - 1] : 0; 893 if (cur == '&' && (prev == '>' || prev == '<')) { 894 continue; 895 } else if (cur == '|' && prev == '>') { 896 continue; 549 897 } 550 } else if (c2 != 0 && c != '$') 551 int_buf[i] |= QUOT; 552 } 553 554 /* skip commands with arguments if line has commands delimiters */ 555 /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */ 898 remove_chunk(int_buf, 0, i + 1 + (cur == int_buf[i + 1])); 899 i = -1; /* back to square 1 */ 900 } 901 } 902 /* Remove all `cmd` */ 556 903 for (i = 0; int_buf[i]; i++) { 557 c = int_buf[i];558 c2 = int_buf[i + 1];559 j = i ? int_buf[i - 1] : -1;560 command_mode = 0;561 if (c == ';' || c == '&' || c == '|') {562 command_mode = 1 + (c == c2);563 if (c == '&') {564 if (j == '>' || j == '<')565 command_mode = 0;566 } else if (c == '|' && j == '>')567 command_mode = 0;568 }569 if (command_mode) {570 collapse_pos(0, i + command_mode);571 i = -1; /* hack incremet */572 }573 }574 /* collapse `command...` */575 for (i = 0; int_buf[i]; i++)576 904 if (int_buf[i] == '`') { 577 for (j = i + 1; int_buf[j]; j++) 905 for (j = i + 1; int_buf[j]; j++) { 578 906 if (int_buf[j] == '`') { 579 collapse_pos(i, j + 1); 580 j = 0; 581 break; 907 /* `cmd` should count as a word: 908 * `cmd` c<tab> should search for files c*, 909 * not commands c*. Therefore we don't drop 910 * `cmd` entirely, we replace it with single `. 911 */ 912 remove_chunk(int_buf, i, j); 913 goto next; 582 914 } 583 if (j) { 584 /* not found close ` - command mode, collapse all previous */ 585 collapse_pos(0, i + 1); 586 break; 587 } else 588 i--; /* hack incremet */ 589 } 590 591 /* collapse (command...(command...)...) or {command...{command...}...} */ 592 c = 0; /* "recursive" level */ 593 c2 = 0; 594 for (i = 0; int_buf[i]; i++) 915 } 916 /* No closing ` - command mode, remove all up to ` */ 917 remove_chunk(int_buf, 0, i + 1); 918 break; 919 next: ; 920 } 921 } 922 923 /* Remove "cmd (" and "cmd {" 924 * Example: "if { c<tab>" 925 * In this example, c should be matched as command pfx. 926 */ 927 for (i = 0; int_buf[i]; i++) { 595 928 if (int_buf[i] == '(' || int_buf[i] == '{') { 596 if (int_buf[i] == '(') 597 c++; 598 else 599 c2++; 600 collapse_pos(0, i + 1); 601 i = -1; /* hack incremet */ 602 } 603 for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) 604 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) { 605 if (int_buf[i] == ')') 606 c--; 607 else 608 c2--; 609 collapse_pos(0, i + 1); 610 i = -1; /* hack incremet */ 611 } 612 613 /* skip first not quote space */ 929 remove_chunk(int_buf, 0, i + 1); 930 i = -1; /* back to square 1 */ 931 } 932 } 933 934 /* Remove leading unquoted spaces */ 614 935 for (i = 0; int_buf[i]; i++) 615 936 if (int_buf[i] != ' ') 616 937 break; 617 if (i) 618 collapse_pos(0, i); 619 620 /* set find mode for completion */ 938 remove_chunk(int_buf, 0, i); 939 940 /* Determine completion mode */ 621 941 command_mode = FIND_EXE_ONLY; 622 for (i = 0; int_buf[i]; i++) 942 for (i = 0; int_buf[i]; i++) { 623 943 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { 624 if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY 625 && matchBuf[pos_buf[0]]=='c' 626 && matchBuf[pos_buf[1]]=='d' 944 if (int_buf[i] == ' ' 945 && command_mode == FIND_EXE_ONLY 946 && (char)int_buf[0] == 'c' 947 && (char)int_buf[1] == 'd' 948 && i == 2 /* -> int_buf[2] == ' ' */ 627 949 ) { 628 950 command_mode = FIND_DIR_ONLY; … … 632 954 } 633 955 } 634 for (i = 0; int_buf[i]; i++) 635 /* "strlen" */; 636 /* find last word */ 956 } 957 if (dbg_bmp) printf("command_mode(0:exe/1:dir/2:file):%d\n", command_mode); 958 959 /* Remove everything except last word */ 960 for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */ 961 continue; 637 962 for (--i; i >= 0; i--) { 638 c = int_buf[i]; 639 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') { 640 collapse_pos(0, i + 1); 641 break; 642 } 643 } 644 /* skip first not quoted '\'' or '"' */ 645 for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++) 646 /*skip*/; 647 /* collapse quote or unquote // or /~ */ 648 while ((int_buf[i] & ~QUOT) == '/' 649 && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~') 650 ) { 963 int cur = int_buf[i]; 964 if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') { 965 remove_chunk(int_buf, 0, i + 1); 966 break; 967 } 968 } 969 970 /* Convert back to string of _chars_ */ 971 i = 0; 972 while ((match_buf[i] = int_buf[i]) != '\0') 651 973 i++; 652 } 653 654 /* set only match and destroy quotes */ 655 j = 0; 656 for (c = 0; pos_buf[i] >= 0; i++) { 657 matchBuf[c++] = matchBuf[pos_buf[i]]; 658 j = pos_buf[i] + 1; 659 } 660 matchBuf[c] = 0; 661 /* old lenght matchBuf with quotes symbols */ 662 *len_with_quotes = j ? j - pos_buf[0] : 0; 974 975 if (dbg_bmp) printf("final match_buf:'%s'\n", match_buf); 663 976 664 977 return command_mode; … … 666 979 667 980 /* 668 * display by column (original idea from ls applet,669 * very optimized by me :)981 * Display by column (original idea from ls applet, 982 * very optimized by me [Vladimir] :) 670 983 */ 671 984 static void showfiles(void) … … 677 990 int l; 678 991 679 /* find the longest file name -use that as the column width */992 /* find the longest file name - use that as the column width */ 680 993 for (row = 0; row < nrows; row++) { 681 l = strlen(matches[row]);994 l = unicode_strwidth(matches[row]); 682 995 if (column_width < l) 683 996 column_width = l; … … 699 1012 for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) { 700 1013 printf("%s%-*s", matches[n], 701 (int)(column_width - strlen(matches[n])), ""); 702 } 703 printf("%s\n", matches[n]); 704 } 705 } 706 707 static char *add_quote_for_spec_chars(char *found) 1014 (int)(column_width - unicode_strwidth(matches[n])), "" 1015 ); 1016 } 1017 if (ENABLE_UNICODE_SUPPORT) 1018 puts(printable_string(NULL, matches[n])); 1019 else 1020 puts(matches[n]); 1021 } 1022 } 1023 1024 static const char *is_special_char(char c) 1025 { 1026 return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c); 1027 } 1028 1029 static char *quote_special_chars(char *found) 708 1030 { 709 1031 int l = 0; 710 char *s = x malloc((strlen(found) + 1) * 2);1032 char *s = xzalloc((strlen(found) + 1) * 2); 711 1033 712 1034 while (*found) { 713 if ( strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>",*found))1035 if (is_special_char(*found)) 714 1036 s[l++] = '\\'; 715 1037 s[l++] = *found++; 716 1038 } 717 s[l] = 0;1039 /* s[l] = '\0'; - already is */ 718 1040 return s; 719 1041 } 720 1042 721 static int match_compare(const void *a, const void *b)722 {723 return strcmp(*(char**)a, *(char**)b);724 }725 726 1043 /* Do TAB completion */ 727 static void input_tab(int *lastWasTab) 728 { 1044 static NOINLINE void input_tab(smallint *lastWasTab) 1045 { 1046 char *chosen_match; 1047 char *match_buf; 1048 size_t len_found; 1049 /* Length of string used for matching */ 1050 unsigned match_pfx_len = match_pfx_len; 1051 int find_type; 1052 # if ENABLE_UNICODE_SUPPORT 1053 /* cursor pos in command converted to multibyte form */ 1054 int cursor_mb; 1055 # endif 729 1056 if (!(state->flags & TAB_COMPLETION)) 730 1057 return; 731 1058 732 if (!*lastWasTab) { 733 char *tmp, *tmp1; 734 int len_found; 735 char matchBuf[MAX_LINELEN]; 736 int find_type; 737 int recalc_pos; 738 739 *lastWasTab = TRUE; /* flop trigger */ 740 741 /* Make a local copy of the string -- up 742 * to the position of the cursor */ 743 tmp = strncpy(matchBuf, command_ps, cursor); 744 tmp[cursor] = '\0'; 745 746 find_type = find_match(matchBuf, &recalc_pos); 747 748 /* Free up any memory already allocated */ 749 free_tab_completion_data(); 750 751 #if ENABLE_FEATURE_USERNAME_COMPLETION 752 /* If the word starts with `~' and there is no slash in the word, 753 * then try completing this word as a username. */ 754 if (state->flags & USERNAME_COMPLETION) 755 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0) 756 username_tab_completion(matchBuf, NULL); 757 #endif 758 /* Try to match any executable in our path and everything 759 * in the current working directory */ 760 if (!matches) 761 exe_n_cwd_tab_completion(matchBuf, find_type); 762 /* Sort, then remove any duplicates found */ 763 if (matches) { 764 int i, n = 0; 765 qsort(matches, num_matches, sizeof(char*), match_compare); 766 for (i = 0; i < num_matches - 1; ++i) { 767 if (matches[i] && matches[i+1]) { /* paranoia */ 768 if (strcmp(matches[i], matches[i+1]) == 0) { 769 free(matches[i]); 770 matches[i] = NULL; /* paranoia */ 771 } else { 772 matches[n++] = matches[i]; 773 } 774 } 775 } 776 matches[n] = matches[i]; 777 num_matches = n + 1; 778 } 779 /* Did we find exactly one match? */ 780 if (!matches || num_matches > 1) { 781 beep(); 782 if (!matches) 783 return; /* not found */ 784 /* find minimal match */ 785 // ash: yet another failure in trying to achieve "we don't die on OOM" 786 tmp1 = xstrdup(matches[0]); 787 for (tmp = tmp1; *tmp; tmp++) 788 for (len_found = 1; len_found < num_matches; len_found++) 789 if (matches[len_found][(tmp - tmp1)] != *tmp) { 790 *tmp = '\0'; 791 break; 792 } 793 if (*tmp1 == '\0') { /* have unique */ 794 free(tmp1); 795 return; 796 } 797 tmp = add_quote_for_spec_chars(tmp1); 798 free(tmp1); 799 } else { /* one match */ 800 tmp = add_quote_for_spec_chars(matches[0]); 801 /* for next completion current found */ 802 *lastWasTab = FALSE; 803 804 len_found = strlen(tmp); 805 if (tmp[len_found-1] != '/') { 806 tmp[len_found] = ' '; 807 tmp[len_found+1] = '\0'; 808 } 809 } 810 len_found = strlen(tmp); 811 /* have space to placed match? */ 812 if ((len_found - strlen(matchBuf) + command_len) < MAX_LINELEN) { 813 /* before word for match */ 814 command_ps[cursor - recalc_pos] = 0; 815 /* save tail line */ 816 strcpy(matchBuf, command_ps + cursor); 817 /* add match */ 818 strcat(command_ps, tmp); 819 /* add tail */ 820 strcat(command_ps, matchBuf); 821 /* back to begin word for match */ 822 input_backward(recalc_pos); 823 /* new pos */ 824 recalc_pos = cursor + len_found; 825 /* new len */ 826 command_len = strlen(command_ps); 827 /* write out the matched command */ 828 redraw(cmdedit_y, command_len - recalc_pos); 829 } 830 free(tmp); 831 } else { 832 /* Ok -- the last char was a TAB. Since they 833 * just hit TAB again, print a list of all the 834 * available choices... */ 835 if (matches && num_matches > 0) { 836 int sav_cursor = cursor; /* change goto_new_line() */ 837 838 /* Go to the next line */ 1059 if (*lastWasTab) { 1060 /* The last char was a TAB too. 1061 * Print a list of all the available choices. 1062 */ 1063 if (num_matches > 0) { 1064 /* cursor will be changed by goto_new_line() */ 1065 int sav_cursor = cursor; 839 1066 goto_new_line(); 840 1067 showfiles(); 841 1068 redraw(0, command_len - sav_cursor); 842 1069 } 843 } 844 } 845 846 #else 847 #define input_tab(a) ((void)0) 848 #endif /* FEATURE_COMMAND_TAB_COMPLETION */ 1070 return; 1071 } 1072 1073 *lastWasTab = 1; 1074 chosen_match = NULL; 1075 1076 /* Make a local copy of the string up to the position of the cursor. 1077 * build_match_prefix will expand it into int16_t's, need to allocate 1078 * twice as much as the string_len+1. 1079 * (we then also (ab)use this extra space later - see (**)) 1080 */ 1081 match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t)); 1082 # if !ENABLE_UNICODE_SUPPORT 1083 save_string(match_buf, cursor + 1); /* +1 for NUL */ 1084 # else 1085 { 1086 CHAR_T wc = command_ps[cursor]; 1087 command_ps[cursor] = BB_NUL; 1088 save_string(match_buf, MAX_LINELEN); 1089 command_ps[cursor] = wc; 1090 cursor_mb = strlen(match_buf); 1091 } 1092 # endif 1093 find_type = build_match_prefix(match_buf); 1094 1095 /* Free up any memory already allocated */ 1096 free_tab_completion_data(); 1097 1098 # if ENABLE_FEATURE_USERNAME_COMPLETION 1099 /* If the word starts with ~ and there is no slash in the word, 1100 * then try completing this word as a username. */ 1101 if (state->flags & USERNAME_COMPLETION) 1102 if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL) 1103 match_pfx_len = complete_username(match_buf); 1104 # endif 1105 /* If complete_username() did not match, 1106 * try to match a command in $PATH, or a directory, or a file */ 1107 if (!matches) 1108 match_pfx_len = complete_cmd_dir_file(match_buf, find_type); 1109 1110 /* Account for backslashes which will be inserted 1111 * by quote_special_chars() later */ 1112 { 1113 const char *e = match_buf + strlen(match_buf); 1114 const char *s = e - match_pfx_len; 1115 while (s < e) 1116 if (is_special_char(*s++)) 1117 match_pfx_len++; 1118 } 1119 1120 /* Remove duplicates */ 1121 if (matches) { 1122 unsigned i, n = 0; 1123 qsort_string_vector(matches, num_matches); 1124 for (i = 0; i < num_matches - 1; ++i) { 1125 //if (matches[i] && matches[i+1]) { /* paranoia */ 1126 if (strcmp(matches[i], matches[i+1]) == 0) { 1127 free(matches[i]); 1128 //matches[i] = NULL; /* paranoia */ 1129 } else { 1130 matches[n++] = matches[i]; 1131 } 1132 //} 1133 } 1134 matches[n++] = matches[i]; 1135 num_matches = n; 1136 } 1137 1138 /* Did we find exactly one match? */ 1139 if (num_matches != 1) { /* no */ 1140 char *cp; 1141 beep(); 1142 if (!matches) 1143 goto ret; /* no matches at all */ 1144 /* Find common prefix */ 1145 chosen_match = xstrdup(matches[0]); 1146 for (cp = chosen_match; *cp; cp++) { 1147 unsigned n; 1148 for (n = 1; n < num_matches; n++) { 1149 if (matches[n][cp - chosen_match] != *cp) { 1150 goto stop; 1151 } 1152 } 1153 } 1154 stop: 1155 if (cp == chosen_match) { /* have unique prefix? */ 1156 goto ret; /* no */ 1157 } 1158 *cp = '\0'; 1159 cp = quote_special_chars(chosen_match); 1160 free(chosen_match); 1161 chosen_match = cp; 1162 len_found = strlen(chosen_match); 1163 } else { /* exactly one match */ 1164 /* Next <tab> is not a double-tab */ 1165 *lastWasTab = 0; 1166 1167 chosen_match = quote_special_chars(matches[0]); 1168 len_found = strlen(chosen_match); 1169 if (chosen_match[len_found-1] != '/') { 1170 chosen_match[len_found] = ' '; 1171 chosen_match[++len_found] = '\0'; 1172 } 1173 } 1174 1175 # if !ENABLE_UNICODE_SUPPORT 1176 /* Have space to place the match? */ 1177 /* The result consists of three parts with these lengths: */ 1178 /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */ 1179 /* it simplifies into: */ 1180 if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) { 1181 int pos; 1182 /* save tail */ 1183 strcpy(match_buf, &command_ps[cursor]); 1184 /* add match and tail */ 1185 sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf); 1186 command_len = strlen(command_ps); 1187 /* new pos */ 1188 pos = cursor + len_found - match_pfx_len; 1189 /* write out the matched command */ 1190 redraw(cmdedit_y, command_len - pos); 1191 } 1192 # else 1193 { 1194 /* Use 2nd half of match_buf as scratch space - see (**) */ 1195 char *command = match_buf + MAX_LINELEN; 1196 int len = save_string(command, MAX_LINELEN); 1197 /* Have space to place the match? */ 1198 /* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */ 1199 if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) { 1200 int pos; 1201 /* save tail */ 1202 strcpy(match_buf, &command[cursor_mb]); 1203 /* where do we want to have cursor after all? */ 1204 strcpy(&command[cursor_mb], chosen_match + match_pfx_len); 1205 len = load_string(command, S.maxsize); 1206 /* add match and tail */ 1207 sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf); 1208 command_len = load_string(command, S.maxsize); 1209 /* write out the matched command */ 1210 /* paranoia: load_string can return 0 on conv error, 1211 * prevent passing pos = (0 - 12) to redraw */ 1212 pos = command_len - len; 1213 redraw(cmdedit_y, pos >= 0 ? pos : 0); 1214 } 1215 } 1216 # endif 1217 ret: 1218 free(chosen_match); 1219 free(match_buf); 1220 } 1221 1222 #endif /* FEATURE_TAB_COMPLETION */ 1223 1224 1225 line_input_t* FAST_FUNC new_line_input_t(int flags) 1226 { 1227 line_input_t *n = xzalloc(sizeof(*n)); 1228 n->flags = flags; 1229 return n; 1230 } 849 1231 850 1232 851 1233 #if MAX_HISTORY > 0 852 1234 1235 static void save_command_ps_at_cur_history(void) 1236 { 1237 if (command_ps[0] != BB_NUL) { 1238 int cur = state->cur_history; 1239 free(state->history[cur]); 1240 1241 # if ENABLE_UNICODE_SUPPORT 1242 { 1243 char tbuf[MAX_LINELEN]; 1244 save_string(tbuf, sizeof(tbuf)); 1245 state->history[cur] = xstrdup(tbuf); 1246 } 1247 # else 1248 state->history[cur] = xstrdup(command_ps); 1249 # endif 1250 } 1251 } 1252 853 1253 /* state->flags is already checked to be nonzero */ 854 static void get_previous_history(void) 855 { 856 if (command_ps[0] != '\0' || state->history[state->cur_history] == NULL) { 857 free(state->history[state->cur_history]); 858 state->history[state->cur_history] = xstrdup(command_ps); 859 } 860 state->cur_history--; 861 } 862 863 static int get_next_history(void) 864 { 865 if (state->flags & DO_HISTORY) { 866 int ch = state->cur_history; 867 if (ch < state->cnt_history) { 868 get_previous_history(); /* save the current history line */ 869 state->cur_history = ch + 1; 870 return state->cur_history; 871 } 1254 static int get_previous_history(void) 1255 { 1256 if ((state->flags & DO_HISTORY) && state->cur_history) { 1257 save_command_ps_at_cur_history(); 1258 state->cur_history--; 1259 return 1; 872 1260 } 873 1261 beep(); … … 875 1263 } 876 1264 877 #if ENABLE_FEATURE_EDITING_SAVEHISTORY 1265 static int get_next_history(void) 1266 { 1267 if (state->flags & DO_HISTORY) { 1268 if (state->cur_history < state->cnt_history) { 1269 save_command_ps_at_cur_history(); /* save the current history line */ 1270 return ++state->cur_history; 1271 } 1272 } 1273 beep(); 1274 return 0; 1275 } 1276 1277 # if ENABLE_FEATURE_EDITING_SAVEHISTORY 1278 /* We try to ensure that concurrent additions to the history 1279 * do not overwrite each other. 1280 * Otherwise shell users get unhappy. 1281 * 1282 * History file is trimmed lazily, when it grows several times longer 1283 * than configured MAX_HISTORY lines. 1284 */ 1285 1286 static void free_line_input_t(line_input_t *n) 1287 { 1288 int i = n->cnt_history; 1289 while (i > 0) 1290 free(n->history[--i]); 1291 free(n); 1292 } 1293 878 1294 /* state->flags is already checked to be nonzero */ 879 static void load_history(const char *fromfile) 880 { 1295 static void load_history(line_input_t *st_parm) 1296 { 1297 char *temp_h[MAX_HISTORY]; 1298 char *line; 881 1299 FILE *fp; 882 int hi; 883 884 /* cleanup old */ 885 for (hi = state->cnt_history; hi > 0;) { 886 hi--; 887 free(state->history[hi]); 888 } 889 890 fp = fopen(fromfile, "r"); 1300 unsigned idx, i, line_len; 1301 1302 /* NB: do not trash old history if file can't be opened */ 1303 1304 fp = fopen_for_read(st_parm->hist_file); 891 1305 if (fp) { 892 for (hi = 0; hi < MAX_HISTORY;) { 893 char *hl = xmalloc_getline(fp); 894 int l; 895 896 if (!hl) 897 break; 898 l = strlen(hl); 899 if (l >= MAX_LINELEN) 900 hl[MAX_LINELEN-1] = '\0'; 901 if (l == 0 || hl[0] == ' ') { 902 free(hl); 1306 /* clean up old history */ 1307 for (idx = st_parm->cnt_history; idx > 0;) { 1308 idx--; 1309 free(st_parm->history[idx]); 1310 st_parm->history[idx] = NULL; 1311 } 1312 1313 /* fill temp_h[], retaining only last MAX_HISTORY lines */ 1314 memset(temp_h, 0, sizeof(temp_h)); 1315 st_parm->cnt_history_in_file = idx = 0; 1316 while ((line = xmalloc_fgetline(fp)) != NULL) { 1317 if (line[0] == '\0') { 1318 free(line); 903 1319 continue; 904 1320 } 905 state->history[hi++] = hl; 1321 free(temp_h[idx]); 1322 temp_h[idx] = line; 1323 st_parm->cnt_history_in_file++; 1324 idx++; 1325 if (idx == MAX_HISTORY) 1326 idx = 0; 906 1327 } 907 1328 fclose(fp); 908 } 909 state->cur_history = state->cnt_history = hi; 1329 1330 /* find first non-NULL temp_h[], if any */ 1331 if (st_parm->cnt_history_in_file) { 1332 while (temp_h[idx] == NULL) { 1333 idx++; 1334 if (idx == MAX_HISTORY) 1335 idx = 0; 1336 } 1337 } 1338 1339 /* copy temp_h[] to st_parm->history[] */ 1340 for (i = 0; i < MAX_HISTORY;) { 1341 line = temp_h[idx]; 1342 if (!line) 1343 break; 1344 idx++; 1345 if (idx == MAX_HISTORY) 1346 idx = 0; 1347 line_len = strlen(line); 1348 if (line_len >= MAX_LINELEN) 1349 line[MAX_LINELEN-1] = '\0'; 1350 st_parm->history[i++] = line; 1351 } 1352 st_parm->cnt_history = i; 1353 } 910 1354 } 911 1355 912 1356 /* state->flags is already checked to be nonzero */ 913 static void save_history(const char *tofile) 914 { 915 FILE *fp; 916 917 fp = fopen(tofile, "w"); 918 if (fp) { 919 int i; 920 921 for (i = 0; i < state->cnt_history; i++) { 922 fprintf(fp, "%s\n", state->history[i]); 923 } 924 fclose(fp); 925 } 926 } 927 #else 928 #define load_history(a) ((void)0) 929 #define save_history(a) ((void)0) 930 #endif /* FEATURE_COMMAND_SAVEHISTORY */ 931 932 static void remember_in_history(const char *str) 1357 static void save_history(char *str) 1358 { 1359 int fd; 1360 int len, len2; 1361 1362 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600); 1363 if (fd < 0) 1364 return; 1365 xlseek(fd, 0, SEEK_END); /* paranoia */ 1366 len = strlen(str); 1367 str[len] = '\n'; /* we (try to) do atomic write */ 1368 len2 = full_write(fd, str, len + 1); 1369 str[len] = '\0'; 1370 close(fd); 1371 if (len2 != len + 1) 1372 return; /* "wtf?" */ 1373 1374 /* did we write so much that history file needs trimming? */ 1375 state->cnt_history_in_file++; 1376 if (state->cnt_history_in_file > MAX_HISTORY * 4) { 1377 char *new_name; 1378 line_input_t *st_temp; 1379 1380 /* we may have concurrently written entries from others. 1381 * load them */ 1382 st_temp = new_line_input_t(state->flags); 1383 st_temp->hist_file = state->hist_file; 1384 load_history(st_temp); 1385 1386 /* write out temp file and replace hist_file atomically */ 1387 new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid()); 1388 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_TRUNC, 0600); 1389 if (fd >= 0) { 1390 FILE *fp; 1391 int i; 1392 1393 fp = xfdopen_for_write(fd); 1394 for (i = 0; i < st_temp->cnt_history; i++) 1395 fprintf(fp, "%s\n", st_temp->history[i]); 1396 fclose(fp); 1397 if (rename(new_name, state->hist_file) == 0) 1398 state->cnt_history_in_file = st_temp->cnt_history; 1399 } 1400 free(new_name); 1401 free_line_input_t(st_temp); 1402 } 1403 } 1404 # else 1405 # define load_history(a) ((void)0) 1406 # define save_history(a) ((void)0) 1407 # endif /* FEATURE_COMMAND_SAVEHISTORY */ 1408 1409 static void remember_in_history(char *str) 933 1410 { 934 1411 int i; … … 936 1413 if (!(state->flags & DO_HISTORY)) 937 1414 return; 938 1415 if (str[0] == '\0') 1416 return; 939 1417 i = state->cnt_history; 940 free(state->history[MAX_HISTORY]); 941 state->history[MAX_HISTORY] = NULL; 942 /* After max history, remove the oldest command */ 1418 /* Don't save dupes */ 1419 if (i && strcmp(state->history[i-1], str) == 0) 1420 return; 1421 1422 free(state->history[MAX_HISTORY]); /* redundant, paranoia */ 1423 state->history[MAX_HISTORY] = NULL; /* redundant, paranoia */ 1424 1425 /* If history[] is full, remove the oldest command */ 1426 /* we need to keep history[MAX_HISTORY] empty, hence >=, not > */ 943 1427 if (i >= MAX_HISTORY) { 944 1428 free(state->history[0]); 945 1429 for (i = 0; i < MAX_HISTORY-1; i++) 946 1430 state->history[i] = state->history[i+1]; 947 }948 // Maybe "if (!i || strcmp(history[i-1], command) != 0) ..." 949 // (i.e. do not save dups?) 1431 /* i == MAX_HISTORY-1 */ 1432 } 1433 /* i <= MAX_HISTORY-1 */ 950 1434 state->history[i++] = xstrdup(str); 1435 /* i <= MAX_HISTORY */ 951 1436 state->cur_history = i; 952 1437 state->cnt_history = i; 953 # ifENABLE_FEATURE_EDITING_SAVEHISTORY1438 # if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY 954 1439 if ((state->flags & SAVE_HISTORY) && state->hist_file) 955 save_history(st ate->hist_file);956 # endif957 USE_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)1440 save_history(str); 1441 # endif 1442 IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;) 958 1443 } 959 1444 960 1445 #else /* MAX_HISTORY == 0 */ 961 # define remember_in_history(a) ((void)0)1446 # define remember_in_history(a) ((void)0) 962 1447 #endif /* MAX_HISTORY */ 963 1448 964 1449 1450 #if ENABLE_FEATURE_EDITING_VI 965 1451 /* 966 * This function is used to grab a character buffer967 * from the input file descriptor and allows you to968 * a string with full command editing (sort of like969 * a mini readline).970 *971 * The following standard commands are not implemented:972 * ESC-b -- Move back one word973 * ESC-f -- Move forward one word974 * ESC-d -- Delete back one word975 * ESC-h -- Delete forward one word976 * CTL-t -- Transpose two characters977 *978 * Minimalist vi-style command line editing available if configured.979 1452 * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us> 980 1453 */ 981 982 #if ENABLE_FEATURE_EDITING_VI983 1454 static void 984 vi_Word_motion(char *command, int eat) 985 { 986 while (cursor < command_len && !isspace(command[cursor])) 1455 vi_Word_motion(int eat) 1456 { 1457 CHAR_T *command = command_ps; 1458 1459 while (cursor < command_len && !BB_isspace(command[cursor])) 987 1460 input_forward(); 988 if (eat) while (cursor < command_len && isspace(command[cursor]))1461 if (eat) while (cursor < command_len && BB_isspace(command[cursor])) 989 1462 input_forward(); 990 1463 } 991 1464 992 1465 static void 993 vi_word_motion(char *command, int eat) 994 { 995 if (isalnum(command[cursor]) || command[cursor] == '_') { 1466 vi_word_motion(int eat) 1467 { 1468 CHAR_T *command = command_ps; 1469 1470 if (BB_isalnum(command[cursor]) || command[cursor] == '_') { 996 1471 while (cursor < command_len 997 && (isalnum(command[cursor+1]) || command[cursor+1] == '_')) 1472 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_') 1473 ) { 998 1474 input_forward(); 999 } else if (ispunct(command[cursor])) { 1000 while (cursor < command_len && ispunct(command[cursor+1])) 1475 } 1476 } else if (BB_ispunct(command[cursor])) { 1477 while (cursor < command_len && BB_ispunct(command[cursor+1])) 1001 1478 input_forward(); 1002 1479 } … … 1005 1482 input_forward(); 1006 1483 1007 if (eat && cursor < command_len && isspace(command[cursor]))1008 while (cursor < command_len && isspace(command[cursor]))1484 if (eat) { 1485 while (cursor < command_len && BB_isspace(command[cursor])) 1009 1486 input_forward(); 1487 } 1010 1488 } 1011 1489 1012 1490 static void 1013 vi_End_motion(char *command) 1014 { 1491 vi_End_motion(void) 1492 { 1493 CHAR_T *command = command_ps; 1494 1015 1495 input_forward(); 1016 while (cursor < command_len && isspace(command[cursor]))1496 while (cursor < command_len && BB_isspace(command[cursor])) 1017 1497 input_forward(); 1018 while (cursor < command_len-1 && ! isspace(command[cursor+1]))1498 while (cursor < command_len-1 && !BB_isspace(command[cursor+1])) 1019 1499 input_forward(); 1020 1500 } 1021 1501 1022 1502 static void 1023 vi_end_motion(char *command) 1024 { 1503 vi_end_motion(void) 1504 { 1505 CHAR_T *command = command_ps; 1506 1025 1507 if (cursor >= command_len-1) 1026 1508 return; 1027 1509 input_forward(); 1028 while (cursor < command_len-1 && isspace(command[cursor]))1510 while (cursor < command_len-1 && BB_isspace(command[cursor])) 1029 1511 input_forward(); 1030 1512 if (cursor >= command_len-1) 1031 1513 return; 1032 if ( isalnum(command[cursor]) || command[cursor] == '_') {1514 if (BB_isalnum(command[cursor]) || command[cursor] == '_') { 1033 1515 while (cursor < command_len-1 1034 && ( isalnum(command[cursor+1]) || command[cursor+1] == '_')1516 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_') 1035 1517 ) { 1036 1518 input_forward(); 1037 1519 } 1038 } else if ( ispunct(command[cursor])) {1039 while (cursor < command_len-1 && ispunct(command[cursor+1]))1520 } else if (BB_ispunct(command[cursor])) { 1521 while (cursor < command_len-1 && BB_ispunct(command[cursor+1])) 1040 1522 input_forward(); 1041 1523 } … … 1043 1525 1044 1526 static void 1045 vi_Back_motion(char *command) 1046 { 1047 while (cursor > 0 && isspace(command[cursor-1])) 1527 vi_Back_motion(void) 1528 { 1529 CHAR_T *command = command_ps; 1530 1531 while (cursor > 0 && BB_isspace(command[cursor-1])) 1048 1532 input_backward(1); 1049 while (cursor > 0 && ! isspace(command[cursor-1]))1533 while (cursor > 0 && !BB_isspace(command[cursor-1])) 1050 1534 input_backward(1); 1051 1535 } 1052 1536 1053 1537 static void 1054 vi_back_motion(char *command) 1055 { 1538 vi_back_motion(void) 1539 { 1540 CHAR_T *command = command_ps; 1541 1056 1542 if (cursor <= 0) 1057 1543 return; 1058 1544 input_backward(1); 1059 while (cursor > 0 && isspace(command[cursor]))1545 while (cursor > 0 && BB_isspace(command[cursor])) 1060 1546 input_backward(1); 1061 1547 if (cursor <= 0) 1062 1548 return; 1063 if ( isalnum(command[cursor]) || command[cursor] == '_') {1549 if (BB_isalnum(command[cursor]) || command[cursor] == '_') { 1064 1550 while (cursor > 0 1065 && ( isalnum(command[cursor-1]) || command[cursor-1] == '_')1551 && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_') 1066 1552 ) { 1067 1553 input_backward(1); 1068 1554 } 1069 } else if ( ispunct(command[cursor])) {1070 while (cursor > 0 && ispunct(command[cursor-1]))1555 } else if (BB_ispunct(command[cursor])) { 1556 while (cursor > 0 && BB_ispunct(command[cursor-1])) 1071 1557 input_backward(1); 1072 1558 } 1073 1559 } 1074 1560 #endif 1561 1562 /* Modelled after bash 4.0 behavior of Ctrl-<arrow> */ 1563 static void ctrl_left(void) 1564 { 1565 CHAR_T *command = command_ps; 1566 1567 while (1) { 1568 CHAR_T c; 1569 1570 input_backward(1); 1571 if (cursor == 0) 1572 break; 1573 c = command[cursor]; 1574 if (c != ' ' && !BB_ispunct(c)) { 1575 /* we reached a "word" delimited by spaces/punct. 1576 * go to its beginning */ 1577 while (1) { 1578 c = command[cursor - 1]; 1579 if (c == ' ' || BB_ispunct(c)) 1580 break; 1581 input_backward(1); 1582 if (cursor == 0) 1583 break; 1584 } 1585 break; 1586 } 1587 } 1588 } 1589 static void ctrl_right(void) 1590 { 1591 CHAR_T *command = command_ps; 1592 1593 while (1) { 1594 CHAR_T c; 1595 1596 c = command[cursor]; 1597 if (c == BB_NUL) 1598 break; 1599 if (c != ' ' && !BB_ispunct(c)) { 1600 /* we reached a "word" delimited by spaces/punct. 1601 * go to its end + 1 */ 1602 while (1) { 1603 input_forward(); 1604 c = command[cursor]; 1605 if (c == BB_NUL || c == ' ' || BB_ispunct(c)) 1606 break; 1607 } 1608 break; 1609 } 1610 input_forward(); 1611 } 1612 } 1075 1613 1076 1614 … … 1079 1617 */ 1080 1618 1619 #if ENABLE_FEATURE_EDITING_ASK_TERMINAL 1620 static void ask_terminal(void) 1621 { 1622 /* Ask terminal where is the cursor now. 1623 * lineedit_read_key handles response and corrects 1624 * our idea of current cursor position. 1625 * Testcase: run "echo -n long_line_long_line_long_line", 1626 * then type in a long, wrapping command and try to 1627 * delete it using backspace key. 1628 * Note: we print it _after_ prompt, because 1629 * prompt may contain CR. Example: PS1='\[\r\n\]\w ' 1630 */ 1631 /* Problem: if there is buffered input on stdin, 1632 * the response will be delivered later, 1633 * possibly to an unsuspecting application. 1634 * Testcase: "sleep 1; busybox ash" + press and hold [Enter]. 1635 * Result: 1636 * ~/srcdevel/bbox/fix/busybox.t4 # 1637 * ~/srcdevel/bbox/fix/busybox.t4 # 1638 * ^[[59;34~/srcdevel/bbox/fix/busybox.t4 # <-- garbage 1639 * ~/srcdevel/bbox/fix/busybox.t4 # 1640 * 1641 * Checking for input with poll only makes the race narrower, 1642 * I still can trigger it. Strace: 1643 * 1644 * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33 1645 * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout) <-- no input exists 1646 * write(1, "\33[6n", 4) = 4 <-- send the ESC sequence, quick! 1647 * poll([{fd=0, events=POLLIN}], 1, 4294967295) = 1 ([{fd=0, revents=POLLIN}]) 1648 * read(0, "\n", 1) = 1 <-- oh crap, user's input got in first 1649 */ 1650 struct pollfd pfd; 1651 1652 pfd.fd = STDIN_FILENO; 1653 pfd.events = POLLIN; 1654 if (safe_poll(&pfd, 1, 0) == 0) { 1655 S.sent_ESC_br6n = 1; 1656 fputs(ESC"[6n", stdout); 1657 fflush_all(); /* make terminal see it ASAP! */ 1658 } 1659 } 1660 #else 1661 #define ask_terminal() ((void)0) 1662 #endif 1663 1081 1664 #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT 1082 static void parse_ prompt(const char *prmt_ptr)1665 static void parse_and_put_prompt(const char *prmt_ptr) 1083 1666 { 1084 1667 cmdedit_prompt = prmt_ptr; … … 1087 1670 } 1088 1671 #else 1089 static void parse_ prompt(const char *prmt_ptr)1672 static void parse_and_put_prompt(const char *prmt_ptr) 1090 1673 { 1091 1674 int prmt_len = 0; … … 1093 1676 char flg_not_length = '['; 1094 1677 char *prmt_mem_ptr = xzalloc(1); 1095 char *pwd_buf = xrealloc_getcwd_or_warn(NULL); 1096 char buf2[PATH_MAX + 1]; 1097 char buf[2]; 1678 char *cwd_buf = xrealloc_getcwd_or_warn(NULL); 1679 char cbuf[2]; 1098 1680 char c; 1099 1681 char *pbuf; … … 1101 1683 cmdedit_prmt_len = 0; 1102 1684 1103 if (!pwd_buf) { 1104 pwd_buf = (char *)bb_msg_unknown; 1105 } 1685 if (!cwd_buf) { 1686 cwd_buf = (char *)bb_msg_unknown; 1687 } 1688 1689 cbuf[1] = '\0'; /* never changes */ 1106 1690 1107 1691 while (*prmt_ptr) { 1108 pbuf = buf; 1109 pbuf[1] = 0; 1692 char *free_me = NULL; 1693 1694 pbuf = cbuf; 1110 1695 c = *prmt_ptr++; 1111 1696 if (c == '\\') { … … 1115 1700 c = bb_process_escape_sequence(&prmt_ptr); 1116 1701 if (prmt_ptr == cp) { 1117 if (*cp == 0)1702 if (*cp == '\0') 1118 1703 break; 1119 1704 c = *prmt_ptr++; 1705 1120 1706 switch (c) { 1121 # if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR1707 # if ENABLE_USERNAME_OR_HOMEDIR 1122 1708 case 'u': 1123 1709 pbuf = user_buf ? user_buf : (char*)""; 1124 1710 break; 1125 # endif1711 # endif 1126 1712 case 'h': 1127 pbuf = hostname_buf; 1128 if (!pbuf) { 1129 pbuf = xzalloc(256); 1130 if (gethostname(pbuf, 255) < 0) { 1131 strcpy(pbuf, "?"); 1132 } else { 1133 char *s = strchr(pbuf, '.'); 1134 if (s) 1135 *s = '\0'; 1136 } 1137 hostname_buf = pbuf; 1138 } 1713 pbuf = free_me = safe_gethostname(); 1714 *strchrnul(pbuf, '.') = '\0'; 1139 1715 break; 1140 1716 case '$': 1141 1717 c = (geteuid() == 0 ? '#' : '$'); 1142 1718 break; 1143 # if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR1719 # if ENABLE_USERNAME_OR_HOMEDIR 1144 1720 case 'w': 1145 pbuf = pwd_buf; 1721 /* /home/user[/something] -> ~[/something] */ 1722 pbuf = cwd_buf; 1146 1723 l = strlen(home_pwd_buf); 1147 1724 if (l != 0 1148 && strncmp(home_pwd_buf, pbuf, l) == 01149 && ( pbuf[l]=='/' || pbuf[l]=='\0')1150 && strlen( pwd_buf+l)<PATH_MAX1725 && strncmp(home_pwd_buf, cwd_buf, l) == 0 1726 && (cwd_buf[l]=='/' || cwd_buf[l]=='\0') 1727 && strlen(cwd_buf + l) < PATH_MAX 1151 1728 ) { 1152 pbuf = buf2; 1153 *pbuf = '~'; 1154 strcpy(pbuf+1, pwd_buf+l); 1729 pbuf = free_me = xasprintf("~%s", cwd_buf + l); 1155 1730 } 1156 1731 break; 1157 # endif1732 # endif 1158 1733 case 'W': 1159 pbuf = pwd_buf;1734 pbuf = cwd_buf; 1160 1735 cp = strrchr(pbuf, '/'); 1161 1736 if (cp != NULL && cp != pbuf) … … 1163 1738 break; 1164 1739 case '!': 1165 pbuf = buf2; 1166 snprintf(buf2, sizeof(buf2), "%d", num_ok_lines); 1740 pbuf = free_me = xasprintf("%d", num_ok_lines); 1167 1741 break; 1168 1742 case 'e': case 'E': /* \e \E = \033 */ 1169 1743 c = '\033'; 1170 1744 break; 1171 case 'x': case 'X': 1745 case 'x': case 'X': { 1746 char buf2[4]; 1172 1747 for (l = 0; l < 3;) { 1173 inth;1748 unsigned h; 1174 1749 buf2[l++] = *prmt_ptr; 1175 buf2[l] = 0;1176 h = strto l(buf2, &pbuf, 16);1750 buf2[l] = '\0'; 1751 h = strtoul(buf2, &pbuf, 16); 1177 1752 if (h > UCHAR_MAX || (pbuf - buf2) < l) { 1178 l--;1753 buf2[--l] = '\0'; 1179 1754 break; 1180 1755 } 1181 1756 prmt_ptr++; 1182 1757 } 1183 buf2[l] = 0; 1184 c = (char)strtol(buf2, NULL, 16); 1758 c = (char)strtoul(buf2, NULL, 16); 1185 1759 if (c == 0) 1186 1760 c = '?'; 1187 pbuf = buf;1761 pbuf = cbuf; 1188 1762 break; 1763 } 1189 1764 case '[': case ']': 1190 1765 if (c == flg_not_length) { 1191 flg_not_length = flg_not_length == '[' ? ']' : '[';1766 flg_not_length = (flg_not_length == '[' ? ']' : '['); 1192 1767 continue; 1193 1768 } 1194 1769 break; 1195 } 1196 } 1197 } 1198 if (pbuf == buf) 1199 *pbuf = c; 1770 } /* switch */ 1771 } /* if */ 1772 } /* if */ 1773 cbuf[0] = c; 1200 1774 cur_prmt_len = strlen(pbuf); 1201 1775 prmt_len += cur_prmt_len; … … 1203 1777 cmdedit_prmt_len += cur_prmt_len; 1204 1778 prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf); 1205 } 1206 if (pwd_buf != (char *)bb_msg_unknown) 1207 free(pwd_buf); 1779 free(free_me); 1780 } /* while */ 1781 1782 if (cwd_buf != (char *)bb_msg_unknown) 1783 free(cwd_buf); 1208 1784 cmdedit_prompt = prmt_mem_ptr; 1209 1785 put_prompt(); 1210 1786 } 1211 1787 #endif 1212 1213 #define setTermSettings(fd, argp) tcsetattr(fd, TCSANOW, argp)1214 #define getTermSettings(fd, argp) tcgetattr(fd, argp);1215 1216 static sighandler_t previous_SIGWINCH_handler;1217 1788 1218 1789 static void cmdedit_setwidth(unsigned w, int redraw_flg) … … 1224 1795 /* redraw */ 1225 1796 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor); 1226 fflush (stdout);1797 fflush_all(); 1227 1798 } 1228 1799 } … … 1230 1801 static void win_changed(int nsig) 1231 1802 { 1232 int width; 1803 int sv_errno = errno; 1804 unsigned width; 1233 1805 get_terminal_width_height(0, &width, NULL); 1234 1806 cmdedit_setwidth(width, nsig /* - just a yes/no flag */); 1235 1807 if (nsig == SIGWINCH) 1236 1808 signal(SIGWINCH, win_changed); /* rearm ourself */ 1237 } 1238 1239 /* 1240 * The emacs and vi modes share much of the code in the big 1241 * command loop. Commands entered when in vi's command mode (aka 1242 * "escape mode") get an extra bit added to distinguish them -- 1243 * this keeps them from being self-inserted. This clutters the 1244 * big switch a bit, but keeps all the code in one place. 1245 */ 1246 1247 #define vbit 0x100 1809 errno = sv_errno; 1810 } 1811 1812 static int lineedit_read_key(char *read_key_buffer) 1813 { 1814 int64_t ic; 1815 int timeout = -1; 1816 #if ENABLE_UNICODE_SUPPORT 1817 char unicode_buf[MB_CUR_MAX + 1]; 1818 int unicode_idx = 0; 1819 #endif 1820 1821 while (1) { 1822 /* Wait for input. TIMEOUT = -1 makes read_key wait even 1823 * on nonblocking stdin, TIMEOUT = 50 makes sure we won't 1824 * insist on full MB_CUR_MAX buffer to declare input like 1825 * "\xff\n",pause,"ls\n" invalid and thus won't lose "ls". 1826 * 1827 * Note: read_key sets errno to 0 on success. 1828 */ 1829 ic = read_key(STDIN_FILENO, read_key_buffer, timeout); 1830 if (errno) { 1831 #if ENABLE_UNICODE_SUPPORT 1832 if (errno == EAGAIN && unicode_idx != 0) 1833 goto pushback; 1834 #endif 1835 break; 1836 } 1837 1838 #if ENABLE_FEATURE_EDITING_ASK_TERMINAL 1839 if ((int32_t)ic == KEYCODE_CURSOR_POS 1840 && S.sent_ESC_br6n 1841 ) { 1842 S.sent_ESC_br6n = 0; 1843 if (cursor == 0) { /* otherwise it may be bogus */ 1844 int col = ((ic >> 32) & 0x7fff) - 1; 1845 if (col > cmdedit_prmt_len) { 1846 cmdedit_x += (col - cmdedit_prmt_len); 1847 while (cmdedit_x >= cmdedit_termw) { 1848 cmdedit_x -= cmdedit_termw; 1849 cmdedit_y++; 1850 } 1851 } 1852 } 1853 continue; 1854 } 1855 #endif 1856 1857 #if ENABLE_UNICODE_SUPPORT 1858 if (unicode_status == UNICODE_ON) { 1859 wchar_t wc; 1860 1861 if ((int32_t)ic < 0) /* KEYCODE_xxx */ 1862 break; 1863 // TODO: imagine sequence like: 0xff,<left-arrow>: we are currently losing 0xff... 1864 1865 unicode_buf[unicode_idx++] = ic; 1866 unicode_buf[unicode_idx] = '\0'; 1867 if (mbstowcs(&wc, unicode_buf, 1) != 1) { 1868 /* Not (yet?) a valid unicode char */ 1869 if (unicode_idx < MB_CUR_MAX) { 1870 timeout = 50; 1871 continue; 1872 } 1873 pushback: 1874 /* Invalid sequence. Save all "bad bytes" except first */ 1875 read_key_ungets(read_key_buffer, unicode_buf + 1, unicode_idx - 1); 1876 # if !ENABLE_UNICODE_PRESERVE_BROKEN 1877 ic = CONFIG_SUBST_WCHAR; 1878 # else 1879 ic = unicode_mark_raw_byte(unicode_buf[0]); 1880 # endif 1881 } else { 1882 /* Valid unicode char, return its code */ 1883 ic = wc; 1884 } 1885 } 1886 #endif 1887 break; 1888 } 1889 1890 return ic; 1891 } 1892 1893 #if ENABLE_UNICODE_BIDI_SUPPORT 1894 static int isrtl_str(void) 1895 { 1896 int idx = cursor; 1897 1898 while (idx < command_len && unicode_bidi_is_neutral_wchar(command_ps[idx])) 1899 idx++; 1900 return unicode_bidi_isrtl(command_ps[idx]); 1901 } 1902 #else 1903 # define isrtl_str() 0 1904 #endif 1248 1905 1249 1906 /* leave out the "vi-mode"-only case labels if vi editing isn't 1250 1907 * configured. */ 1251 #define vi_case(caselabel) USE_FEATURE_EDITING(case caselabel)1908 #define vi_case(caselabel) IF_FEATURE_EDITING_VI(case caselabel) 1252 1909 1253 1910 /* convert uppercase ascii to equivalent control char, for readability */ … … 1255 1912 #define CTRL(a) ((a) & ~0x40) 1256 1913 1257 /* Returns: 1258 * -1 on read errors or EOF, or on bare Ctrl-D. 1259 * 0 on ctrl-C, 1914 /* maxsize must be >= 2. 1915 * Returns: 1916 * -1 on read errors or EOF, or on bare Ctrl-D, 1917 * 0 on ctrl-C (the line entered is still returned in 'command'), 1260 1918 * >0 length of input string, including terminating '\n' 1261 1919 */ 1262 int read_line_input(const char* prompt, char* command, int maxsize, line_input_t *st) 1263 { 1264 int lastWasTab = FALSE; 1265 unsigned int ic; 1266 unsigned char c; 1920 int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, line_input_t *st) 1921 { 1922 int len; 1923 #if ENABLE_FEATURE_TAB_COMPLETION 1924 smallint lastWasTab = 0; 1925 #endif 1267 1926 smallint break_out = 0; 1268 1927 #if ENABLE_FEATURE_EDITING_VI 1269 1928 smallint vi_cmdmode = 0; 1270 smalluint prevc; 1271 #endif 1929 #endif 1930 struct termios initial_settings; 1931 struct termios new_settings; 1932 char read_key_buffer[KEYCODE_BUFFER_SIZE]; 1933 1934 INIT_S(); 1935 1936 if (tcgetattr(STDIN_FILENO, &initial_settings) < 0 1937 || !(initial_settings.c_lflag & ECHO) 1938 ) { 1939 /* Happens when e.g. stty -echo was run before */ 1940 parse_and_put_prompt(prompt); 1941 /* fflush_all(); - done by parse_and_put_prompt */ 1942 if (fgets(command, maxsize, stdin) == NULL) 1943 len = -1; /* EOF or error */ 1944 else 1945 len = strlen(command); 1946 DEINIT_S(); 1947 return len; 1948 } 1949 1950 init_unicode(); 1272 1951 1273 1952 // FIXME: audit & improve this 1274 1953 if (maxsize > MAX_LINELEN) 1275 1954 maxsize = MAX_LINELEN; 1955 S.maxsize = maxsize; 1276 1956 1277 1957 /* With null flags, no other fields are ever used */ 1278 1958 state = st ? st : (line_input_t*) &const_int_0; 1279 #if ENABLE_FEATURE_EDITING_SAVEHISTORY 1959 #if MAX_HISTORY > 0 1960 # if ENABLE_FEATURE_EDITING_SAVEHISTORY 1280 1961 if ((state->flags & SAVE_HISTORY) && state->hist_file) 1281 load_history(state->hist_file); 1962 if (state->cnt_history == 0) 1963 load_history(state); 1964 # endif 1965 if (state->flags & DO_HISTORY) 1966 state->cur_history = state->cnt_history; 1282 1967 #endif 1283 1968 … … 1285 1970 cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */ 1286 1971 command_len = 0; 1972 #if ENABLE_UNICODE_SUPPORT 1973 command_ps = xzalloc(maxsize * sizeof(command_ps[0])); 1974 #else 1287 1975 command_ps = command; 1288 1976 command[0] = '\0'; 1289 1290 getTermSettings(0, (void *) &initial_settings); 1291 memcpy(&new_settings, &initial_settings, sizeof(new_settings)); 1977 #endif 1978 #define command command_must_not_be_used 1979 1980 new_settings = initial_settings; 1292 1981 new_settings.c_lflag &= ~ICANON; /* unbuffered input */ 1293 1982 /* Turn off echoing and CTRL-C, so we can trap it */ … … 1298 1987 /* Turn off CTRL-C, so we can trap it */ 1299 1988 #ifndef _POSIX_VDISABLE 1300 # define _POSIX_VDISABLE '\0'1989 # define _POSIX_VDISABLE '\0' 1301 1990 #endif 1302 1991 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; 1303 setTermSettings(0, (void *)&new_settings);1992 tcsetattr_stdin_TCSANOW(&new_settings); 1304 1993 1305 1994 /* Now initialize things */ 1306 1995 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); 1307 1996 win_changed(0); /* do initial resizing */ 1308 #if ENABLE_ FEATURE_GETUSERNAME_AND_HOMEDIR1997 #if ENABLE_USERNAME_OR_HOMEDIR 1309 1998 { 1310 1999 struct passwd *entry; … … 1312 2001 entry = getpwuid(geteuid()); 1313 2002 if (entry) { 1314 /* If we enter read_line_input for the Nth time,1315 * they may be already allocated! Need to free. */1316 free(user_buf);1317 if (home_pwd_buf != null_str)1318 free(home_pwd_buf);1319 2003 user_buf = xstrdup(entry->pw_name); 1320 2004 home_pwd_buf = xstrdup(entry->pw_dir); 1321 /* They are not freed on exit (too small to bother) */ 1322 } 1323 } 1324 #endif 1325 /* Print out the command prompt */ 1326 parse_prompt(prompt); 1327 2005 } 2006 } 2007 #endif 2008 2009 #if 0 2010 for (i = 0; i <= MAX_HISTORY; i++) 2011 bb_error_msg("history[%d]:'%s'", i, state->history[i]); 2012 bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history); 2013 #endif 2014 2015 /* Print out the command prompt, optionally ask where cursor is */ 2016 parse_and_put_prompt(prompt); 2017 ask_terminal(); 2018 2019 read_key_buffer[0] = 0; 1328 2020 while (1) { 1329 fflush(stdout); 1330 1331 if (safe_read(0, &c, 1) < 1) { 1332 /* if we can't read input then exit */ 1333 goto prepare_to_die; 1334 } 1335 1336 ic = c; 2021 /* 2022 * The emacs and vi modes share much of the code in the big 2023 * command loop. Commands entered when in vi's command mode 2024 * (aka "escape mode") get an extra bit added to distinguish 2025 * them - this keeps them from being self-inserted. This 2026 * clutters the big switch a bit, but keeps all the code 2027 * in one place. 2028 */ 2029 enum { 2030 VI_CMDMODE_BIT = 0x40000000, 2031 /* 0x80000000 bit flags KEYCODE_xxx */ 2032 }; 2033 int32_t ic, ic_raw; 2034 2035 fflush_all(); 2036 ic = ic_raw = lineedit_read_key(read_key_buffer); 1337 2037 1338 2038 #if ENABLE_FEATURE_EDITING_VI 1339 2039 newdelflag = 1; 1340 if (vi_cmdmode) 1341 ic |= vbit; 1342 #endif 2040 if (vi_cmdmode) { 2041 /* btw, since KEYCODE_xxx are all < 0, this doesn't 2042 * change ic if it contains one of them: */ 2043 ic |= VI_CMDMODE_BIT; 2044 } 2045 #endif 2046 1343 2047 switch (ic) { 1344 2048 case '\n': 1345 2049 case '\r': 1346 vi_case('\n'| vbit:)1347 vi_case('\r'| vbit:)2050 vi_case('\n'|VI_CMDMODE_BIT:) 2051 vi_case('\r'|VI_CMDMODE_BIT:) 1348 2052 /* Enter */ 1349 2053 goto_new_line(); 1350 2054 break_out = 1; 1351 2055 break; 1352 #if ENABLE_FEATURE_EDITING_FANCY_KEYS1353 2056 case CTRL('A'): 1354 vi_case('0'| vbit:)2057 vi_case('0'|VI_CMDMODE_BIT:) 1355 2058 /* Control-a -- Beginning of line */ 1356 2059 input_backward(cursor); 1357 2060 break; 1358 2061 case CTRL('B'): 1359 vi_case('h'|vbit:) 1360 vi_case('\b'|vbit:) 1361 vi_case('\x7f'|vbit:) /* DEL */ 1362 /* Control-b -- Move back one character */ 1363 input_backward(1); 1364 break; 1365 #endif 1366 case CTRL('C'): 1367 vi_case(CTRL('C')|vbit:) 1368 /* Control-c -- stop gathering input */ 1369 goto_new_line(); 1370 command_len = 0; 1371 break_out = -1; /* "do not append '\n'" */ 1372 break; 1373 case CTRL('D'): 1374 /* Control-d -- Delete one character, or exit 1375 * if the len=0 and no chars to delete */ 1376 if (command_len == 0) { 1377 errno = 0; 1378 prepare_to_die: 1379 /* to control stopped jobs */ 1380 break_out = command_len = -1; 1381 break; 1382 } 1383 input_delete(0); 1384 break; 1385 1386 #if ENABLE_FEATURE_EDITING_FANCY_KEYS 2062 vi_case('h'|VI_CMDMODE_BIT:) 2063 vi_case('\b'|VI_CMDMODE_BIT:) /* ^H */ 2064 vi_case('\x7f'|VI_CMDMODE_BIT:) /* DEL */ 2065 input_backward(1); /* Move back one character */ 2066 break; 1387 2067 case CTRL('E'): 1388 vi_case('$'| vbit:)2068 vi_case('$'|VI_CMDMODE_BIT:) 1389 2069 /* Control-e -- End of line */ 1390 input_end();2070 put_till_end_and_adv_cursor(); 1391 2071 break; 1392 2072 case CTRL('F'): 1393 vi_case('l'|vbit:) 1394 vi_case(' '|vbit:) 1395 /* Control-f -- Move forward one character */ 1396 input_forward(); 1397 break; 1398 #endif 1399 1400 case '\b': 2073 vi_case('l'|VI_CMDMODE_BIT:) 2074 vi_case(' '|VI_CMDMODE_BIT:) 2075 input_forward(); /* Move forward one character */ 2076 break; 2077 case '\b': /* ^H */ 1401 2078 case '\x7f': /* DEL */ 1402 /* Control-h and DEL */ 1403 input_backspace(); 1404 break; 1405 2079 if (!isrtl_str()) 2080 input_backspace(); 2081 else 2082 input_delete(0); 2083 break; 2084 case KEYCODE_DELETE: 2085 if (!isrtl_str()) 2086 input_delete(0); 2087 else 2088 input_backspace(); 2089 break; 2090 #if ENABLE_FEATURE_TAB_COMPLETION 1406 2091 case '\t': 1407 2092 input_tab(&lastWasTab); 1408 2093 break; 1409 1410 #if ENABLE_FEATURE_EDITING_FANCY_KEYS 2094 #endif 1411 2095 case CTRL('K'): 1412 2096 /* Control-k -- clear to end of line */ 1413 command [cursor] = 0;2097 command_ps[cursor] = BB_NUL; 1414 2098 command_len = cursor; 1415 printf( "\033[J");2099 printf(SEQ_CLEAR_TILL_END_OF_SCREEN); 1416 2100 break; 1417 2101 case CTRL('L'): 1418 vi_case(CTRL('L')| vbit:)2102 vi_case(CTRL('L')|VI_CMDMODE_BIT:) 1419 2103 /* Control-l -- clear screen */ 1420 printf( "\033[H");2104 printf(ESC"[H"); /* cursor to top,left */ 1421 2105 redraw(0, command_len - cursor); 1422 2106 break; 1423 #endif1424 1425 2107 #if MAX_HISTORY > 0 1426 2108 case CTRL('N'): 1427 vi_case(CTRL('N')| vbit:)1428 vi_case('j'| vbit:)2109 vi_case(CTRL('N')|VI_CMDMODE_BIT:) 2110 vi_case('j'|VI_CMDMODE_BIT:) 1429 2111 /* Control-n -- Get next command in history */ 1430 2112 if (get_next_history()) … … 1432 2114 break; 1433 2115 case CTRL('P'): 1434 vi_case(CTRL('P')| vbit:)1435 vi_case('k'| vbit:)2116 vi_case(CTRL('P')|VI_CMDMODE_BIT:) 2117 vi_case('k'|VI_CMDMODE_BIT:) 1436 2118 /* Control-p -- Get previous command from history */ 1437 if ((state->flags & DO_HISTORY) && state->cur_history > 0) { 1438 get_previous_history(); 2119 if (get_previous_history()) 1439 2120 goto rewrite_line; 1440 } 1441 beep(); 1442 break; 1443 #endif 1444 1445 #if ENABLE_FEATURE_EDITING_FANCY_KEYS 2121 break; 2122 #endif 1446 2123 case CTRL('U'): 1447 vi_case(CTRL('U')| vbit:)2124 vi_case(CTRL('U')|VI_CMDMODE_BIT:) 1448 2125 /* Control-U -- Clear line before cursor */ 1449 2126 if (cursor) { 1450 strcpy(command, command + cursor);1451 2127 command_len -= cursor; 2128 memmove(command_ps, command_ps + cursor, 2129 (command_len + 1) * sizeof(command_ps[0])); 1452 2130 redraw(cmdedit_y, command_len); 1453 2131 } 1454 2132 break; 1455 #endif1456 2133 case CTRL('W'): 1457 vi_case(CTRL('W')| vbit:)2134 vi_case(CTRL('W')|VI_CMDMODE_BIT:) 1458 2135 /* Control-W -- Remove the last word */ 1459 while (cursor > 0 && isspace(command[cursor-1]))2136 while (cursor > 0 && BB_isspace(command_ps[cursor-1])) 1460 2137 input_backspace(); 1461 while (cursor > 0 && ! isspace(command[cursor-1]))2138 while (cursor > 0 && !BB_isspace(command_ps[cursor-1])) 1462 2139 input_backspace(); 1463 2140 break; 1464 2141 1465 2142 #if ENABLE_FEATURE_EDITING_VI 1466 case 'i'| vbit:2143 case 'i'|VI_CMDMODE_BIT: 1467 2144 vi_cmdmode = 0; 1468 2145 break; 1469 case 'I'| vbit:2146 case 'I'|VI_CMDMODE_BIT: 1470 2147 input_backward(cursor); 1471 2148 vi_cmdmode = 0; 1472 2149 break; 1473 case 'a'| vbit:2150 case 'a'|VI_CMDMODE_BIT: 1474 2151 input_forward(); 1475 2152 vi_cmdmode = 0; 1476 2153 break; 1477 case 'A'| vbit:1478 input_end();2154 case 'A'|VI_CMDMODE_BIT: 2155 put_till_end_and_adv_cursor(); 1479 2156 vi_cmdmode = 0; 1480 2157 break; 1481 case 'x'| vbit:2158 case 'x'|VI_CMDMODE_BIT: 1482 2159 input_delete(1); 1483 2160 break; 1484 case 'X'| vbit:2161 case 'X'|VI_CMDMODE_BIT: 1485 2162 if (cursor > 0) { 1486 2163 input_backward(1); … … 1488 2165 } 1489 2166 break; 1490 case 'W'| vbit:1491 vi_Word_motion( command,1);1492 break; 1493 case 'w'| vbit:1494 vi_word_motion( command,1);1495 break; 1496 case 'E'| vbit:1497 vi_End_motion( command);1498 break; 1499 case 'e'| vbit:1500 vi_end_motion( command);1501 break; 1502 case 'B'| vbit:1503 vi_Back_motion( command);1504 break; 1505 case 'b'| vbit:1506 vi_back_motion( command);1507 break; 1508 case 'C'| vbit:2167 case 'W'|VI_CMDMODE_BIT: 2168 vi_Word_motion(1); 2169 break; 2170 case 'w'|VI_CMDMODE_BIT: 2171 vi_word_motion(1); 2172 break; 2173 case 'E'|VI_CMDMODE_BIT: 2174 vi_End_motion(); 2175 break; 2176 case 'e'|VI_CMDMODE_BIT: 2177 vi_end_motion(); 2178 break; 2179 case 'B'|VI_CMDMODE_BIT: 2180 vi_Back_motion(); 2181 break; 2182 case 'b'|VI_CMDMODE_BIT: 2183 vi_back_motion(); 2184 break; 2185 case 'C'|VI_CMDMODE_BIT: 1509 2186 vi_cmdmode = 0; 1510 2187 /* fall through */ 1511 case 'D'| vbit:2188 case 'D'|VI_CMDMODE_BIT: 1512 2189 goto clear_to_eol; 1513 2190 1514 case 'c'| vbit:2191 case 'c'|VI_CMDMODE_BIT: 1515 2192 vi_cmdmode = 0; 1516 2193 /* fall through */ 1517 case 'd'| vbit: {2194 case 'd'|VI_CMDMODE_BIT: { 1518 2195 int nc, sc; 1519 sc = cursor; 1520 prevc = ic; 1521 if (safe_read(0, &c, 1) < 1) 1522 goto prepare_to_die; 1523 if (c == (prevc & 0xff)) { 1524 /* "cc", "dd" */ 2196 2197 ic = lineedit_read_key(read_key_buffer); 2198 if (errno) /* error */ 2199 goto return_error_indicator; 2200 if (ic == ic_raw) { /* "cc", "dd" */ 1525 2201 input_backward(cursor); 1526 2202 goto clear_to_eol; 1527 2203 break; 1528 2204 } 1529 switch (c) { 2205 2206 sc = cursor; 2207 switch (ic) { 1530 2208 case 'w': 1531 2209 case 'W': 1532 2210 case 'e': 1533 2211 case 'E': 1534 switch ( c) {2212 switch (ic) { 1535 2213 case 'w': /* "dw", "cw" */ 1536 vi_word_motion( command,vi_cmdmode);2214 vi_word_motion(vi_cmdmode); 1537 2215 break; 1538 2216 case 'W': /* 'dW', 'cW' */ 1539 vi_Word_motion( command,vi_cmdmode);2217 vi_Word_motion(vi_cmdmode); 1540 2218 break; 1541 2219 case 'e': /* 'de', 'ce' */ 1542 vi_end_motion( command);2220 vi_end_motion(); 1543 2221 input_forward(); 1544 2222 break; 1545 2223 case 'E': /* 'dE', 'cE' */ 1546 vi_End_motion( command);2224 vi_End_motion(); 1547 2225 input_forward(); 1548 2226 break; … … 1555 2233 case 'b': /* "db", "cb" */ 1556 2234 case 'B': /* implemented as B */ 1557 if ( c == 'b')1558 vi_back_motion( command);2235 if (ic == 'b') 2236 vi_back_motion(); 1559 2237 else 1560 vi_Back_motion( command);2238 vi_Back_motion(); 1561 2239 while (sc-- > cursor) 1562 2240 input_delete(1); … … 1566 2244 break; 1567 2245 case '$': /* "d$", "c$" */ 1568 2246 clear_to_eol: 1569 2247 while (cursor < command_len) 1570 2248 input_delete(1); … … 1573 2251 break; 1574 2252 } 1575 case 'p'| vbit:2253 case 'p'|VI_CMDMODE_BIT: 1576 2254 input_forward(); 1577 2255 /* fallthrough */ 1578 case 'P'| vbit:2256 case 'P'|VI_CMDMODE_BIT: 1579 2257 put(); 1580 2258 break; 1581 case 'r'|vbit: 1582 if (safe_read(0, &c, 1) < 1) 1583 goto prepare_to_die; 1584 if (c == 0) 2259 case 'r'|VI_CMDMODE_BIT: 2260 //FIXME: unicode case? 2261 ic = lineedit_read_key(read_key_buffer); 2262 if (errno) /* error */ 2263 goto return_error_indicator; 2264 if (ic < ' ' || ic > 255) { 1585 2265 beep(); 1586 else {1587 *(command + cursor) =c;1588 putchar(c);1589 putchar('\b');2266 } else { 2267 command_ps[cursor] = ic; 2268 bb_putchar(ic); 2269 bb_putchar('\b'); 1590 2270 } 1591 2271 break; 1592 #endif /* FEATURE_COMMAND_EDITING_VI */1593 1594 2272 case '\x1b': /* ESC */ 1595 1596 #if ENABLE_FEATURE_EDITING_VI1597 2273 if (state->flags & VI_MODE) { 1598 /* ESC:insert mode --> command mode */2274 /* insert mode --> command mode */ 1599 2275 vi_cmdmode = 1; 1600 2276 input_backward(1); 2277 } 2278 break; 2279 #endif /* FEATURE_COMMAND_EDITING_VI */ 2280 2281 #if MAX_HISTORY > 0 2282 case KEYCODE_UP: 2283 if (get_previous_history()) 2284 goto rewrite_line; 2285 beep(); 2286 break; 2287 case KEYCODE_DOWN: 2288 if (!get_next_history()) 2289 break; 2290 rewrite_line: 2291 /* Rewrite the line with the selected history item */ 2292 /* change command */ 2293 command_len = load_string(state->history[state->cur_history] ? 2294 state->history[state->cur_history] : "", maxsize); 2295 /* redraw and go to eol (bol, in vi) */ 2296 redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0); 2297 break; 2298 #endif 2299 case KEYCODE_RIGHT: 2300 input_forward(); 2301 break; 2302 case KEYCODE_LEFT: 2303 input_backward(1); 2304 break; 2305 case KEYCODE_CTRL_LEFT: 2306 ctrl_left(); 2307 break; 2308 case KEYCODE_CTRL_RIGHT: 2309 ctrl_right(); 2310 break; 2311 case KEYCODE_HOME: 2312 input_backward(cursor); 2313 break; 2314 case KEYCODE_END: 2315 put_till_end_and_adv_cursor(); 2316 break; 2317 2318 default: 2319 if (initial_settings.c_cc[VINTR] != 0 2320 && ic_raw == initial_settings.c_cc[VINTR] 2321 ) { 2322 /* Ctrl-C (usually) - stop gathering input */ 2323 goto_new_line(); 2324 command_len = 0; 2325 break_out = -1; /* "do not append '\n'" */ 1601 2326 break; 1602 2327 } 1603 #endif 1604 /* escape sequence follows */ 1605 if (safe_read(0, &c, 1) < 1) 1606 goto prepare_to_die; 1607 /* different vt100 emulations */ 1608 if (c == '[' || c == 'O') { 1609 vi_case('['|vbit:) 1610 vi_case('O'|vbit:) 1611 if (safe_read(0, &c, 1) < 1) 1612 goto prepare_to_die; 1613 } 1614 if (c >= '1' && c <= '9') { 1615 unsigned char dummy; 1616 1617 if (safe_read(0, &dummy, 1) < 1) 1618 goto prepare_to_die; 1619 if (dummy != '~') 1620 c = '\0'; 1621 } 1622 1623 switch (c) { 1624 #if ENABLE_FEATURE_TAB_COMPLETION 1625 case '\t': /* Alt-Tab */ 1626 input_tab(&lastWasTab); 1627 break; 1628 #endif 1629 #if MAX_HISTORY > 0 1630 case 'A': 1631 /* Up Arrow -- Get previous command from history */ 1632 if ((state->flags & DO_HISTORY) && state->cur_history > 0) { 1633 get_previous_history(); 1634 goto rewrite_line; 2328 if (initial_settings.c_cc[VEOF] != 0 2329 && ic_raw == initial_settings.c_cc[VEOF] 2330 ) { 2331 /* Ctrl-D (usually) - delete one character, 2332 * or exit if len=0 and no chars to delete */ 2333 if (command_len == 0) { 2334 errno = 0; 2335 2336 case -1: /* error (e.g. EIO when tty is destroyed) */ 2337 IF_FEATURE_EDITING_VI(return_error_indicator:) 2338 break_out = command_len = -1; 2339 break; 1635 2340 } 1636 beep();1637 break;1638 case 'B':1639 /* Down Arrow -- Get next command in history */1640 if (!get_next_history())1641 break;1642 rewrite_line:1643 /* Rewrite the line with the selected history item */1644 /* change command */1645 command_len = strlen(strcpy(command, state->history[state->cur_history]));1646 /* redraw and go to eol (bol, in vi */1647 redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);1648 break;1649 #endif1650 case 'C':1651 /* Right Arrow -- Move forward one character */1652 input_forward();1653 break;1654 case 'D':1655 /* Left Arrow -- Move back one character */1656 input_backward(1);1657 break;1658 case '3':1659 /* Delete */1660 2341 input_delete(0); 1661 2342 break; 1662 case '1': // vt100? linux vt? or what? 1663 case '7': // vt100? linux vt? or what? 1664 case 'H': /* xterm's <Home> */ 1665 input_backward(cursor); 2343 } 2344 // /* Control-V -- force insert of next char */ 2345 // if (c == CTRL('V')) { 2346 // if (safe_read(STDIN_FILENO, &c, 1) < 1) 2347 // goto return_error_indicator; 2348 // if (c == 0) { 2349 // beep(); 2350 // break; 2351 // } 2352 // } 2353 if (ic < ' ' 2354 || (!ENABLE_UNICODE_SUPPORT && ic >= 256) 2355 || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT) 2356 ) { 2357 /* If VI_CMDMODE_BIT is set, ic is >= 256 2358 * and vi mode ignores unexpected chars. 2359 * Otherwise, we are here if ic is a 2360 * control char or an unhandled ESC sequence, 2361 * which is also ignored. 2362 */ 1666 2363 break; 1667 case '4': // vt100? linux vt? or what? 1668 case '8': // vt100? linux vt? or what? 1669 case 'F': /* xterm's <End> */ 1670 input_end(); 2364 } 2365 if ((int)command_len >= (maxsize - 2)) { 2366 /* Not enough space for the char and EOL */ 1671 2367 break; 1672 default:1673 c = '\0';1674 beep();1675 2368 } 1676 break;1677 1678 default: /* If it's regular input, do the normal thing */1679 #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT1680 /* Control-V -- Add non-printable symbol */1681 if (c == CTRL('V')) {1682 if (safe_read(0, &c, 1) < 1)1683 goto prepare_to_die;1684 if (c == 0) {1685 beep();1686 break;1687 }1688 } else1689 #endif1690 1691 #if ENABLE_FEATURE_EDITING_VI1692 if (vi_cmdmode) /* Don't self-insert */1693 break;1694 #endif1695 if (!Isprint(c)) /* Skip non-printable characters */1696 break;1697 1698 if (command_len >= (maxsize - 2)) /* Need to leave space for enter */1699 break;1700 2369 1701 2370 command_len++; 1702 if (cursor == (command_len - 1)) { /* Append if at the end of the line */ 1703 command[cursor] = c; 1704 command[cursor+1] = '\0'; 1705 cmdedit_set_out_char(' '); 1706 } else { /* Insert otherwise */ 2371 if (cursor == (command_len - 1)) { 2372 /* We are at the end, append */ 2373 command_ps[cursor] = ic; 2374 command_ps[cursor + 1] = BB_NUL; 2375 put_cur_glyph_and_inc_cursor(); 2376 if (unicode_bidi_isrtl(ic)) 2377 input_backward(1); 2378 } else { 2379 /* In the middle, insert */ 1707 2380 int sc = cursor; 1708 2381 1709 memmove(command + sc + 1, command + sc, command_len - sc); 1710 command[sc] = c; 1711 sc++; 1712 /* rewrite from cursor */ 1713 input_end(); 2382 memmove(command_ps + sc + 1, command_ps + sc, 2383 (command_len - sc) * sizeof(command_ps[0])); 2384 command_ps[sc] = ic; 2385 /* is right-to-left char, or neutral one (e.g. comma) was just added to rtl text? */ 2386 if (!isrtl_str()) 2387 sc++; /* no */ 2388 put_till_end_and_adv_cursor(); 1714 2389 /* to prev x pos + 1 */ 1715 2390 input_backward(cursor - sc); 1716 2391 } 1717 2392 break; 1718 } 1719 if (break_out) /* Enter is the command terminator, no more input. */ 1720 break; 1721 1722 if (c != '\t') 1723 lastWasTab = FALSE; 1724 } 2393 } /* switch (ic) */ 2394 2395 if (break_out) 2396 break; 2397 2398 #if ENABLE_FEATURE_TAB_COMPLETION 2399 if (ic_raw != '\t') 2400 lastWasTab = 0; 2401 #endif 2402 } /* while (1) */ 2403 2404 #if ENABLE_FEATURE_EDITING_ASK_TERMINAL 2405 if (S.sent_ESC_br6n) { 2406 /* "sleep 1; busybox ash" + hold [Enter] to trigger. 2407 * We sent "ESC [ 6 n", but got '\n' first, and 2408 * KEYCODE_CURSOR_POS response is now buffered from terminal. 2409 * It's bad already and not much can be done with it 2410 * (it _will_ be visible for the next process to read stdin), 2411 * but without this delay it even shows up on the screen 2412 * as garbage because we restore echo settings with tcsetattr 2413 * before it comes in. UGLY! 2414 */ 2415 usleep(20*1000); 2416 } 2417 #endif 2418 2419 /* End of bug-catching "command_must_not_be_used" trick */ 2420 #undef command 2421 2422 #if ENABLE_UNICODE_SUPPORT 2423 command[0] = '\0'; 2424 if (command_len > 0) 2425 command_len = save_string(command, maxsize - 1); 2426 free(command_ps); 2427 #endif 1725 2428 1726 2429 if (command_len > 0) … … 1732 2435 } 1733 2436 1734 #if ENABLE_FEATURE_ CLEAN_UP && ENABLE_FEATURE_TAB_COMPLETION2437 #if ENABLE_FEATURE_TAB_COMPLETION 1735 2438 free_tab_completion_data(); 1736 2439 #endif 1737 2440 1738 #if ENABLE_FEATURE_EDITING_FANCY_PROMPT1739 free((char*)cmdedit_prompt);1740 #endif1741 2441 /* restore initial_settings */ 1742 setTermSettings(STDIN_FILENO, (void *)&initial_settings);2442 tcsetattr_stdin_TCSANOW(&initial_settings); 1743 2443 /* restore SIGWINCH handler */ 1744 2444 signal(SIGWINCH, previous_SIGWINCH_handler); 1745 fflush(stdout); 1746 return command_len; 1747 } 1748 1749 line_input_t *new_line_input_t(int flags) 1750 { 1751 line_input_t *n = xzalloc(sizeof(*n)); 1752 n->flags = flags; 1753 return n; 1754 } 1755 1756 #else 2445 fflush_all(); 2446 2447 len = command_len; 2448 DEINIT_S(); 2449 2450 return len; /* can't return command_len, DEINIT_S() destroys it */ 2451 } 2452 2453 #else /* !FEATURE_EDITING */ 1757 2454 1758 2455 #undef read_line_input 1759 int read_line_input(const char* prompt, char* command, int maxsize)2456 int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize) 1760 2457 { 1761 2458 fputs(prompt, stdout); 1762 fflush (stdout);2459 fflush_all(); 1763 2460 fgets(command, maxsize, stdin); 1764 2461 return strlen(command); 1765 2462 } 1766 2463 1767 #endif /* FEATURE_COMMAND_EDITING */2464 #endif /* !FEATURE_EDITING */ 1768 2465 1769 2466 … … 1790 2487 #endif 1791 2488 1792 #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT1793 setlocale(LC_ALL, "");1794 #endif1795 2489 while (1) { 1796 2490 int l; … … 1798 2492 if (l <= 0 || buff[l-1] != '\n') 1799 2493 break; 1800 buff[l-1] = 0;2494 buff[l-1] = '\0'; 1801 2495 printf("*** read_line_input() returned line =%s=\n", buff); 1802 2496 }
Note:
See TracChangeset
for help on using the changeset viewer.