aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--cmdedit.c1521
-rw-r--r--shell/cmdedit.c1521
3 files changed, 2057 insertions, 986 deletions
diff --git a/Makefile b/Makefile
index b1f88aa9c..588f8e0a2 100644
--- a/Makefile
+++ b/Makefile
@@ -78,6 +78,7 @@ BB_SRC_DIR = .
78# to something more interesting, like "powerpc-linux-". 78# to something more interesting, like "powerpc-linux-".
79CROSS = 79CROSS =
80CC = $(CROSS)gcc 80CC = $(CROSS)gcc
81AR = $(CROSS)ar
81STRIPTOOL = $(CROSS)strip 82STRIPTOOL = $(CROSS)strip
82 83
83# To compile vs uClibc, just use the compiler wrapper built by uClibc... 84# To compile vs uClibc, just use the compiler wrapper built by uClibc...
diff --git a/cmdedit.c b/cmdedit.c
index 2e57b9a34..6a53c12f6 100644
--- a/cmdedit.c
+++ b/cmdedit.c
@@ -1,21 +1,18 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* 2/*
3 * Termios command line History and Editting, originally 3 * Termios command line History and Editting.
4 * intended for NetBSD sh (ash)
5 * Copyright (c) 1999
6 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu>
7 * Etc: Dave Cinege <dcinege@psychosis.com>
8 * Majorly adjusted/re-written for busybox:
9 * Erik Andersen <andersee@debian.org>
10 * 4 *
11 * You may use this code as you wish, so long as the original author(s) 5 * Copyright (c) 1986-2001 may safely be consumed by a BSD or GPL license.
12 * are attributed in any redistributions of the source code. 6 * Written by: Vladimir Oleynik <vodz@usa.net>
13 * This code is 'as is' with no warranty. 7 *
14 * This code may safely be consumed by a BSD or GPL license. 8 * Used ideas:
9 * Adam Rogoyski <rogoyski@cs.utexas.edu>
10 * Dave Cinege <dcinege@psychosis.com>
11 * Jakub Jelinek (c) 1995
12 * Erik Andersen <andersee@debian.org> (Majorly adjusted for busybox)
15 * 13 *
16 * v 0.5 19990328 Initial release 14 * This code is 'as is' with no warranty.
17 * 15 *
18 * Future plans: Simple file and path name completion. (like BASH)
19 * 16 *
20 */ 17 */
21 18
@@ -27,20 +24,46 @@
27 to work in an Xterm and console. Ctrl-A also works as Home. 24 to work in an Xterm and console. Ctrl-A also works as Home.
28 Ctrl-E also works as End. 25 Ctrl-E also works as End.
29 26
27 Small bugs (simple effect):
28 - not true viewing if terminal size (x*y symbols) less
29 size (prompt + editor`s line + 2 symbols)
30 */
30 31
31 Editor with vertical scrolling and completion by
32 Vladimir Oleynik. vodz@usa.net (c) 2001
33 32
34 Small bug: not true work if terminal size (x*y symbols) less 33#define TEST
35 size (prompt + editor`s line + 2 symbols)
36 */
37 34
38 35
36#ifndef TEST
39 37
40#include "busybox.h" 38#include "busybox.h"
41 39
40#define D(x)
41
42#else
43
44#define BB_FEATURE_SH_COMMAND_EDITING
45#define BB_FEATURE_SH_TAB_COMPLETION
46#define BB_FEATURE_USERNAME_COMPLETION
47#define BB_FEATURE_NONPRINTABLE_INVERSE_PUT
48#define BB_FEATURE_BASH_STYLE_PROMT
49#define BB_FEATURE_CLEAN_UP
50
51#define TRUE 1
52#define FALSE 0
53#define D(x) x
54
55#endif /* TEST */
56
42#ifdef BB_FEATURE_SH_COMMAND_EDITING 57#ifdef BB_FEATURE_SH_COMMAND_EDITING
43 58
59#ifndef BB_FEATURE_SH_TAB_COMPLETION
60#undef BB_FEATURE_USERNAME_COMPLETION
61#endif
62
63#if defined(BB_FEATURE_USERNAME_COMPLETION) || defined(BB_FEATURE_BASH_STYLE_PROMT)
64#define BB_FEATURE_GETUSERNAME_AND_HOMEDIR
65#endif
66
44#include <stdio.h> 67#include <stdio.h>
45#include <errno.h> 68#include <errno.h>
46#include <unistd.h> 69#include <unistd.h>
@@ -49,26 +72,61 @@
49#include <sys/ioctl.h> 72#include <sys/ioctl.h>
50#include <ctype.h> 73#include <ctype.h>
51#include <signal.h> 74#include <signal.h>
75#include <limits.h>
52 76
53#ifdef BB_FEATURE_SH_TAB_COMPLETION 77#ifdef BB_FEATURE_SH_TAB_COMPLETION
78#include <dirent.h>
54#include <sys/stat.h> 79#include <sys/stat.h>
55#endif 80#endif
56 81
82#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR
83#ifndef TEST
57#include "pwd_grp/pwd.h" 84#include "pwd_grp/pwd.h"
85#else
86#include <pwd.h>
87#endif /* TEST */
88#endif /* advanced FEATURES */
58 89
59 90
60static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */ 91#ifdef TEST
92void *xrealloc(void *old, size_t size)
93{
94 return realloc(old, size);
95}
61 96
62enum { 97void *xmalloc(size_t size)
63 ESC = 27, 98{
64 DEL = 127, 99 return malloc(size);
100}
101char *xstrdup(const char *s)
102{
103 return strdup(s);
104}
105
106void *xcalloc(size_t size, size_t se)
107{
108 return calloc(size, se);
109}
110
111#define error_msg(s, d) fprintf(stderr, s, d)
112#endif
113
114
115struct history {
116 char *s;
117 struct history *p;
118 struct history *n;
65}; 119};
66 120
67#define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0) 121/* Maximum length of the linked list for the command line history */
68#define whitespace(c) (((c) == ' ') || ((c) == '\t')) 122static const int MAX_HISTORY = 15;
123
124/* First element in command line list */
125static struct history *his_front = NULL;
126
127/* Last element in command line list */
128static struct history *his_end = NULL;
69 129
70static struct history *his_front = NULL; /* First element in command line list */
71static struct history *his_end = NULL; /* Last element in command line list */
72 130
73/* ED: sparc termios is broken: revert back to old termio handling. */ 131/* ED: sparc termios is broken: revert back to old termio handling. */
74 132
@@ -87,87 +145,100 @@ static struct history *his_end = NULL; /* Last element in command line list */
87static struct termios initial_settings, new_settings; 145static struct termios initial_settings, new_settings;
88 146
89 147
90#ifndef _POSIX_VDISABLE 148#ifndef _POSIX_VDISABLE
91#define _POSIX_VDISABLE '\0' 149#define _POSIX_VDISABLE '\0'
92#endif 150#endif
93 151
94 152
95static 153static
96volatile int cmdedit_termw; /* actual terminal width */ 154volatile int cmdedit_termw = 80; /* actual terminal width */
97static int history_counter = 0; /* Number of commands in history list */ 155static int history_counter = 0; /* Number of commands in history list */
98
99static 156static
100volatile int handlers_sets = 0; /* Set next bites 157volatile int handlers_sets = 0; /* Set next bites: */
101 when atexit() has been called 158
102 and set many "terminates" signal handlers
103 and winchg signal handler
104 and if the terminal needs to be reset upon exit
105 */
106enum { 159enum {
107 SET_ATEXIT = 1, 160 SET_ATEXIT = 1, /* when atexit() has been called and
108 SET_TERM_HANDLERS = 2, 161 get euid,uid,gid to fast compare */
109 SET_WCHG_HANDLERS = 4, 162 SET_TERM_HANDLERS = 2, /* set many terminates signal handlers */
110 SET_RESET_TERM = 8, 163 SET_WCHG_HANDLERS = 4, /* winchg signal handler */
164 SET_RESET_TERM = 8, /* if the terminal needs to be reset upon exit */
111}; 165};
112 166
113
114static int cmdedit_x; /* real x terminal position,
115 require put prompt in start x position */
116static int cmdedit_y; /* pseudoreal y terminal position */
117static int cmdedit_prmt_len; /* for fast running, without duplicate calculate */
118 167
119static int cursor; /* required global for signal handler */ 168static int cmdedit_x; /* real x terminal position */
120static int len; /* --- "" - - "" - -"- --""-- --""--- */ 169static int cmdedit_y; /* pseudoreal y terminal position */
121static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */ 170static int cmdedit_prmt_len; /* lenght prompt without colores string */
122static const char *cmdedit_prompt;/* --- "" - - "" - -"- --""-- --""--- */
123 171
124/* Link into lash to reset context to 0 172static int cursor; /* required global for signal handler */
125 * on ^C and such */ 173static int len; /* --- "" - - "" - -"- --""-- --""--- */
174static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */
175static
176#ifndef BB_FEATURE_BASH_STYLE_PROMT
177 const
178#endif
179char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */
180
181/* Link into lash to reset context to 0 on ^C and such */
126extern unsigned int shell_context; 182extern unsigned int shell_context;
127 183
128 184
129struct history { 185#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR
130 char *s; 186static char *user_buf = "";
131 struct history *p; 187static char *home_pwd_buf = "";
132 struct history *n; 188static int my_euid;
133}; 189#endif
190
191#ifdef BB_FEATURE_BASH_STYLE_PROMT
192static char *hostname_buf = "";
193static int num_ok_lines = 1;
194#endif
195
196
197#ifdef BB_FEATURE_SH_TAB_COMPLETION
198
199#ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIR
200static int my_euid;
201#endif
202
203static int my_uid;
204static int my_gid;
205
206#endif /* BB_FEATURE_SH_TAB_COMPLETION */
207
134 208
135static void cmdedit_setwidth(int w, int redraw_flg); 209static void cmdedit_setwidth(int w, int redraw_flg);
136 210
137static void win_changed(int nsig) 211static void win_changed(int nsig)
138{ 212{
139 struct winsize win = { 0, 0, 0, 0 }; 213 struct winsize win = { 0, 0, 0, 0 };
140 static __sighandler_t previous_SIGWINCH_handler; /* for reset */ 214 static __sighandler_t previous_SIGWINCH_handler; /* for reset */
141 215
142 /* emulate signal call if not called as a sig handler */ 216 /* emulate || signal call */
143 if(nsig == -SIGWINCH || nsig == SIGWINCH) { 217 if (nsig == -SIGWINCH || nsig == SIGWINCH) {
144 ioctl(0, TIOCGWINSZ, &win); 218 ioctl(0, TIOCGWINSZ, &win);
145 if (win.ws_col > 0) { 219 if (win.ws_col > 0) {
146 cmdedit_setwidth( win.ws_col, nsig == SIGWINCH ); 220 cmdedit_setwidth(win.ws_col, nsig == SIGWINCH);
147 } else { 221 }
148 /* Default to 79 if their console doesn't want to share */
149 cmdedit_setwidth( 79, nsig == SIGWINCH );
150 }
151 } 222 }
152
153 /* Unix not all standart in recall signal */ 223 /* Unix not all standart in recall signal */
154 224
155 if(nsig == -SIGWINCH) /* save previous handler */ 225 if (nsig == -SIGWINCH) /* save previous handler */
156 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); 226 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
157 else if(nsig == SIGWINCH) /* signaled called handler */ 227 else if (nsig == SIGWINCH) /* signaled called handler */
158 signal(SIGWINCH, win_changed); /* set for next call */ 228 signal(SIGWINCH, win_changed); /* set for next call */
159 else /* set previous handler */ 229 else /* nsig == 0 */
160 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */ 230 /* set previous handler */
231 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
161} 232}
162 233
163static void cmdedit_reset_term(void) 234static void cmdedit_reset_term(void)
164{ 235{
165 if((handlers_sets & SET_RESET_TERM)!=0) { 236 if ((handlers_sets & SET_RESET_TERM) != 0) {
166 /* sparc and other have broken termios support: use old termio handling. */ 237 /* sparc and other have broken termios support: use old termio handling. */
167 setTermSettings(fileno(stdin), (void*) &initial_settings); 238 setTermSettings(fileno(stdin), (void *) &initial_settings);
168 handlers_sets &= ~SET_RESET_TERM; 239 handlers_sets &= ~SET_RESET_TERM;
169 } 240 }
170 if((handlers_sets & SET_WCHG_HANDLERS)!=0) { 241 if ((handlers_sets & SET_WCHG_HANDLERS) != 0) {
171 /* reset SIGWINCH handler to previous (default) */ 242 /* reset SIGWINCH handler to previous (default) */
172 win_changed(0); 243 win_changed(0);
173 handlers_sets &= ~SET_WCHG_HANDLERS; 244 handlers_sets &= ~SET_WCHG_HANDLERS;
@@ -176,28 +247,45 @@ static void cmdedit_reset_term(void)
176#ifdef BB_FEATURE_CLEAN_UP 247#ifdef BB_FEATURE_CLEAN_UP
177 if (his_front) { 248 if (his_front) {
178 struct history *n; 249 struct history *n;
250
179 //while(his_front!=his_end) { 251 //while(his_front!=his_end) {
180 while(his_front!=his_end) { 252 while (his_front != his_end) {
181 n = his_front->n; 253 n = his_front->n;
182 free(his_front->s); 254 free(his_front->s);
183 free(his_front); 255 free(his_front);
184 his_front=n; 256 his_front = n;
185 } 257 }
186 } 258 }
187#endif 259#endif
188} 260}
189 261
190 262
191
192/* special for recount position for scroll and remove terminal margin effect */ 263/* special for recount position for scroll and remove terminal margin effect */
193static void cmdedit_set_out_char(int c, int next_char) { 264static void cmdedit_set_out_char(int next_char)
194 putchar(c); 265{
195 if(++cmdedit_x>=cmdedit_termw) { 266
267 int c = command_ps[cursor];
268
269 if (c == 0)
270 c = ' '; /* destroy end char? */
271#ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT
272 if (!isprint(c)) { /* Inverse put non-printable characters */
273 if (((unsigned char) c) >= 128)
274 c -= 128;
275 if (((unsigned char) c) < ' ')
276 c += '@';
277 if (c == 127)
278 c = '?';
279 printf("\033[7m%c\033[0m", c);
280 } else
281#endif
282 putchar(c);
283 if (++cmdedit_x >= cmdedit_termw) {
196 /* terminal is scrolled down */ 284 /* terminal is scrolled down */
197 cmdedit_y++; 285 cmdedit_y++;
198 cmdedit_x=0; 286 cmdedit_x = 0;
199 287
200 if(!next_char) 288 if (!next_char)
201 next_char = ' '; 289 next_char = ' ';
202 /* destroy "(auto)margin" */ 290 /* destroy "(auto)margin" */
203 putchar(next_char); 291 putchar(next_char);
@@ -206,56 +294,221 @@ static void cmdedit_set_out_char(int c, int next_char) {
206 cursor++; 294 cursor++;
207} 295}
208 296
209/* Move to end line. Bonus: rewrite line from cursor without use 297/* Move to end line. Bonus: rewrite line from cursor */
210 special control terminal strings, also saved size and speed! */ 298static void input_end(void)
211static void input_end (void) { 299{
212 while(cursor < len) 300 while (cursor < len)
213 cmdedit_set_out_char(command_ps[cursor], 0); 301 cmdedit_set_out_char(0);
214} 302}
215 303
216/* Go to the next line */ 304/* Go to the next line */
217static void goto_new_line(void) { 305static void goto_new_line(void)
306{
218 input_end(); 307 input_end();
219 cmdedit_set_out_char('\n', 0); 308 if (cmdedit_x)
309 putchar('\n');
220} 310}
221 311
222 312
223static inline void out1str(const char *s) { fputs (s, stdout); } 313static inline void out1str(const char *s)
224static inline void beep (void) { putchar('\007'); } 314{
315 fputs(s, stdout);
316}
317static inline void beep(void)
318{
319 putchar('\007');
320}
225 321
226/* Go to HOME position */ 322/* Move back one charactor */
227static void input_home(void) 323/* special for slow terminal */
324static void input_backward(int num)
228{ 325{
229 while(cmdedit_y>0) { /* up to start y */ 326 if (num > cursor)
230 out1str("\033[A"); 327 num = cursor;
231 cmdedit_y--; 328 cursor -= num; /* new cursor (in command, not terminal) */
329
330 if (cmdedit_x >= num) { /* no to up line */
331 cmdedit_x -= num;
332 if (num < 4)
333 while (num-- > 0)
334 putchar('\b');
335
336 else
337 printf("\033[%dD", num);
338 } else {
339 int count_y;
340
341 if (cmdedit_x) {
342 putchar('\r'); /* back to first terminal pos. */
343 num -= cmdedit_x; /* set previous backward */
344 }
345 count_y = 1 + num / cmdedit_termw;
346 printf("\033[%dA", count_y);
347 cmdedit_y -= count_y;
348 /* require forward after uping */
349 cmdedit_x = cmdedit_termw * count_y - num;
350 printf("\033[%dC", cmdedit_x); /* set term cursor */
232 } 351 }
233 putchar('\r'); 352}
234 cursor = 0; 353
354static void put_prompt(void)
355{
235 out1str(cmdedit_prompt); 356 out1str(cmdedit_prompt);
236 cmdedit_x = cmdedit_prmt_len; 357 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
358 cursor = 0;
359}
237 360
361#ifdef BB_FEATURE_BASH_STYLE_PROMT
362static void
363add_to_prompt(char **prmt_mem_ptr, int *alm, int *prmt_len,
364 const char *addb)
365{
366 int l = strlen(addb);
367
368 *prmt_len += l;
369 if (*alm < (*prmt_len) + 1) {
370 *alm = (*prmt_len) + 1;
371 *prmt_mem_ptr = xrealloc(*prmt_mem_ptr, *alm);
372 }
373 strcat(*prmt_mem_ptr, addb);
238} 374}
375#endif
239 376
240/* Move back one charactor */ 377static void parse_prompt(const char *prmt_ptr)
241static void input_backward(void) { 378{
242 if (cursor > 0) { 379#ifdef BB_FEATURE_BASH_STYLE_PROMT
243 cursor--; 380 int alm = strlen(prmt_ptr) + 1; /* supposedly require memory */
244 if(cmdedit_x!=0) { /* no first position in terminal line */ 381 int prmt_len = 0;
245 putchar('\b'); 382 int sub_len = 0;
246 cmdedit_x--; 383 int flg_not_length = '[';
384 char *prmt_mem_ptr = xstrdup(prmt_ptr);
385 char pwd_buf[PATH_MAX + 1];
386 char buf[16];
387 int c;
388
389 pwd_buf[0] = 0;
390 *prmt_mem_ptr = 0;
391
392 while (*prmt_ptr) {
393 c = *prmt_ptr++;
394 if (c == '\\') {
395 c = *prmt_ptr;
396 if (c == 0)
397 break;
398 prmt_ptr++;
399 switch (c) {
400 case 'u':
401 add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, user_buf);
402 continue;
403 case 'h':
404 if (hostname_buf[0] == 0) {
405 hostname_buf = xcalloc(256, 1);
406 if (gethostname(hostname_buf, 255) < 0) {
407 strcpy(hostname_buf, "?");
408 } else {
409 char *s = strchr(hostname_buf, '.');
410
411 if (s)
412 *s = 0;
413 }
414 }
415 add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len,
416 hostname_buf);
417 continue;
418 case '$':
419 c = my_euid == 0 ? '#' : '$';
420 break;
421 case 'w':
422 if (pwd_buf[0] == 0) {
423 int l;
424
425 getcwd(pwd_buf, PATH_MAX);
426 l = strlen(home_pwd_buf);
427 if (home_pwd_buf[0] != 0 &&
428 strncmp(home_pwd_buf, pwd_buf, l) == 0) {
429 strcpy(pwd_buf + 1, pwd_buf + l);
430 pwd_buf[0] = '~';
431 }
432 }
433 add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, pwd_buf);
434 continue;
435 case '!':
436 snprintf(buf, sizeof(buf), "%d", num_ok_lines);
437 add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, buf);
438 continue;
439 case 'e':
440 case 'E': /* \e \E = \033 */
441 c = '\033';
442 break;
443 case 'x':
444 case 'X':
445 case '0':
446 case '1':
447 case '2':
448 case '3':
449 case '4':
450 case '5':
451 case '6':
452 case '7':{
453 int l;
454 int ho = 0;
455 char *eho;
456
457 if (c == 'X')
458 c = 'x';
459
460 for (l = 0; l < 3;) {
461
462 buf[l++] = *prmt_ptr;
463 buf[l] = 0;
464 ho = strtol(buf, &eho, c == 'x' ? 16 : 8);
465 if (ho > UCHAR_MAX || (eho - buf) < l) {
466 l--;
467 break;
468 }
469 prmt_ptr++;
470 }
471 buf[l] = 0;
472 ho = strtol(buf, 0, c == 'x' ? 16 : 8);
473 c = ho == 0 ? '?' : (char) ho;
474 break;
247 } 475 }
248 else { 476 case '[':
249 out1str("\033[A"); /* up */ 477 case ']':
250 cmdedit_y--; 478 if (c == flg_not_length) {
251 479 flg_not_length = flg_not_length == '[' ? ']' : '[';
252 /* to end in current terminal line */ 480 continue;
253 while(cmdedit_x<(cmdedit_termw-1)) {
254 out1str("\033[C");
255 cmdedit_x++;
256 } 481 }
482 break;
257 } 483 }
484 }
485 buf[0] = c;
486 buf[1] = 0;
487 add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, buf);
488 if (flg_not_length == ']')
489 sub_len++;
258 } 490 }
491 cmdedit_prmt_len = prmt_len - sub_len;
492 cmdedit_prompt = prmt_mem_ptr;
493#else
494 cmdedit_prompt = prmt_ptr;
495 cmdedit_prmt_len = strlen(prmt_ptr);
496#endif
497 put_prompt();
498}
499
500
501/* draw promt, editor line, and clear tail */
502static void redraw(int y, int back_cursor)
503{
504 if (y > 0) /* up to start y */
505 printf("\033[%dA", y);
506 cmdedit_y = 0; /* new quasireal y */
507 putchar('\r');
508 put_prompt();
509 input_end(); /* rewrite */
510 printf("\033[J"); /* destroy tail after cursor */
511 input_backward(back_cursor);
259} 512}
260 513
261/* Delete the char in front of the cursor */ 514/* Delete the char in front of the cursor */
@@ -265,21 +518,20 @@ static void input_delete(void)
265 518
266 if (j == len) 519 if (j == len)
267 return; 520 return;
268 521
269 memmove (command_ps + j, command_ps + j + 1, BUFSIZ - j - 1); 522 strcpy(command_ps + j, command_ps + j + 1);
270 len--; 523 len--;
271 input_end(); /* rewtite new line */ 524 input_end(); /* rewtite new line */
272 cmdedit_set_out_char(' ', 0); /* destroy end char */ 525 cmdedit_set_out_char(0); /* destroy end char */
273 while (j < cursor) 526 input_backward(cursor - j); /* back to old pos cursor */
274 input_backward(); /* back to old pos cursor */
275} 527}
276 528
277/* Delete the char in back of the cursor */ 529/* Delete the char in back of the cursor */
278static void input_backspace(void) 530static void input_backspace(void)
279{ 531{
280 if (cursor > 0) { 532 if (cursor > 0) {
281 input_backward(); 533 input_backward(1);
282 input_delete (); 534 input_delete();
283 } 535 }
284} 536}
285 537
@@ -287,382 +539,653 @@ static void input_backspace(void)
287/* Move forward one charactor */ 539/* Move forward one charactor */
288static void input_forward(void) 540static void input_forward(void)
289{ 541{
290 if (cursor < len) 542 if (cursor < len)
291 cmdedit_set_out_char(command_ps[cursor], command_ps[cursor + 1]); 543 cmdedit_set_out_char(command_ps[cursor + 1]);
292} 544}
293 545
294 546
295static void clean_up_and_die(int sig) 547static void clean_up_and_die(int sig)
296{ 548{
297 goto_new_line(); 549 goto_new_line();
298 if (sig!=SIGINT) 550 if (sig != SIGINT)
299 exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */ 551 exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */
300 cmdedit_reset_term(); 552 cmdedit_reset_term();
301} 553}
302 554
303static void cmdedit_setwidth(int w, int redraw_flg) 555static void cmdedit_setwidth(int w, int redraw_flg)
304{ 556{
305 cmdedit_termw = cmdedit_prmt_len+2; 557 cmdedit_termw = cmdedit_prmt_len + 2;
306 if (w > cmdedit_termw) { 558 if (w > cmdedit_termw) {
307 559
308 cmdedit_termw = w; 560 cmdedit_termw = w;
309 561
310 if(redraw_flg) { 562 if (redraw_flg) {
311 int sav_cursor = cursor; 563 /* new y for current cursor */
312 564 int new_y = (cursor + cmdedit_prmt_len) / w;
313 /* set variables for new terminal size */
314 cmdedit_y = sav_cursor/w;
315 cmdedit_x = sav_cursor-cmdedit_y*w;
316 565
317 /* redraw */ 566 /* redraw */
318 input_home(); 567 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
319 input_end(); 568 fflush(stdout);
320 while(sav_cursor<cursor)
321 input_backward();
322 } 569 }
323 } else { 570 } else {
324 error_msg("\n*** Error: minimum screen width is %d", cmdedit_termw); 571 error_msg("\n*** Error: minimum screen width is %d",
572 cmdedit_termw);
325 } 573 }
326} 574}
327 575
328extern void cmdedit_init(void) 576extern void cmdedit_init(void)
329{ 577{
330 if((handlers_sets & SET_WCHG_HANDLERS)==0) { 578 if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
331 /* pretend we received a signal in order to set term size and sig handling */ 579 /* emulate usage handler to set handler and call yours work */
332 win_changed(-SIGWINCH); 580 win_changed(-SIGWINCH);
333 handlers_sets |= SET_WCHG_HANDLERS; 581 handlers_sets |= SET_WCHG_HANDLERS;
334 } 582 }
335 583
336 if((handlers_sets & SET_ATEXIT)==0) { 584 if ((handlers_sets & SET_ATEXIT) == 0) {
337 atexit(cmdedit_reset_term); /* be sure to do this only once */ 585#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR
586 struct passwd *entry;
587
588 my_euid = geteuid();
589 entry = getpwuid(my_euid);
590 if (entry) {
591 user_buf = xstrdup(entry->pw_name);
592 home_pwd_buf = xstrdup(entry->pw_dir);
593 }
594#endif
595
596#ifdef BB_FEATURE_SH_TAB_COMPLETION
597
598#ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIR
599 my_euid = geteuid();
600#endif
601 my_uid = getuid();
602 my_gid = getgid();
603#endif /* BB_FEATURE_SH_TAB_COMPLETION */
338 handlers_sets |= SET_ATEXIT; 604 handlers_sets |= SET_ATEXIT;
605 atexit(cmdedit_reset_term); /* be sure to do this only once */
339 } 606 }
340 if((handlers_sets & SET_TERM_HANDLERS)==0) { 607
608 if ((handlers_sets & SET_TERM_HANDLERS) == 0) {
341 signal(SIGKILL, clean_up_and_die); 609 signal(SIGKILL, clean_up_and_die);
342 signal(SIGINT, clean_up_and_die); 610 signal(SIGINT, clean_up_and_die);
343 signal(SIGQUIT, clean_up_and_die); 611 signal(SIGQUIT, clean_up_and_die);
344 signal(SIGTERM, clean_up_and_die); 612 signal(SIGTERM, clean_up_and_die);
345 handlers_sets |= SET_TERM_HANDLERS; 613 handlers_sets |= SET_TERM_HANDLERS;
346 } 614 }
615
347} 616}
348 617
349#ifdef BB_FEATURE_SH_TAB_COMPLETION 618#ifdef BB_FEATURE_SH_TAB_COMPLETION
350 619
620static int is_execute(const struct stat *st)
621{
622 if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
623 (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
624 (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
625 (st->st_mode & S_IXOTH)) return TRUE;
626 return FALSE;
627}
628
351#ifdef BB_FEATURE_USERNAME_COMPLETION 629#ifdef BB_FEATURE_USERNAME_COMPLETION
352static char** username_tab_completion(char *ud, int *num_matches) 630
631static char **username_tab_completion(char *ud, int *num_matches)
353{ 632{
354 static struct passwd *entry; 633 struct passwd *entry;
355 int userlen; 634 int userlen;
356 char **matches = (char **) NULL; 635 char *temp;
357 char *temp;
358 int nm = 0;
359 636
360 setpwent ();
361 userlen = strlen (ud + 1);
362 637
363 while ((entry = getpwent ()) != NULL) { 638 ud++; /* ~user/... to user/... */
364 /* Null usernames should result in all users as possible completions. */ 639 userlen = strlen(ud);
365 if (!userlen || !strncmp (ud + 1, entry->pw_name, userlen)) {
366 640
367 temp = xmalloc (3 + strlen (entry->pw_name)); 641 if (num_matches == 0) { /* "~/..." or "~user/..." */
368 sprintf(temp, "~%s/", entry->pw_name); 642 char *sav_ud = ud - 1;
643 char *home = 0;
369 644
370 matches = xrealloc(matches, (nm+1)*sizeof(char *)); 645 if (*ud == '/') { /* "~/..." */
371 matches[nm++] = temp; 646 home = home_pwd_buf;
372 } 647 } else {
373 } 648 /* "~user/..." */
649 temp = strchr(ud, '/');
650 *temp = 0; /* ~user\0 */
651 entry = getpwnam(ud);
652 *temp = '/'; /* restore ~user/... */
653 ud = temp;
654 if (entry)
655 home = entry->pw_dir;
656 }
657 if (home) {
658 if ((userlen + strlen(home) + 1) < BUFSIZ) {
659 char temp2[BUFSIZ]; /* argument size */
374 660
375 endpwent (); 661 /* /home/user/... */
376 (*num_matches) = nm; 662 sprintf(temp2, "%s%s", home, ud);
377 return (matches); 663 strcpy(sav_ud, temp2);
664 }
665 }
666 return 0; /* void, result save to argument :-) */
667 } else {
668 /* "~[^/]*" */
669 char **matches = (char **) NULL;
670 int nm = 0;
671
672 setpwent();
673
674 while ((entry = getpwent()) != NULL) {
675 /* Null usernames should result in all users as possible completions. */
676 if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
677
678 temp = xmalloc(3 + strlen(entry->pw_name));
679 sprintf(temp, "~%s/", entry->pw_name);
680 matches = xrealloc(matches, (nm + 1) * sizeof(char *));
681
682 matches[nm++] = temp;
683 }
684 }
685
686 endpwent();
687 (*num_matches) = nm;
688 return (matches);
689 }
378} 690}
379#endif 691#endif /* BB_FEATURE_USERNAME_COMPLETION */
380 692
381enum { 693enum {
382 FIND_EXE_ONLY = 0, 694 FIND_EXE_ONLY = 0,
383 FIND_DIR_ONLY = 1, 695 FIND_DIR_ONLY = 1,
384 FIND_FILE_ONLY = 2, 696 FIND_FILE_ONLY = 2,
385}; 697};
386 698
387#include <dirent.h>
388
389static int path_parse(char ***p, int flags) 699static int path_parse(char ***p, int flags)
390{ 700{
391 int npth; 701 int npth;
392 char *tmp; 702 char *tmp;
393 char *pth; 703 char *pth;
394 704
395 if(flags!=FIND_EXE_ONLY || (pth=getenv("PATH"))==0) {
396 /* if not setenv PATH variable, to search cur dir "." */ 705 /* if not setenv PATH variable, to search cur dir "." */
397 (*p) = xmalloc(sizeof(char *)); 706 if (flags != FIND_EXE_ONLY || (pth = getenv("PATH")) == 0 ||
398 (*p)[0] = xstrdup("."); 707 /* PATH=<empty> or PATH=:<empty> */
708 *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
399 return 1; 709 return 1;
400 } 710 }
401 711
402 tmp = pth; 712 tmp = pth;
403 npth=0; 713 npth = 0;
404 714
405 for(;;) { 715 for (;;) {
406 npth++; /* count words is + 1 count ':' */ 716 npth++; /* count words is + 1 count ':' */
407 tmp = strchr(tmp, ':'); 717 tmp = strchr(tmp, ':');
408 if(tmp) 718 if (tmp) {
409 tmp++; 719 if (*++tmp == 0)
410 else 720 break; /* :<empty> */
721 } else
411 break; 722 break;
412 } 723 }
413 724
414 *p = xmalloc(npth*sizeof(char *)); 725 *p = xmalloc(npth * sizeof(char *));
415 726
416 tmp = pth; 727 tmp = pth;
417 (*p)[0] = xstrdup(tmp); 728 (*p)[0] = xstrdup(tmp);
418 npth=1; /* count words is + 1 count ':' */ 729 npth = 1; /* count words is + 1 count ':' */
419 730
420 for(;;) { 731 for (;;) {
421 tmp = strchr(tmp, ':'); 732 tmp = strchr(tmp, ':');
422 if(tmp) { 733 if (tmp) {
423 (*p)[0][(tmp-pth)]=0; /* ':' -> '\0'*/ 734 (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */
424 tmp++; 735 if (*++tmp == 0)
736 break; /* :<empty> */
425 } else 737 } else
426 break; 738 break;
427 (*p)[npth++] = &(*p)[0][(tmp-pth)]; /* p[next]=p[0][&'\0'+1] */ 739 (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */
428 } 740 }
429 741
430 return npth; 742 return npth;
431} 743}
432 744
433static char** exe_n_cwd_tab_completion(char* command, int *num_matches, int type) 745static char *add_quote_for_spec_chars(char *found)
434{ 746{
435 char *dirName; 747 int l = 0;
436 char **matches = 0; 748 char *s = xmalloc((strlen(found) + 1) * 2);
437 DIR *dir;
438 struct dirent *next;
439 char cmd [BUFSIZ+4];
440 char *dirbuf;
441 char found [BUFSIZ+4];
442 int nm = *num_matches;
443 struct stat st;
444 char **paths;
445 int npaths;
446 int i;
447 char full_pth[BUFSIZ+4+PATH_MAX];
448 749
750 while (*found) {
751 if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
752 s[l++] = '\\';
753 s[l++] = *found++;
754 }
755 s[l] = 0;
756 return s;
757}
449 758
450 strcpy(cmd, command); /* save for change (last '/' to '\0') */ 759static char **exe_n_cwd_tab_completion(char *command, int *num_matches,
760 int type)
761{
451 762
452 dirName = strrchr(cmd, '/'); 763 char **matches = 0;
453 if(dirName==NULL) { 764 DIR *dir;
765 struct dirent *next;
766 char dirbuf[BUFSIZ];
767 int nm = *num_matches;
768 struct stat st;
769 char *path1[1];
770 char **paths = path1;
771 int npaths;
772 int i;
773 char found[BUFSIZ + 4 + PATH_MAX];
774 char *pfind = strrchr(command, '/');
775
776 path1[0] = ".";
777
778 if (pfind == NULL) {
454 /* no dir, if flags==EXE_ONLY - get paths, else "." */ 779 /* no dir, if flags==EXE_ONLY - get paths, else "." */
455 npaths = path_parse(&paths, type); 780 npaths = path_parse(&paths, type);
456 if(npaths==0) 781 pfind = command;
457 return 0;
458 } else { 782 } else {
459 /* with dir */ 783 /* with dir */
784 /* save for change */
785 strcpy(dirbuf, command);
786 /* set dir only */
787 dirbuf[(pfind - command) + 1] = 0;
788#ifdef BB_FEATURE_USERNAME_COMPLETION
789 if (dirbuf[0] == '~') /* ~/... or ~user/... */
790 username_tab_completion(dirbuf, 0);
791#endif
792 /* "strip" dirname in command */
793 pfind++;
460 794
461 /* save dir */
462 dirbuf = xstrdup(cmd);
463 /* set only dirname */
464 dirbuf[(dirName-cmd)+1]=0;
465
466 /* strip dirname in cmd */
467 strcpy(cmd, dirName+1);
468
469 paths = xmalloc(sizeof(char*));
470 paths[0] = dirbuf; 795 paths[0] = dirbuf;
471 npaths = 1; /* only 1 dir */ 796 npaths = 1; /* only 1 dir */
472 } 797 }
473 798
474 for(i=0; i < npaths; i++) { 799 for (i = 0; i < npaths; i++) {
475 800
476 dir = opendir(paths[i]); 801 dir = opendir(paths[i]);
477 if (!dir) { 802 if (!dir) /* Don't print an error */
478 /* Don't print an error, just shut up and return */ 803 continue;
479 return (matches); 804
480 } 805 while ((next = readdir(dir)) != NULL) {
481 while ((next = readdir(dir)) != NULL) { 806 const char *str_merge = "%s/%s";
807 char *str_found = next->d_name;
808
482 /* matched ? */ 809 /* matched ? */
483 if(strncmp(next->d_name, cmd, strlen(cmd))) 810 if (strncmp(str_found, pfind, strlen(pfind)))
484 continue; 811 continue;
485 /* not see .name without .match */ 812 /* not see .name without .match */
486 if(*next->d_name == '.' && *cmd != '.') 813 if (*str_found == '.' && *pfind == 0) {
487 continue; 814 if (*paths[i] == '/' && paths[i][1] == 0
488 sprintf(full_pth, "%s/%s", paths[i], next->d_name); 815 && str_found[1] == 0) str_found = ""; /* only "/" */
489 /* hmm, remover in progress? */ 816 else
490 if(stat(full_pth, &st)<0)
491 continue; 817 continue;
492 /* Cool, found a match. */ 818 }
819 if (paths[i][strlen(paths[i]) - 1] == '/')
820 str_merge = "%s%s";
821 sprintf(found, str_merge, paths[i], str_found);
822 /* hmm, remover in progress? */
823 if (stat(found, &st) < 0)
824 continue;
825 /* find with dirs ? */
826 if (paths[i] != dirbuf)
827 strcpy(found, next->d_name); /* only name */
493 if (S_ISDIR(st.st_mode)) { 828 if (S_ISDIR(st.st_mode)) {
494 /* name is directory */ 829 /* name is directory */
495 strcpy(found, next->d_name); 830 /* algorithmic only "/" ? */
496 strcat(found, "/"); 831 if (*str_found)
497 if(type==FIND_DIR_ONLY) 832 strcat(found, "/");
498 strcat(found, " "); 833 str_found = add_quote_for_spec_chars(found);
499 } else { 834 } else {
500 /* not put found file if search only dirs for cd */ 835 /* not put found file if search only dirs for cd */
501 if(type==FIND_DIR_ONLY) 836 if (type == FIND_DIR_ONLY)
502 continue; 837 continue;
503 strcpy(found, next->d_name); 838 str_found = add_quote_for_spec_chars(found);
504 strcat(found, " "); 839 if (type == FIND_FILE_ONLY ||
505 } 840 (type == FIND_EXE_ONLY && is_execute(&st) == TRUE))
841 strcat(str_found, " ");
842 }
506 /* Add it to the list */ 843 /* Add it to the list */
507 matches = xrealloc(matches, (nm+1)*sizeof(char *)); 844 matches = xrealloc(matches, (nm + 1) * sizeof(char *));
508 matches[nm++] = xstrdup(found); 845
846 matches[nm++] = str_found;
509 } 847 }
848 closedir(dir);
849 }
850 if (paths != path1) {
851 free(paths[0]); /* allocated memory only in first member */
852 free(paths);
510 } 853 }
511 free(paths[0]); /* allocate memory only in first member */
512 free(paths);
513 *num_matches = nm; 854 *num_matches = nm;
514 return (matches); 855 return (matches);
515} 856}
516 857
517static void input_tab(int lastWasTab) 858static int match_compare(const void *a, const void *b)
518{ 859{
519 /* Do TAB completion */ 860 return strcmp(*(char **) a, *(char **) b);
520 static int num_matches; 861}
521 static char **matches;
522 862
523 char matchBuf[BUFSIZ];
524 863
525 int pos = cursor;
526 int find_type=FIND_FILE_ONLY;
527 864
865#define QUOT (UCHAR_MAX+1)
528 866
529 if (lastWasTab == FALSE) { 867#define collapse_pos(is, in) { \
530 char *tmp, *tmp1; 868 memcpy(int_buf+is, int_buf+in, (BUFSIZ+1-is-in)*sizeof(int)); \
531 int len_found; 869 memcpy(pos_buf+is, pos_buf+in, (BUFSIZ+1-is-in)*sizeof(int)); }
532 870
533 /* For now, we will not bother with trying to distinguish 871static int find_match(char *matchBuf, int *len_with_quotes)
534 * whether the cursor is in/at a command extression -- we 872{
535 * will always try all possible matches. If you don't like 873 int i, j;
536 * that then feel free to fix it. 874 int command_mode;
537 */ 875 int c, c2;
538 876 int int_buf[BUFSIZ + 1];
539 /* Make a local copy of the string -- up 877 int pos_buf[BUFSIZ + 1];
540 * to the position of the cursor */ 878
541 memset(matchBuf, 0, BUFSIZ); 879 /* set to integer dimension characters and own positions */
542 tmp = strncpy(matchBuf, command_ps, cursor); 880 for (i = 0;; i++) {
881 int_buf[i] = (int) ((unsigned char) matchBuf[i]);
882 if (int_buf[i] == 0) {
883 pos_buf[i] = -1; /* indicator end line */
884 break;
885 } else
886 pos_buf[i] = i;
887 }
543 888
544 /* skip past any command seperator tokens */ 889 /* mask \+symbol and convert '\t' to ' ' */
545 while ( (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { 890 for (i = j = 0; matchBuf[i]; i++, j++)
546 tmp = ++tmp1; 891 if (matchBuf[i] == '\\') {
892 collapse_pos(j, j + 1);
893 int_buf[j] |= QUOT;
894 i++;
895#ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT
896 if (matchBuf[i] == '\t') /* algorithm equivalent */
897 int_buf[j] = ' ' | QUOT;
898#endif
547 } 899 }
900#ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT
901 else if (matchBuf[i] == '\t')
902 int_buf[j] = ' ';
903#endif
548 904
549 /* skip any leading white space */ 905 /* mask "symbols" or 'symbols' */
550 while (*tmp == ' ') 906 c2 = 0;
551 tmp++; 907 for (i = 0; int_buf[i]; i++) {
908 c = int_buf[i];
909 if (c == '\'' || c == '"') {
910 if (c2 == 0)
911 c2 = c;
912 else {
913 if (c == c2)
914 c2 = 0;
915 else
916 int_buf[i] |= QUOT;
917 }
918 } else if (c2 != 0 && c != '$')
919 int_buf[i] |= QUOT;
920 }
552 921
553 if(strncmp(tmp, "cd ", 3)==0) 922 /* skip commands with arguments if line have commands delimiters */
554 find_type = FIND_DIR_ONLY; 923 /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
555 else if(strchr(tmp, ' ')==NULL) 924 for (i = 0; int_buf[i]; i++) {
556 find_type = FIND_EXE_ONLY; 925 c = int_buf[i];
926 c2 = int_buf[i + 1];
927 j = i ? int_buf[i - 1] : -1;
928 command_mode = 0;
929 if (c == ';' || c == '&' || c == '|') {
930 command_mode = 1 + (c == c2);
931 if (c == '&') {
932 if (j == '>' || j == '<')
933 command_mode = 0;
934 } else if (c == '|' && j == '>')
935 command_mode = 0;
936 }
937 if (command_mode) {
938 collapse_pos(0, i + command_mode);
939 i = -1; /* hack incremet */
940 }
941 }
942 /* collapse `command...` */
943 for (i = 0; int_buf[i]; i++)
944 if (int_buf[i] == '`') {
945 for (j = i + 1; int_buf[j]; j++)
946 if (int_buf[j] == '`') {
947 collapse_pos(i, j + 1);
948 j = 0;
949 break;
950 }
951 if (j) {
952 /* not found close ` - command mode, collapse all previous */
953 collapse_pos(0, i + 1);
954 break;
955 } else
956 i--; /* hack incremet */
957 }
557 958
558 /* find begin curent word */ 959 /* collapse (command...(command...)...) or {command...{command...}...} */
559 if( (tmp1=strrchr(tmp, ' ')) != NULL) { 960 c = 0; /* "recursive" level */
560 tmp = ++tmp1; 961 c2 = 0;
962 for (i = 0; int_buf[i]; i++)
963 if (int_buf[i] == '(' || int_buf[i] == '{') {
964 if (int_buf[i] == '(')
965 c++;
966 else
967 c2++;
968 collapse_pos(0, i + 1);
969 i = -1; /* hack incremet */
970 }
971 for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
972 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
973 if (int_buf[i] == ')')
974 c--;
975 else
976 c2--;
977 collapse_pos(0, i + 1);
978 i = -1; /* hack incremet */
561 } 979 }
562 strcpy(matchBuf, tmp);
563 980
564 /* Free up any memory already allocated */ 981 /* skip first not quote space */
982 for (i = 0; int_buf[i]; i++)
983 if (int_buf[i] != ' ')
984 break;
985 if (i)
986 collapse_pos(0, i);
987
988 /* set find mode for completion */
989 command_mode = FIND_EXE_ONLY;
990 for (i = 0; int_buf[i]; i++)
991 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
992 if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
993 && strncmp(&matchBuf[pos_buf[0]], "cd", 2) == 0)
994 command_mode = FIND_DIR_ONLY;
995 else {
996 command_mode = FIND_FILE_ONLY;
997 break;
998 }
999 }
1000 /* "strlen" */
1001 for (i = 0; int_buf[i]; i++);
1002 /* find last word */
1003 for (--i; i >= 0; i--) {
1004 c = int_buf[i];
1005 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
1006 collapse_pos(0, i + 1);
1007 break;
1008 }
1009 }
1010 /* skip first not quoted '\'' or '"' */
1011 for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
1012 /* collapse quote or unquote // or /~ */
1013 while ((int_buf[i] & ~QUOT) == '/' && (
1014 (int_buf[i + 1] & ~QUOT) == '/'
1015 || (int_buf[i + 1] & ~QUOT) ==
1016 '~')) i++;
1017 if (i)
1018 collapse_pos(0, i);
1019
1020 /* set only match and destroy quotes */
1021 j = 0;
1022 for (i = 0; pos_buf[i] >= 0; i++) {
1023 matchBuf[i] = matchBuf[pos_buf[i]];
1024 j = pos_buf[i] + 1;
1025 }
1026 matchBuf[i] = 0;
1027 /* old lenght matchBuf with quotes symbols */
1028 *len_with_quotes = j ? j - pos_buf[0] : 0;
1029
1030 return command_mode;
1031}
1032
1033
1034static void input_tab(int *lastWasTab)
1035{
1036 /* Do TAB completion */
1037 static int num_matches;
1038 static char **matches;
1039
1040 if (lastWasTab == 0) { /* free all memory */
565 if (matches) { 1041 if (matches) {
566 while(num_matches>0) 1042 while (num_matches > 0)
567 free(matches[--num_matches]); 1043 free(matches[--num_matches]);
568 free(matches); 1044 free(matches);
569 matches = (char **) NULL; 1045 matches = (char **) NULL;
570 } 1046 }
1047 return;
1048 }
1049 if (*lastWasTab == FALSE) {
1050
1051 char *tmp;
1052 int len_found;
1053 char matchBuf[BUFSIZ];
1054 int find_type;
1055 int recalc_pos;
1056
1057 *lastWasTab = TRUE; /* flop trigger */
1058
1059 /* Make a local copy of the string -- up
1060 * to the position of the cursor */
1061 tmp = strncpy(matchBuf, command_ps, cursor);
1062 tmp[cursor] = 0;
1063
1064 find_type = find_match(matchBuf, &recalc_pos);
1065
1066 /* Free up any memory already allocated */
1067 input_tab(0);
571 1068
572#ifdef BB_FEATURE_USERNAME_COMPLETION 1069#ifdef BB_FEATURE_USERNAME_COMPLETION
573 /* If the word starts with `~' and there is no slash in the word, 1070 /* If the word starts with `~' and there is no slash in the word,
574 * then try completing this word as a username. */ 1071 * then try completing this word as a username. */
575 1072
576 if (matchBuf[0]=='~' && strchr(matchBuf, '/')==0) { 1073 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
577 matches = username_tab_completion(matchBuf, &num_matches); 1074 matches = username_tab_completion(matchBuf, &num_matches);
578 }
579#endif 1075#endif
580 /* Try to match any executable in our path and everything 1076 /* Try to match any executable in our path and everything
581 * in the current working directory that matches. */ 1077 * in the current working directory that matches. */
582 if (!matches) 1078 if (!matches)
583 matches = exe_n_cwd_tab_completion(matchBuf, &num_matches, find_type); 1079 matches =
1080 exe_n_cwd_tab_completion(matchBuf, &num_matches,
1081 find_type);
584 1082
585 /* Did we find exactly one match? */ 1083 /* Did we find exactly one match? */
586 if(!matches || num_matches>1) { 1084 if (!matches || num_matches > 1) {
1085 char *tmp1;
1086
587 beep(); 1087 beep();
588 return; 1088 if (!matches)
1089 return; /* not found */
1090 /* sort */
1091 qsort(matches, num_matches, sizeof(char *), match_compare);
1092
1093 /* find minimal match */
1094 tmp = xstrdup(matches[0]);
1095 for (tmp1 = tmp; *tmp1; tmp1++)
1096 for (len_found = 1; len_found < num_matches; len_found++)
1097 if (matches[len_found][(tmp1 - tmp)] != *tmp1) {
1098 *tmp1 = 0;
1099 break;
1100 }
1101 if (*tmp == 0) { /* have unique */
1102 free(tmp);
1103 return;
1104 }
1105 } else { /* one match */
1106 tmp = matches[0];
1107 /* for next completion current found */
1108 *lastWasTab = FALSE;
589 } 1109 }
590 1110
591 len_found = strlen(matches[0]); 1111 len_found = strlen(tmp);
592
593 /* have space to placed match? */ 1112 /* have space to placed match? */
594 if ( (len_found-strlen(matchBuf)+len) < BUFSIZ ) { 1113 if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
595 1114
596 int recalc_pos = len; 1115 /* before word for match */
597 1116 command_ps[cursor - recalc_pos] = 0;
598 /* before word for match */ 1117 /* save tail line */
599 command_ps[pos-strlen(matchBuf)]=0; 1118 strcpy(matchBuf, command_ps + cursor);
600 1119 /* add match */
601 /* tail line */ 1120 strcat(command_ps, tmp);
602 strcpy(matchBuf, command_ps+pos); 1121 /* add tail */
603
604 /* add match */
605 strcat(command_ps, matches[0]);
606 /* add tail */
607 strcat(command_ps, matchBuf); 1122 strcat(command_ps, matchBuf);
608 1123 /* back to begin word for match */
609 /* write out the matched command */ 1124 input_backward(recalc_pos);
610 len=strlen(command_ps); 1125 /* new pos */
611 recalc_pos = len-recalc_pos+pos; 1126 recalc_pos = cursor + len_found;
612 input_end(); /* write */ 1127 /* new len */
613 while(recalc_pos<cursor) 1128 len = strlen(command_ps);
614 input_backward(); 1129 /* write out the matched command */
615 return; 1130 input_end();
1131 input_backward(cursor - recalc_pos);
616 } 1132 }
1133 if (tmp != matches[0])
1134 free(tmp);
617 } else { 1135 } else {
618 /* Ok -- the last char was a TAB. Since they 1136 /* Ok -- the last char was a TAB. Since they
619 * just hit TAB again, print a list of all the 1137 * just hit TAB again, print a list of all the
620 * available choices... */ 1138 * available choices... */
621 if ( matches && num_matches>0 ) { 1139 if (matches && num_matches > 0) {
622 int i, col; 1140 int i, col, l;
623 int sav_cursor = cursor; 1141 int sav_cursor = cursor; /* change goto_new_line() */
624 1142
625 /* Go to the next line */ 1143 /* Go to the next line */
626 goto_new_line(); 1144 goto_new_line();
627 for (i=0,col=0; i<num_matches; i++) { 1145 for (i = 0, col = 0; i < num_matches; i++) {
628 printf("%s ", matches[i]); 1146 l = strlen(matches[i]);
629 col += strlen(matches[i])+2; 1147 if (l < 14)
630 col -= (col/cmdedit_termw)*cmdedit_termw; 1148 l = 14;
631 if (col > 60 && matches[i+1] != NULL) { 1149 printf("%-14s ", matches[i]);
1150 if ((l += 2) > 16)
1151 while (l % 16) {
1152 putchar(' ');
1153 l++;
1154 }
1155 col += l;
1156 col -= (col / cmdedit_termw) * cmdedit_termw;
1157 if (col > 60 && matches[i + 1] != NULL) {
632 putchar('\n'); 1158 putchar('\n');
633 col = 0; 1159 col = 0;
634 } 1160 }
635 } 1161 }
636 /* Go to the next line and rewrite the prompt */ 1162 /* Go to the next line and rewrite */
637 printf("\n%s", cmdedit_prompt); 1163 putchar('\n');
638 cmdedit_x = cmdedit_prmt_len; 1164 redraw(0, len - sav_cursor);
639 cmdedit_y = 0;
640 cursor = 0;
641 input_end(); /* Rewrite the command */
642 /* Put the cursor back to where it used to be */
643 while (sav_cursor < cursor)
644 input_backward();
645 } 1165 }
646 } 1166 }
647} 1167}
648#endif 1168#endif /* BB_FEATURE_SH_TAB_COMPLETION */
649 1169
650static void get_previous_history(struct history **hp, char* command) 1170static void get_previous_history(struct history **hp, struct history *p)
651{ 1171{
652 if ((*hp)->s) 1172 if ((*hp)->s)
653 free((*hp)->s); 1173 free((*hp)->s);
654 (*hp)->s = strdup(command); 1174 (*hp)->s = xstrdup(command_ps);
655 *hp = (*hp)->p; 1175 *hp = p;
656} 1176}
657 1177
658static void get_next_history(struct history **hp, char* command) 1178static inline void get_next_history(struct history **hp)
659{ 1179{
660 if ((*hp)->s) 1180 get_previous_history(hp, (*hp)->n);
661 free((*hp)->s);
662 (*hp)->s = strdup(command);
663 *hp = (*hp)->n;
664} 1181}
665 1182
1183enum {
1184 ESC = 27,
1185 DEL = 127,
1186};
1187
1188
666/* 1189/*
667 * This function is used to grab a character buffer 1190 * This function is used to grab a character buffer
668 * from the input file descriptor and allows you to 1191 * from the input file descriptor and allows you to
@@ -678,95 +1201,78 @@ static void get_next_history(struct history **hp, char* command)
678 * 1201 *
679 * Furthermore, the "vi" command editing keys are not implemented. 1202 * Furthermore, the "vi" command editing keys are not implemented.
680 * 1203 *
681 * TODO: implement TAB command completion. :)
682 */ 1204 */
683extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) 1205extern void cmdedit_read_input(char *prompt, char command[BUFSIZ])
684{ 1206{
685 1207
686 int inputFd=fileno(stdin); 1208 int inputFd = fileno(stdin);
687 1209
688 int j = 0;
689 int break_out = 0; 1210 int break_out = 0;
690 int ret = 0;
691 int lastWasTab = FALSE; 1211 int lastWasTab = FALSE;
692 char c = 0; 1212 char c = 0;
693 struct history *hp = his_end; 1213 struct history *hp = his_end;
694 1214
1215 /* prepare before init handlers */
1216 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
695 len = 0; 1217 len = 0;
696 cursor = 0;
697 command_ps = command; 1218 command_ps = command;
698 1219
699 if (new_settings.c_cc[VMIN]==0) { 1220 if (new_settings.c_cc[VMIN] == 0) { /* first call */
700 1221
701 getTermSettings(inputFd, (void*) &initial_settings); 1222 getTermSettings(inputFd, (void *) &initial_settings);
702 memcpy(&new_settings, &initial_settings, sizeof(struct termios)); 1223 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
1224
703 new_settings.c_cc[VMIN] = 1; 1225 new_settings.c_cc[VMIN] = 1;
704 new_settings.c_cc[VTIME] = 0; 1226 new_settings.c_cc[VTIME] = 0;
705 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */ 1227 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */
706 new_settings.c_lflag &= ~ICANON; /* unbuffered input */ 1228 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
707 new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */ 1229 new_settings.c_lflag &= ~(ECHO | ECHOCTL | ECHONL); /* Turn off echoing */
708 } 1230 }
709 setTermSettings(inputFd, (void*) &new_settings);
710 handlers_sets |= SET_RESET_TERM;
711 1231
712 memset(command, 0, BUFSIZ); 1232 command[0] = 0;
713 1233
714 cmdedit_init(); 1234 setTermSettings(inputFd, (void *) &new_settings);
1235 handlers_sets |= SET_RESET_TERM;
715 1236
1237 cmdedit_init();
716 /* Print out the command prompt */ 1238 /* Print out the command prompt */
717 cmdedit_prompt = prompt; 1239 parse_prompt(prompt);
718 cmdedit_prmt_len = strlen(prompt);
719 printf("%s", prompt);
720 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
721 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
722
723 1240
724 while (1) { 1241 while (1) {
725 1242
726 fflush(stdout); /* buffered out to fast */ 1243 fflush(stdout); /* buffered out to fast */
727 1244
728 if ((ret = read(inputFd, &c, 1)) < 1) 1245 if (read(inputFd, &c, 1) < 1)
729 return; 1246 return;
730 //fprintf(stderr, "got a '%c' (%d)\n", c, c);
731 1247
732 switch (c) { 1248 switch (c) {
733 case '\n': 1249 case '\n':
734 case '\r': 1250 case '\r':
735 /* Enter */ 1251 /* Enter */
736 *(command + len) = c; 1252 goto_new_line();
737 len++;
738 input_end ();
739 break_out = 1; 1253 break_out = 1;
740 break; 1254 break;
741 case 1: 1255 case 1:
742 /* Control-a -- Beginning of line */ 1256 /* Control-a -- Beginning of line */
743 input_home(); 1257 input_backward(cursor);
744 break; 1258 break;
745 case 2: 1259 case 2:
746 /* Control-b -- Move back one character */ 1260 /* Control-b -- Move back one character */
747 input_backward(); 1261 input_backward(1);
748 break; 1262 break;
749 case 3: 1263 case 3:
750 /* Control-c -- stop gathering input */ 1264 /* Control-c -- stop gathering input */
751 1265
752 /* Link into lash to reset context to 0 on ^C and such */ 1266 /* Link into lash to reset context to 0 on ^C and such */
753 shell_context = 0; 1267 shell_context = 0;
754 1268
755 /* Go to the next line */ 1269 /* Go to the next line */
756 goto_new_line(); 1270 goto_new_line();
1271 command[0] = 0;
757 1272
758#if 0
759 /* Rewrite the prompt */
760 printf("%s", prompt);
761
762 /* Reset the command string */
763 memset(command, 0, BUFSIZ);
764 len = cursor = 0;
765#endif
766 return; 1273 return;
767
768 case 4: 1274 case 4:
769 /* Control-d -- Delete one character, or exit 1275 /* Control-d -- Delete one character, or exit
770 * if the len=0 and no chars to delete */ 1276 * if the len=0 and no chars to delete */
771 if (len == 0) { 1277 if (len == 0) {
772 printf("exit"); 1278 printf("exit");
@@ -790,13 +1296,13 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
790 break; 1296 break;
791 case '\t': 1297 case '\t':
792#ifdef BB_FEATURE_SH_TAB_COMPLETION 1298#ifdef BB_FEATURE_SH_TAB_COMPLETION
793 input_tab(lastWasTab); 1299 input_tab(&lastWasTab);
794#endif 1300#endif
795 break; 1301 break;
796 case 14: 1302 case 14:
797 /* Control-n -- Get next command in history */ 1303 /* Control-n -- Get next command in history */
798 if (hp && hp->n && hp->n->s) { 1304 if (hp && hp->n && hp->n->s) {
799 get_next_history(&hp, command); 1305 get_next_history(&hp);
800 goto rewrite_line; 1306 goto rewrite_line;
801 } else { 1307 } else {
802 beep(); 1308 beep();
@@ -805,114 +1311,111 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
805 case 16: 1311 case 16:
806 /* Control-p -- Get previous command from history */ 1312 /* Control-p -- Get previous command from history */
807 if (hp && hp->p) { 1313 if (hp && hp->p) {
808 get_previous_history(&hp, command); 1314 get_previous_history(&hp, hp->p);
809 goto rewrite_line; 1315 goto rewrite_line;
810 } else { 1316 } else {
811 beep(); 1317 beep();
812 } 1318 }
813 break; 1319 break;
1320 case 21:
1321 /* Control-U -- Clear line before cursor */
1322 if (cursor) {
1323 strcpy(command, command + cursor);
1324 redraw(cmdedit_y, len -= cursor);
1325 }
1326 break;
1327
814 case ESC:{ 1328 case ESC:{
815 /* escape sequence follows */ 1329 /* escape sequence follows */
816 if ((ret = read(inputFd, &c, 1)) < 1) 1330 if (read(inputFd, &c, 1) < 1)
1331 return;
1332 /* different vt100 emulations */
1333 if (c == '[' || c == 'O') {
1334 if (read(inputFd, &c, 1) < 1)
817 return; 1335 return;
1336 }
1337 switch (c) {
1338#ifdef BB_FEATURE_SH_TAB_COMPLETION
1339 case '\t': /* Alt-Tab */
818 1340
819 if (c == '[') { /* 91 */ 1341 input_tab(&lastWasTab);
820 if ((ret = read(inputFd, &c, 1)) < 1) 1342 break;
821 return; 1343#endif
822 1344 case 'A':
823 switch (c) { 1345 /* Up Arrow -- Get previous command from history */
824 case 'A': 1346 if (hp && hp->p) {
825 /* Up Arrow -- Get previous command from history */ 1347 get_previous_history(&hp, hp->p);
826 if (hp && hp->p) { 1348 goto rewrite_line;
827 get_previous_history(&hp, command); 1349 } else {
828 goto rewrite_line; 1350 beep();
829 } else {
830 beep();
831 }
832 break;
833 case 'B':
834 /* Down Arrow -- Get next command in history */
835 if (hp && hp->n && hp->n->s) {
836 get_next_history(&hp, command);
837 goto rewrite_line;
838 } else {
839 beep();
840 }
841 break;
842
843 /* Rewrite the line with the selected history item */
844 rewrite_line:
845 /* return to begin of line */
846 input_home ();
847 /* for next memmoves without set '\0' */
848 memset (command, 0, BUFSIZ);
849 /* change command */
850 strcpy (command, hp->s);
851 /* write new command */
852 for (j=0; command[j]; j++)
853 cmdedit_set_out_char(command[j], 0);
854 ret = cursor;
855 /* erase tail if required */
856 for (j = ret; j < len; j++)
857 cmdedit_set_out_char(' ', 0);
858 /* and backward cursor */
859 for (j = ret; j < len; j++)
860 input_backward();
861 len = cursor; /* set new len */
862 break;
863 case 'C':
864 /* Right Arrow -- Move forward one character */
865 input_forward();
866 break;
867 case 'D':
868 /* Left Arrow -- Move back one character */
869 input_backward();
870 break;
871 case '3':
872 /* Delete */
873 input_delete();
874 break;
875 case '1':
876 /* Home (Ctrl-A) */
877 input_home();
878 break;
879 case '4':
880 /* End (Ctrl-E) */
881 input_end();
882 break;
883 default:
884 beep();
885 }
886 if (c == '1' || c == '3' || c == '4')
887 if ((ret = read(inputFd, &c, 1)) < 1)
888 return; /* read 126 (~) */
889 } 1351 }
890 if (c == 'O') { 1352 break;
891 /* 79 */ 1353 case 'B':
892 if ((ret = read(inputFd, &c, 1)) < 1) 1354 /* Down Arrow -- Get next command in history */
893 return; 1355 if (hp && hp->n && hp->n->s) {
894 switch (c) { 1356 get_next_history(&hp);
895 case 'H': 1357 goto rewrite_line;
896 /* Home (xterm) */ 1358 } else {
897 input_home(); 1359 beep();
898 break;
899 case 'F':
900 /* End (xterm) */
901 input_end();
902 break;
903 default:
904 beep();
905 }
906 } 1360 }
907 c = 0;
908 break; 1361 break;
1362
1363 /* Rewrite the line with the selected history item */
1364 rewrite_line:
1365 /* change command */
1366 len = strlen(strcpy(command, hp->s));
1367 /* redraw and go to end line */
1368 redraw(cmdedit_y, 0);
1369 break;
1370 case 'C':
1371 /* Right Arrow -- Move forward one character */
1372 input_forward();
1373 break;
1374 case 'D':
1375 /* Left Arrow -- Move back one character */
1376 input_backward(1);
1377 break;
1378 case '3':
1379 /* Delete */
1380 input_delete();
1381 break;
1382 case '1':
1383 case 'H':
1384 /* Home (Ctrl-A) */
1385 input_backward(cursor);
1386 break;
1387 case '4':
1388 case 'F':
1389 /* End (Ctrl-E) */
1390 input_end();
1391 break;
1392 default:
1393 if (!(c >= '1' && c <= '9'))
1394 c = 0;
1395 beep();
909 } 1396 }
1397 if (c >= '1' && c <= '9')
1398 do
1399 if (read(inputFd, &c, 1) < 1)
1400 return;
1401 while (c != '~');
1402 break;
1403 }
910 1404
911 default: /* If it's regular input, do the normal thing */ 1405 default: /* If it's regular input, do the normal thing */
912 1406#ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT
913 if (!isprint(c)) { /* Skip non-printable characters */ 1407 /* Control-V -- Add non-printable symbol */
1408 if (c == 22) {
1409 if (read(inputFd, &c, 1) < 1)
1410 return;
1411 if (c == 0) {
1412 beep();
1413 break;
1414 }
1415 } else
1416#endif
1417 if (!isprint(c)) /* Skip non-printable characters */
914 break; 1418 break;
915 }
916 1419
917 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ 1420 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
918 break; 1421 break;
@@ -921,45 +1424,41 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
921 1424
922 if (cursor == (len - 1)) { /* Append if at the end of the line */ 1425 if (cursor == (len - 1)) { /* Append if at the end of the line */
923 *(command + cursor) = c; 1426 *(command + cursor) = c;
924 cmdedit_set_out_char(c, command[cursor+1]); 1427 *(command + cursor + 1) = 0;
1428 cmdedit_set_out_char(0);
925 } else { /* Insert otherwise */ 1429 } else { /* Insert otherwise */
926 memmove(command + cursor + 1, command + cursor, 1430 int sc = cursor;
927 len - cursor - 1);
928 1431
929 *(command + cursor) = c; 1432 memmove(command + sc + 1, command + sc, len - sc);
930 j = cursor+1; 1433 *(command + sc) = c;
1434 sc++;
931 /* rewrite from cursor */ 1435 /* rewrite from cursor */
932 input_end (); 1436 input_end();
933 /* to prev x pos + 1 */ 1437 /* to prev x pos + 1 */
934 while(cursor > j) 1438 input_backward(cursor - sc);
935 input_backward();
936 } 1439 }
937 1440
938 break; 1441 break;
939 } 1442 }
940 if (c == '\t')
941 lastWasTab = TRUE;
942 else
943 lastWasTab = FALSE;
944
945 if (break_out) /* Enter is the command terminator, no more input. */ 1443 if (break_out) /* Enter is the command terminator, no more input. */
946 break; 1444 break;
1445
1446 if (c != '\t')
1447 lastWasTab = FALSE;
947 } 1448 }
948 1449
949 setTermSettings (inputFd, (void *) &initial_settings); 1450 setTermSettings(inputFd, (void *) &initial_settings);
950 handlers_sets &= ~SET_RESET_TERM; 1451 handlers_sets &= ~SET_RESET_TERM;
951 1452
952 /* Handle command history log */ 1453 /* Handle command history log */
953 if (len>1) { /* no put empty line (only '\n') */ 1454 if (len) { /* no put empty line */
954 1455
955 struct history *h = his_end; 1456 struct history *h = his_end;
956 char *ss; 1457 char *ss;
957 1458
958 command[len-1] = 0; /* destroy end '\n' */ 1459 ss = xstrdup(command); /* duplicate */
959 ss = strdup(command); /* duplicate without '\n' */
960 command[len-1] = '\n'; /* restore '\n' */
961 1460
962 if (!h) { 1461 if (h == 0) {
963 /* No previous history -- this memory is never freed */ 1462 /* No previous history -- this memory is never freed */
964 h = his_front = xmalloc(sizeof(struct history)); 1463 h = his_front = xmalloc(sizeof(struct history));
965 h->n = xmalloc(sizeof(struct history)); 1464 h->n = xmalloc(sizeof(struct history));
@@ -994,8 +1493,18 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
994 history_counter++; 1493 history_counter++;
995 } 1494 }
996 } 1495 }
1496#if defined(BB_FEATURE_BASH_STYLE_PROMT)
1497 num_ok_lines++;
1498#endif
997 } 1499 }
998 1500 command[len++] = '\n'; /* set '\n' */
1501 command[len] = 0;
1502#if defined(BB_FEATURE_CLEAN_UP) && defined(BB_FEATURE_SH_TAB_COMPLETION)
1503 input_tab(0); /* strong free */
1504#endif
1505#if defined(BB_FEATURE_BASH_STYLE_PROMT)
1506 free(cmdedit_prompt);
1507#endif
999 return; 1508 return;
1000} 1509}
1001 1510
@@ -1004,7 +1513,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
1004extern void cmdedit_terminate(void) 1513extern void cmdedit_terminate(void)
1005{ 1514{
1006 cmdedit_reset_term(); 1515 cmdedit_reset_term();
1007 if((handlers_sets & SET_TERM_HANDLERS)!=0) { 1516 if ((handlers_sets & SET_TERM_HANDLERS) != 0) {
1008 signal(SIGKILL, SIG_DFL); 1517 signal(SIGKILL, SIG_DFL);
1009 signal(SIGINT, SIG_DFL); 1518 signal(SIGINT, SIG_DFL);
1010 signal(SIGQUIT, SIG_DFL); 1519 signal(SIGQUIT, SIG_DFL);
@@ -1014,6 +1523,32 @@ extern void cmdedit_terminate(void)
1014 } 1523 }
1015} 1524}
1016 1525
1526#endif /* BB_FEATURE_SH_COMMAND_EDITING */
1527
1528
1529#ifdef TEST
1530
1531unsigned int shell_context;
1017 1532
1533int main(int argc, char **argv)
1534{
1535 char buff[BUFSIZ];
1536 char *prompt =
1537#if defined(BB_FEATURE_BASH_STYLE_PROMT)
1538 "\\[\\033[32;1m\\]\\u@\\[\\033[33;1m\\]\\h:\
1539\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
1540\\!\\[\\033[36;1m\\]\\$ \\[\\033[0m\\]";
1541#else
1542 "% ";
1543#endif
1544
1545 shell_context = 1;
1546 do {
1547 cmdedit_read_input(prompt, buff);
1548 printf("*** cmdedit_read_input() returned line =%s=\n", buff);
1549 } while (shell_context);
1550 printf("*** cmdedit_read_input() detect ^C\n");
1551 return 0;
1552}
1018 1553
1019#endif /* BB_FEATURE_SH_COMMAND_EDITING */ 1554#endif /* TEST */
diff --git a/shell/cmdedit.c b/shell/cmdedit.c
index 2e57b9a34..6a53c12f6 100644
--- a/shell/cmdedit.c
+++ b/shell/cmdedit.c
@@ -1,21 +1,18 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* 2/*
3 * Termios command line History and Editting, originally 3 * Termios command line History and Editting.
4 * intended for NetBSD sh (ash)
5 * Copyright (c) 1999
6 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu>
7 * Etc: Dave Cinege <dcinege@psychosis.com>
8 * Majorly adjusted/re-written for busybox:
9 * Erik Andersen <andersee@debian.org>
10 * 4 *
11 * You may use this code as you wish, so long as the original author(s) 5 * Copyright (c) 1986-2001 may safely be consumed by a BSD or GPL license.
12 * are attributed in any redistributions of the source code. 6 * Written by: Vladimir Oleynik <vodz@usa.net>
13 * This code is 'as is' with no warranty. 7 *
14 * This code may safely be consumed by a BSD or GPL license. 8 * Used ideas:
9 * Adam Rogoyski <rogoyski@cs.utexas.edu>
10 * Dave Cinege <dcinege@psychosis.com>
11 * Jakub Jelinek (c) 1995
12 * Erik Andersen <andersee@debian.org> (Majorly adjusted for busybox)
15 * 13 *
16 * v 0.5 19990328 Initial release 14 * This code is 'as is' with no warranty.
17 * 15 *
18 * Future plans: Simple file and path name completion. (like BASH)
19 * 16 *
20 */ 17 */
21 18
@@ -27,20 +24,46 @@
27 to work in an Xterm and console. Ctrl-A also works as Home. 24 to work in an Xterm and console. Ctrl-A also works as Home.
28 Ctrl-E also works as End. 25 Ctrl-E also works as End.
29 26
27 Small bugs (simple effect):
28 - not true viewing if terminal size (x*y symbols) less
29 size (prompt + editor`s line + 2 symbols)
30 */
30 31
31 Editor with vertical scrolling and completion by
32 Vladimir Oleynik. vodz@usa.net (c) 2001
33 32
34 Small bug: not true work if terminal size (x*y symbols) less 33#define TEST
35 size (prompt + editor`s line + 2 symbols)
36 */
37 34
38 35
36#ifndef TEST
39 37
40#include "busybox.h" 38#include "busybox.h"
41 39
40#define D(x)
41
42#else
43
44#define BB_FEATURE_SH_COMMAND_EDITING
45#define BB_FEATURE_SH_TAB_COMPLETION
46#define BB_FEATURE_USERNAME_COMPLETION
47#define BB_FEATURE_NONPRINTABLE_INVERSE_PUT
48#define BB_FEATURE_BASH_STYLE_PROMT
49#define BB_FEATURE_CLEAN_UP
50
51#define TRUE 1
52#define FALSE 0
53#define D(x) x
54
55#endif /* TEST */
56
42#ifdef BB_FEATURE_SH_COMMAND_EDITING 57#ifdef BB_FEATURE_SH_COMMAND_EDITING
43 58
59#ifndef BB_FEATURE_SH_TAB_COMPLETION
60#undef BB_FEATURE_USERNAME_COMPLETION
61#endif
62
63#if defined(BB_FEATURE_USERNAME_COMPLETION) || defined(BB_FEATURE_BASH_STYLE_PROMT)
64#define BB_FEATURE_GETUSERNAME_AND_HOMEDIR
65#endif
66
44#include <stdio.h> 67#include <stdio.h>
45#include <errno.h> 68#include <errno.h>
46#include <unistd.h> 69#include <unistd.h>
@@ -49,26 +72,61 @@
49#include <sys/ioctl.h> 72#include <sys/ioctl.h>
50#include <ctype.h> 73#include <ctype.h>
51#include <signal.h> 74#include <signal.h>
75#include <limits.h>
52 76
53#ifdef BB_FEATURE_SH_TAB_COMPLETION 77#ifdef BB_FEATURE_SH_TAB_COMPLETION
78#include <dirent.h>
54#include <sys/stat.h> 79#include <sys/stat.h>
55#endif 80#endif
56 81
82#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR
83#ifndef TEST
57#include "pwd_grp/pwd.h" 84#include "pwd_grp/pwd.h"
85#else
86#include <pwd.h>
87#endif /* TEST */
88#endif /* advanced FEATURES */
58 89
59 90
60static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */ 91#ifdef TEST
92void *xrealloc(void *old, size_t size)
93{
94 return realloc(old, size);
95}
61 96
62enum { 97void *xmalloc(size_t size)
63 ESC = 27, 98{
64 DEL = 127, 99 return malloc(size);
100}
101char *xstrdup(const char *s)
102{
103 return strdup(s);
104}
105
106void *xcalloc(size_t size, size_t se)
107{
108 return calloc(size, se);
109}
110
111#define error_msg(s, d) fprintf(stderr, s, d)
112#endif
113
114
115struct history {
116 char *s;
117 struct history *p;
118 struct history *n;
65}; 119};
66 120
67#define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0) 121/* Maximum length of the linked list for the command line history */
68#define whitespace(c) (((c) == ' ') || ((c) == '\t')) 122static const int MAX_HISTORY = 15;
123
124/* First element in command line list */
125static struct history *his_front = NULL;
126
127/* Last element in command line list */
128static struct history *his_end = NULL;
69 129
70static struct history *his_front = NULL; /* First element in command line list */
71static struct history *his_end = NULL; /* Last element in command line list */
72 130
73/* ED: sparc termios is broken: revert back to old termio handling. */ 131/* ED: sparc termios is broken: revert back to old termio handling. */
74 132
@@ -87,87 +145,100 @@ static struct history *his_end = NULL; /* Last element in command line list */
87static struct termios initial_settings, new_settings; 145static struct termios initial_settings, new_settings;
88 146
89 147
90#ifndef _POSIX_VDISABLE 148#ifndef _POSIX_VDISABLE
91#define _POSIX_VDISABLE '\0' 149#define _POSIX_VDISABLE '\0'
92#endif 150#endif
93 151
94 152
95static 153static
96volatile int cmdedit_termw; /* actual terminal width */ 154volatile int cmdedit_termw = 80; /* actual terminal width */
97static int history_counter = 0; /* Number of commands in history list */ 155static int history_counter = 0; /* Number of commands in history list */
98
99static 156static
100volatile int handlers_sets = 0; /* Set next bites 157volatile int handlers_sets = 0; /* Set next bites: */
101 when atexit() has been called 158
102 and set many "terminates" signal handlers
103 and winchg signal handler
104 and if the terminal needs to be reset upon exit
105 */
106enum { 159enum {
107 SET_ATEXIT = 1, 160 SET_ATEXIT = 1, /* when atexit() has been called and
108 SET_TERM_HANDLERS = 2, 161 get euid,uid,gid to fast compare */
109 SET_WCHG_HANDLERS = 4, 162 SET_TERM_HANDLERS = 2, /* set many terminates signal handlers */
110 SET_RESET_TERM = 8, 163 SET_WCHG_HANDLERS = 4, /* winchg signal handler */
164 SET_RESET_TERM = 8, /* if the terminal needs to be reset upon exit */
111}; 165};
112 166
113
114static int cmdedit_x; /* real x terminal position,
115 require put prompt in start x position */
116static int cmdedit_y; /* pseudoreal y terminal position */
117static int cmdedit_prmt_len; /* for fast running, without duplicate calculate */
118 167
119static int cursor; /* required global for signal handler */ 168static int cmdedit_x; /* real x terminal position */
120static int len; /* --- "" - - "" - -"- --""-- --""--- */ 169static int cmdedit_y; /* pseudoreal y terminal position */
121static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */ 170static int cmdedit_prmt_len; /* lenght prompt without colores string */
122static const char *cmdedit_prompt;/* --- "" - - "" - -"- --""-- --""--- */
123 171
124/* Link into lash to reset context to 0 172static int cursor; /* required global for signal handler */
125 * on ^C and such */ 173static int len; /* --- "" - - "" - -"- --""-- --""--- */
174static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */
175static
176#ifndef BB_FEATURE_BASH_STYLE_PROMT
177 const
178#endif
179char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */
180
181/* Link into lash to reset context to 0 on ^C and such */
126extern unsigned int shell_context; 182extern unsigned int shell_context;
127 183
128 184
129struct history { 185#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR
130 char *s; 186static char *user_buf = "";
131 struct history *p; 187static char *home_pwd_buf = "";
132 struct history *n; 188static int my_euid;
133}; 189#endif
190
191#ifdef BB_FEATURE_BASH_STYLE_PROMT
192static char *hostname_buf = "";
193static int num_ok_lines = 1;
194#endif
195
196
197#ifdef BB_FEATURE_SH_TAB_COMPLETION
198
199#ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIR
200static int my_euid;
201#endif
202
203static int my_uid;
204static int my_gid;
205
206#endif /* BB_FEATURE_SH_TAB_COMPLETION */
207
134 208
135static void cmdedit_setwidth(int w, int redraw_flg); 209static void cmdedit_setwidth(int w, int redraw_flg);
136 210
137static void win_changed(int nsig) 211static void win_changed(int nsig)
138{ 212{
139 struct winsize win = { 0, 0, 0, 0 }; 213 struct winsize win = { 0, 0, 0, 0 };
140 static __sighandler_t previous_SIGWINCH_handler; /* for reset */ 214 static __sighandler_t previous_SIGWINCH_handler; /* for reset */
141 215
142 /* emulate signal call if not called as a sig handler */ 216 /* emulate || signal call */
143 if(nsig == -SIGWINCH || nsig == SIGWINCH) { 217 if (nsig == -SIGWINCH || nsig == SIGWINCH) {
144 ioctl(0, TIOCGWINSZ, &win); 218 ioctl(0, TIOCGWINSZ, &win);
145 if (win.ws_col > 0) { 219 if (win.ws_col > 0) {
146 cmdedit_setwidth( win.ws_col, nsig == SIGWINCH ); 220 cmdedit_setwidth(win.ws_col, nsig == SIGWINCH);
147 } else { 221 }
148 /* Default to 79 if their console doesn't want to share */
149 cmdedit_setwidth( 79, nsig == SIGWINCH );
150 }
151 } 222 }
152
153 /* Unix not all standart in recall signal */ 223 /* Unix not all standart in recall signal */
154 224
155 if(nsig == -SIGWINCH) /* save previous handler */ 225 if (nsig == -SIGWINCH) /* save previous handler */
156 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); 226 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
157 else if(nsig == SIGWINCH) /* signaled called handler */ 227 else if (nsig == SIGWINCH) /* signaled called handler */
158 signal(SIGWINCH, win_changed); /* set for next call */ 228 signal(SIGWINCH, win_changed); /* set for next call */
159 else /* set previous handler */ 229 else /* nsig == 0 */
160 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */ 230 /* set previous handler */
231 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
161} 232}
162 233
163static void cmdedit_reset_term(void) 234static void cmdedit_reset_term(void)
164{ 235{
165 if((handlers_sets & SET_RESET_TERM)!=0) { 236 if ((handlers_sets & SET_RESET_TERM) != 0) {
166 /* sparc and other have broken termios support: use old termio handling. */ 237 /* sparc and other have broken termios support: use old termio handling. */
167 setTermSettings(fileno(stdin), (void*) &initial_settings); 238 setTermSettings(fileno(stdin), (void *) &initial_settings);
168 handlers_sets &= ~SET_RESET_TERM; 239 handlers_sets &= ~SET_RESET_TERM;
169 } 240 }
170 if((handlers_sets & SET_WCHG_HANDLERS)!=0) { 241 if ((handlers_sets & SET_WCHG_HANDLERS) != 0) {
171 /* reset SIGWINCH handler to previous (default) */ 242 /* reset SIGWINCH handler to previous (default) */
172 win_changed(0); 243 win_changed(0);
173 handlers_sets &= ~SET_WCHG_HANDLERS; 244 handlers_sets &= ~SET_WCHG_HANDLERS;
@@ -176,28 +247,45 @@ static void cmdedit_reset_term(void)
176#ifdef BB_FEATURE_CLEAN_UP 247#ifdef BB_FEATURE_CLEAN_UP
177 if (his_front) { 248 if (his_front) {
178 struct history *n; 249 struct history *n;
250
179 //while(his_front!=his_end) { 251 //while(his_front!=his_end) {
180 while(his_front!=his_end) { 252 while (his_front != his_end) {
181 n = his_front->n; 253 n = his_front->n;
182 free(his_front->s); 254 free(his_front->s);
183 free(his_front); 255 free(his_front);
184 his_front=n; 256 his_front = n;
185 } 257 }
186 } 258 }
187#endif 259#endif
188} 260}
189 261
190 262
191
192/* special for recount position for scroll and remove terminal margin effect */ 263/* special for recount position for scroll and remove terminal margin effect */
193static void cmdedit_set_out_char(int c, int next_char) { 264static void cmdedit_set_out_char(int next_char)
194 putchar(c); 265{
195 if(++cmdedit_x>=cmdedit_termw) { 266
267 int c = command_ps[cursor];
268
269 if (c == 0)
270 c = ' '; /* destroy end char? */
271#ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT
272 if (!isprint(c)) { /* Inverse put non-printable characters */
273 if (((unsigned char) c) >= 128)
274 c -= 128;
275 if (((unsigned char) c) < ' ')
276 c += '@';
277 if (c == 127)
278 c = '?';
279 printf("\033[7m%c\033[0m", c);
280 } else
281#endif
282 putchar(c);
283 if (++cmdedit_x >= cmdedit_termw) {
196 /* terminal is scrolled down */ 284 /* terminal is scrolled down */
197 cmdedit_y++; 285 cmdedit_y++;
198 cmdedit_x=0; 286 cmdedit_x = 0;
199 287
200 if(!next_char) 288 if (!next_char)
201 next_char = ' '; 289 next_char = ' ';
202 /* destroy "(auto)margin" */ 290 /* destroy "(auto)margin" */
203 putchar(next_char); 291 putchar(next_char);
@@ -206,56 +294,221 @@ static void cmdedit_set_out_char(int c, int next_char) {
206 cursor++; 294 cursor++;
207} 295}
208 296
209/* Move to end line. Bonus: rewrite line from cursor without use 297/* Move to end line. Bonus: rewrite line from cursor */
210 special control terminal strings, also saved size and speed! */ 298static void input_end(void)
211static void input_end (void) { 299{
212 while(cursor < len) 300 while (cursor < len)
213 cmdedit_set_out_char(command_ps[cursor], 0); 301 cmdedit_set_out_char(0);
214} 302}
215 303
216/* Go to the next line */ 304/* Go to the next line */
217static void goto_new_line(void) { 305static void goto_new_line(void)
306{
218 input_end(); 307 input_end();
219 cmdedit_set_out_char('\n', 0); 308 if (cmdedit_x)
309 putchar('\n');
220} 310}
221 311
222 312
223static inline void out1str(const char *s) { fputs (s, stdout); } 313static inline void out1str(const char *s)
224static inline void beep (void) { putchar('\007'); } 314{
315 fputs(s, stdout);
316}
317static inline void beep(void)
318{
319 putchar('\007');
320}
225 321
226/* Go to HOME position */ 322/* Move back one charactor */
227static void input_home(void) 323/* special for slow terminal */
324static void input_backward(int num)
228{ 325{
229 while(cmdedit_y>0) { /* up to start y */ 326 if (num > cursor)
230 out1str("\033[A"); 327 num = cursor;
231 cmdedit_y--; 328 cursor -= num; /* new cursor (in command, not terminal) */
329
330 if (cmdedit_x >= num) { /* no to up line */
331 cmdedit_x -= num;
332 if (num < 4)
333 while (num-- > 0)
334 putchar('\b');
335
336 else
337 printf("\033[%dD", num);
338 } else {
339 int count_y;
340
341 if (cmdedit_x) {
342 putchar('\r'); /* back to first terminal pos. */
343 num -= cmdedit_x; /* set previous backward */
344 }
345 count_y = 1 + num / cmdedit_termw;
346 printf("\033[%dA", count_y);
347 cmdedit_y -= count_y;
348 /* require forward after uping */
349 cmdedit_x = cmdedit_termw * count_y - num;
350 printf("\033[%dC", cmdedit_x); /* set term cursor */
232 } 351 }
233 putchar('\r'); 352}
234 cursor = 0; 353
354static void put_prompt(void)
355{
235 out1str(cmdedit_prompt); 356 out1str(cmdedit_prompt);
236 cmdedit_x = cmdedit_prmt_len; 357 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
358 cursor = 0;
359}
237 360
361#ifdef BB_FEATURE_BASH_STYLE_PROMT
362static void
363add_to_prompt(char **prmt_mem_ptr, int *alm, int *prmt_len,
364 const char *addb)
365{
366 int l = strlen(addb);
367
368 *prmt_len += l;
369 if (*alm < (*prmt_len) + 1) {
370 *alm = (*prmt_len) + 1;
371 *prmt_mem_ptr = xrealloc(*prmt_mem_ptr, *alm);
372 }
373 strcat(*prmt_mem_ptr, addb);
238} 374}
375#endif
239 376
240/* Move back one charactor */ 377static void parse_prompt(const char *prmt_ptr)
241static void input_backward(void) { 378{
242 if (cursor > 0) { 379#ifdef BB_FEATURE_BASH_STYLE_PROMT
243 cursor--; 380 int alm = strlen(prmt_ptr) + 1; /* supposedly require memory */
244 if(cmdedit_x!=0) { /* no first position in terminal line */ 381 int prmt_len = 0;
245 putchar('\b'); 382 int sub_len = 0;
246 cmdedit_x--; 383 int flg_not_length = '[';
384 char *prmt_mem_ptr = xstrdup(prmt_ptr);
385 char pwd_buf[PATH_MAX + 1];
386 char buf[16];
387 int c;
388
389 pwd_buf[0] = 0;
390 *prmt_mem_ptr = 0;
391
392 while (*prmt_ptr) {
393 c = *prmt_ptr++;
394 if (c == '\\') {
395 c = *prmt_ptr;
396 if (c == 0)
397 break;
398 prmt_ptr++;
399 switch (c) {
400 case 'u':
401 add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, user_buf);
402 continue;
403 case 'h':
404 if (hostname_buf[0] == 0) {
405 hostname_buf = xcalloc(256, 1);
406 if (gethostname(hostname_buf, 255) < 0) {
407 strcpy(hostname_buf, "?");
408 } else {
409 char *s = strchr(hostname_buf, '.');
410
411 if (s)
412 *s = 0;
413 }
414 }
415 add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len,
416 hostname_buf);
417 continue;
418 case '$':
419 c = my_euid == 0 ? '#' : '$';
420 break;
421 case 'w':
422 if (pwd_buf[0] == 0) {
423 int l;
424
425 getcwd(pwd_buf, PATH_MAX);
426 l = strlen(home_pwd_buf);
427 if (home_pwd_buf[0] != 0 &&
428 strncmp(home_pwd_buf, pwd_buf, l) == 0) {
429 strcpy(pwd_buf + 1, pwd_buf + l);
430 pwd_buf[0] = '~';
431 }
432 }
433 add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, pwd_buf);
434 continue;
435 case '!':
436 snprintf(buf, sizeof(buf), "%d", num_ok_lines);
437 add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, buf);
438 continue;
439 case 'e':
440 case 'E': /* \e \E = \033 */
441 c = '\033';
442 break;
443 case 'x':
444 case 'X':
445 case '0':
446 case '1':
447 case '2':
448 case '3':
449 case '4':
450 case '5':
451 case '6':
452 case '7':{
453 int l;
454 int ho = 0;
455 char *eho;
456
457 if (c == 'X')
458 c = 'x';
459
460 for (l = 0; l < 3;) {
461
462 buf[l++] = *prmt_ptr;
463 buf[l] = 0;
464 ho = strtol(buf, &eho, c == 'x' ? 16 : 8);
465 if (ho > UCHAR_MAX || (eho - buf) < l) {
466 l--;
467 break;
468 }
469 prmt_ptr++;
470 }
471 buf[l] = 0;
472 ho = strtol(buf, 0, c == 'x' ? 16 : 8);
473 c = ho == 0 ? '?' : (char) ho;
474 break;
247 } 475 }
248 else { 476 case '[':
249 out1str("\033[A"); /* up */ 477 case ']':
250 cmdedit_y--; 478 if (c == flg_not_length) {
251 479 flg_not_length = flg_not_length == '[' ? ']' : '[';
252 /* to end in current terminal line */ 480 continue;
253 while(cmdedit_x<(cmdedit_termw-1)) {
254 out1str("\033[C");
255 cmdedit_x++;
256 } 481 }
482 break;
257 } 483 }
484 }
485 buf[0] = c;
486 buf[1] = 0;
487 add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, buf);
488 if (flg_not_length == ']')
489 sub_len++;
258 } 490 }
491 cmdedit_prmt_len = prmt_len - sub_len;
492 cmdedit_prompt = prmt_mem_ptr;
493#else
494 cmdedit_prompt = prmt_ptr;
495 cmdedit_prmt_len = strlen(prmt_ptr);
496#endif
497 put_prompt();
498}
499
500
501/* draw promt, editor line, and clear tail */
502static void redraw(int y, int back_cursor)
503{
504 if (y > 0) /* up to start y */
505 printf("\033[%dA", y);
506 cmdedit_y = 0; /* new quasireal y */
507 putchar('\r');
508 put_prompt();
509 input_end(); /* rewrite */
510 printf("\033[J"); /* destroy tail after cursor */
511 input_backward(back_cursor);
259} 512}
260 513
261/* Delete the char in front of the cursor */ 514/* Delete the char in front of the cursor */
@@ -265,21 +518,20 @@ static void input_delete(void)
265 518
266 if (j == len) 519 if (j == len)
267 return; 520 return;
268 521
269 memmove (command_ps + j, command_ps + j + 1, BUFSIZ - j - 1); 522 strcpy(command_ps + j, command_ps + j + 1);
270 len--; 523 len--;
271 input_end(); /* rewtite new line */ 524 input_end(); /* rewtite new line */
272 cmdedit_set_out_char(' ', 0); /* destroy end char */ 525 cmdedit_set_out_char(0); /* destroy end char */
273 while (j < cursor) 526 input_backward(cursor - j); /* back to old pos cursor */
274 input_backward(); /* back to old pos cursor */
275} 527}
276 528
277/* Delete the char in back of the cursor */ 529/* Delete the char in back of the cursor */
278static void input_backspace(void) 530static void input_backspace(void)
279{ 531{
280 if (cursor > 0) { 532 if (cursor > 0) {
281 input_backward(); 533 input_backward(1);
282 input_delete (); 534 input_delete();
283 } 535 }
284} 536}
285 537
@@ -287,382 +539,653 @@ static void input_backspace(void)
287/* Move forward one charactor */ 539/* Move forward one charactor */
288static void input_forward(void) 540static void input_forward(void)
289{ 541{
290 if (cursor < len) 542 if (cursor < len)
291 cmdedit_set_out_char(command_ps[cursor], command_ps[cursor + 1]); 543 cmdedit_set_out_char(command_ps[cursor + 1]);
292} 544}
293 545
294 546
295static void clean_up_and_die(int sig) 547static void clean_up_and_die(int sig)
296{ 548{
297 goto_new_line(); 549 goto_new_line();
298 if (sig!=SIGINT) 550 if (sig != SIGINT)
299 exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */ 551 exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */
300 cmdedit_reset_term(); 552 cmdedit_reset_term();
301} 553}
302 554
303static void cmdedit_setwidth(int w, int redraw_flg) 555static void cmdedit_setwidth(int w, int redraw_flg)
304{ 556{
305 cmdedit_termw = cmdedit_prmt_len+2; 557 cmdedit_termw = cmdedit_prmt_len + 2;
306 if (w > cmdedit_termw) { 558 if (w > cmdedit_termw) {
307 559
308 cmdedit_termw = w; 560 cmdedit_termw = w;
309 561
310 if(redraw_flg) { 562 if (redraw_flg) {
311 int sav_cursor = cursor; 563 /* new y for current cursor */
312 564 int new_y = (cursor + cmdedit_prmt_len) / w;
313 /* set variables for new terminal size */
314 cmdedit_y = sav_cursor/w;
315 cmdedit_x = sav_cursor-cmdedit_y*w;
316 565
317 /* redraw */ 566 /* redraw */
318 input_home(); 567 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
319 input_end(); 568 fflush(stdout);
320 while(sav_cursor<cursor)
321 input_backward();
322 } 569 }
323 } else { 570 } else {
324 error_msg("\n*** Error: minimum screen width is %d", cmdedit_termw); 571 error_msg("\n*** Error: minimum screen width is %d",
572 cmdedit_termw);
325 } 573 }
326} 574}
327 575
328extern void cmdedit_init(void) 576extern void cmdedit_init(void)
329{ 577{
330 if((handlers_sets & SET_WCHG_HANDLERS)==0) { 578 if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
331 /* pretend we received a signal in order to set term size and sig handling */ 579 /* emulate usage handler to set handler and call yours work */
332 win_changed(-SIGWINCH); 580 win_changed(-SIGWINCH);
333 handlers_sets |= SET_WCHG_HANDLERS; 581 handlers_sets |= SET_WCHG_HANDLERS;
334 } 582 }
335 583
336 if((handlers_sets & SET_ATEXIT)==0) { 584 if ((handlers_sets & SET_ATEXIT) == 0) {
337 atexit(cmdedit_reset_term); /* be sure to do this only once */ 585#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR
586 struct passwd *entry;
587
588 my_euid = geteuid();
589 entry = getpwuid(my_euid);
590 if (entry) {
591 user_buf = xstrdup(entry->pw_name);
592 home_pwd_buf = xstrdup(entry->pw_dir);
593 }
594#endif
595
596#ifdef BB_FEATURE_SH_TAB_COMPLETION
597
598#ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIR
599 my_euid = geteuid();
600#endif
601 my_uid = getuid();
602 my_gid = getgid();
603#endif /* BB_FEATURE_SH_TAB_COMPLETION */
338 handlers_sets |= SET_ATEXIT; 604 handlers_sets |= SET_ATEXIT;
605 atexit(cmdedit_reset_term); /* be sure to do this only once */
339 } 606 }
340 if((handlers_sets & SET_TERM_HANDLERS)==0) { 607
608 if ((handlers_sets & SET_TERM_HANDLERS) == 0) {
341 signal(SIGKILL, clean_up_and_die); 609 signal(SIGKILL, clean_up_and_die);
342 signal(SIGINT, clean_up_and_die); 610 signal(SIGINT, clean_up_and_die);
343 signal(SIGQUIT, clean_up_and_die); 611 signal(SIGQUIT, clean_up_and_die);
344 signal(SIGTERM, clean_up_and_die); 612 signal(SIGTERM, clean_up_and_die);
345 handlers_sets |= SET_TERM_HANDLERS; 613 handlers_sets |= SET_TERM_HANDLERS;
346 } 614 }
615
347} 616}
348 617
349#ifdef BB_FEATURE_SH_TAB_COMPLETION 618#ifdef BB_FEATURE_SH_TAB_COMPLETION
350 619
620static int is_execute(const struct stat *st)
621{
622 if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
623 (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
624 (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
625 (st->st_mode & S_IXOTH)) return TRUE;
626 return FALSE;
627}
628
351#ifdef BB_FEATURE_USERNAME_COMPLETION 629#ifdef BB_FEATURE_USERNAME_COMPLETION
352static char** username_tab_completion(char *ud, int *num_matches) 630
631static char **username_tab_completion(char *ud, int *num_matches)
353{ 632{
354 static struct passwd *entry; 633 struct passwd *entry;
355 int userlen; 634 int userlen;
356 char **matches = (char **) NULL; 635 char *temp;
357 char *temp;
358 int nm = 0;
359 636
360 setpwent ();
361 userlen = strlen (ud + 1);
362 637
363 while ((entry = getpwent ()) != NULL) { 638 ud++; /* ~user/... to user/... */
364 /* Null usernames should result in all users as possible completions. */ 639 userlen = strlen(ud);
365 if (!userlen || !strncmp (ud + 1, entry->pw_name, userlen)) {
366 640
367 temp = xmalloc (3 + strlen (entry->pw_name)); 641 if (num_matches == 0) { /* "~/..." or "~user/..." */
368 sprintf(temp, "~%s/", entry->pw_name); 642 char *sav_ud = ud - 1;
643 char *home = 0;
369 644
370 matches = xrealloc(matches, (nm+1)*sizeof(char *)); 645 if (*ud == '/') { /* "~/..." */
371 matches[nm++] = temp; 646 home = home_pwd_buf;
372 } 647 } else {
373 } 648 /* "~user/..." */
649 temp = strchr(ud, '/');
650 *temp = 0; /* ~user\0 */
651 entry = getpwnam(ud);
652 *temp = '/'; /* restore ~user/... */
653 ud = temp;
654 if (entry)
655 home = entry->pw_dir;
656 }
657 if (home) {
658 if ((userlen + strlen(home) + 1) < BUFSIZ) {
659 char temp2[BUFSIZ]; /* argument size */
374 660
375 endpwent (); 661 /* /home/user/... */
376 (*num_matches) = nm; 662 sprintf(temp2, "%s%s", home, ud);
377 return (matches); 663 strcpy(sav_ud, temp2);
664 }
665 }
666 return 0; /* void, result save to argument :-) */
667 } else {
668 /* "~[^/]*" */
669 char **matches = (char **) NULL;
670 int nm = 0;
671
672 setpwent();
673
674 while ((entry = getpwent()) != NULL) {
675 /* Null usernames should result in all users as possible completions. */
676 if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
677
678 temp = xmalloc(3 + strlen(entry->pw_name));
679 sprintf(temp, "~%s/", entry->pw_name);
680 matches = xrealloc(matches, (nm + 1) * sizeof(char *));
681
682 matches[nm++] = temp;
683 }
684 }
685
686 endpwent();
687 (*num_matches) = nm;
688 return (matches);
689 }
378} 690}
379#endif 691#endif /* BB_FEATURE_USERNAME_COMPLETION */
380 692
381enum { 693enum {
382 FIND_EXE_ONLY = 0, 694 FIND_EXE_ONLY = 0,
383 FIND_DIR_ONLY = 1, 695 FIND_DIR_ONLY = 1,
384 FIND_FILE_ONLY = 2, 696 FIND_FILE_ONLY = 2,
385}; 697};
386 698
387#include <dirent.h>
388
389static int path_parse(char ***p, int flags) 699static int path_parse(char ***p, int flags)
390{ 700{
391 int npth; 701 int npth;
392 char *tmp; 702 char *tmp;
393 char *pth; 703 char *pth;
394 704
395 if(flags!=FIND_EXE_ONLY || (pth=getenv("PATH"))==0) {
396 /* if not setenv PATH variable, to search cur dir "." */ 705 /* if not setenv PATH variable, to search cur dir "." */
397 (*p) = xmalloc(sizeof(char *)); 706 if (flags != FIND_EXE_ONLY || (pth = getenv("PATH")) == 0 ||
398 (*p)[0] = xstrdup("."); 707 /* PATH=<empty> or PATH=:<empty> */
708 *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
399 return 1; 709 return 1;
400 } 710 }
401 711
402 tmp = pth; 712 tmp = pth;
403 npth=0; 713 npth = 0;
404 714
405 for(;;) { 715 for (;;) {
406 npth++; /* count words is + 1 count ':' */ 716 npth++; /* count words is + 1 count ':' */
407 tmp = strchr(tmp, ':'); 717 tmp = strchr(tmp, ':');
408 if(tmp) 718 if (tmp) {
409 tmp++; 719 if (*++tmp == 0)
410 else 720 break; /* :<empty> */
721 } else
411 break; 722 break;
412 } 723 }
413 724
414 *p = xmalloc(npth*sizeof(char *)); 725 *p = xmalloc(npth * sizeof(char *));
415 726
416 tmp = pth; 727 tmp = pth;
417 (*p)[0] = xstrdup(tmp); 728 (*p)[0] = xstrdup(tmp);
418 npth=1; /* count words is + 1 count ':' */ 729 npth = 1; /* count words is + 1 count ':' */
419 730
420 for(;;) { 731 for (;;) {
421 tmp = strchr(tmp, ':'); 732 tmp = strchr(tmp, ':');
422 if(tmp) { 733 if (tmp) {
423 (*p)[0][(tmp-pth)]=0; /* ':' -> '\0'*/ 734 (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */
424 tmp++; 735 if (*++tmp == 0)
736 break; /* :<empty> */
425 } else 737 } else
426 break; 738 break;
427 (*p)[npth++] = &(*p)[0][(tmp-pth)]; /* p[next]=p[0][&'\0'+1] */ 739 (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */
428 } 740 }
429 741
430 return npth; 742 return npth;
431} 743}
432 744
433static char** exe_n_cwd_tab_completion(char* command, int *num_matches, int type) 745static char *add_quote_for_spec_chars(char *found)
434{ 746{
435 char *dirName; 747 int l = 0;
436 char **matches = 0; 748 char *s = xmalloc((strlen(found) + 1) * 2);
437 DIR *dir;
438 struct dirent *next;
439 char cmd [BUFSIZ+4];
440 char *dirbuf;
441 char found [BUFSIZ+4];
442 int nm = *num_matches;
443 struct stat st;
444 char **paths;
445 int npaths;
446 int i;
447 char full_pth[BUFSIZ+4+PATH_MAX];
448 749
750 while (*found) {
751 if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
752 s[l++] = '\\';
753 s[l++] = *found++;
754 }
755 s[l] = 0;
756 return s;
757}
449 758
450 strcpy(cmd, command); /* save for change (last '/' to '\0') */ 759static char **exe_n_cwd_tab_completion(char *command, int *num_matches,
760 int type)
761{
451 762
452 dirName = strrchr(cmd, '/'); 763 char **matches = 0;
453 if(dirName==NULL) { 764 DIR *dir;
765 struct dirent *next;
766 char dirbuf[BUFSIZ];
767 int nm = *num_matches;
768 struct stat st;
769 char *path1[1];
770 char **paths = path1;
771 int npaths;
772 int i;
773 char found[BUFSIZ + 4 + PATH_MAX];
774 char *pfind = strrchr(command, '/');
775
776 path1[0] = ".";
777
778 if (pfind == NULL) {
454 /* no dir, if flags==EXE_ONLY - get paths, else "." */ 779 /* no dir, if flags==EXE_ONLY - get paths, else "." */
455 npaths = path_parse(&paths, type); 780 npaths = path_parse(&paths, type);
456 if(npaths==0) 781 pfind = command;
457 return 0;
458 } else { 782 } else {
459 /* with dir */ 783 /* with dir */
784 /* save for change */
785 strcpy(dirbuf, command);
786 /* set dir only */
787 dirbuf[(pfind - command) + 1] = 0;
788#ifdef BB_FEATURE_USERNAME_COMPLETION
789 if (dirbuf[0] == '~') /* ~/... or ~user/... */
790 username_tab_completion(dirbuf, 0);
791#endif
792 /* "strip" dirname in command */
793 pfind++;
460 794
461 /* save dir */
462 dirbuf = xstrdup(cmd);
463 /* set only dirname */
464 dirbuf[(dirName-cmd)+1]=0;
465
466 /* strip dirname in cmd */
467 strcpy(cmd, dirName+1);
468
469 paths = xmalloc(sizeof(char*));
470 paths[0] = dirbuf; 795 paths[0] = dirbuf;
471 npaths = 1; /* only 1 dir */ 796 npaths = 1; /* only 1 dir */
472 } 797 }
473 798
474 for(i=0; i < npaths; i++) { 799 for (i = 0; i < npaths; i++) {
475 800
476 dir = opendir(paths[i]); 801 dir = opendir(paths[i]);
477 if (!dir) { 802 if (!dir) /* Don't print an error */
478 /* Don't print an error, just shut up and return */ 803 continue;
479 return (matches); 804
480 } 805 while ((next = readdir(dir)) != NULL) {
481 while ((next = readdir(dir)) != NULL) { 806 const char *str_merge = "%s/%s";
807 char *str_found = next->d_name;
808
482 /* matched ? */ 809 /* matched ? */
483 if(strncmp(next->d_name, cmd, strlen(cmd))) 810 if (strncmp(str_found, pfind, strlen(pfind)))
484 continue; 811 continue;
485 /* not see .name without .match */ 812 /* not see .name without .match */
486 if(*next->d_name == '.' && *cmd != '.') 813 if (*str_found == '.' && *pfind == 0) {
487 continue; 814 if (*paths[i] == '/' && paths[i][1] == 0
488 sprintf(full_pth, "%s/%s", paths[i], next->d_name); 815 && str_found[1] == 0) str_found = ""; /* only "/" */
489 /* hmm, remover in progress? */ 816 else
490 if(stat(full_pth, &st)<0)
491 continue; 817 continue;
492 /* Cool, found a match. */ 818 }
819 if (paths[i][strlen(paths[i]) - 1] == '/')
820 str_merge = "%s%s";
821 sprintf(found, str_merge, paths[i], str_found);
822 /* hmm, remover in progress? */
823 if (stat(found, &st) < 0)
824 continue;
825 /* find with dirs ? */
826 if (paths[i] != dirbuf)
827 strcpy(found, next->d_name); /* only name */
493 if (S_ISDIR(st.st_mode)) { 828 if (S_ISDIR(st.st_mode)) {
494 /* name is directory */ 829 /* name is directory */
495 strcpy(found, next->d_name); 830 /* algorithmic only "/" ? */
496 strcat(found, "/"); 831 if (*str_found)
497 if(type==FIND_DIR_ONLY) 832 strcat(found, "/");
498 strcat(found, " "); 833 str_found = add_quote_for_spec_chars(found);
499 } else { 834 } else {
500 /* not put found file if search only dirs for cd */ 835 /* not put found file if search only dirs for cd */
501 if(type==FIND_DIR_ONLY) 836 if (type == FIND_DIR_ONLY)
502 continue; 837 continue;
503 strcpy(found, next->d_name); 838 str_found = add_quote_for_spec_chars(found);
504 strcat(found, " "); 839 if (type == FIND_FILE_ONLY ||
505 } 840 (type == FIND_EXE_ONLY && is_execute(&st) == TRUE))
841 strcat(str_found, " ");
842 }
506 /* Add it to the list */ 843 /* Add it to the list */
507 matches = xrealloc(matches, (nm+1)*sizeof(char *)); 844 matches = xrealloc(matches, (nm + 1) * sizeof(char *));
508 matches[nm++] = xstrdup(found); 845
846 matches[nm++] = str_found;
509 } 847 }
848 closedir(dir);
849 }
850 if (paths != path1) {
851 free(paths[0]); /* allocated memory only in first member */
852 free(paths);
510 } 853 }
511 free(paths[0]); /* allocate memory only in first member */
512 free(paths);
513 *num_matches = nm; 854 *num_matches = nm;
514 return (matches); 855 return (matches);
515} 856}
516 857
517static void input_tab(int lastWasTab) 858static int match_compare(const void *a, const void *b)
518{ 859{
519 /* Do TAB completion */ 860 return strcmp(*(char **) a, *(char **) b);
520 static int num_matches; 861}
521 static char **matches;
522 862
523 char matchBuf[BUFSIZ];
524 863
525 int pos = cursor;
526 int find_type=FIND_FILE_ONLY;
527 864
865#define QUOT (UCHAR_MAX+1)
528 866
529 if (lastWasTab == FALSE) { 867#define collapse_pos(is, in) { \
530 char *tmp, *tmp1; 868 memcpy(int_buf+is, int_buf+in, (BUFSIZ+1-is-in)*sizeof(int)); \
531 int len_found; 869 memcpy(pos_buf+is, pos_buf+in, (BUFSIZ+1-is-in)*sizeof(int)); }
532 870
533 /* For now, we will not bother with trying to distinguish 871static int find_match(char *matchBuf, int *len_with_quotes)
534 * whether the cursor is in/at a command extression -- we 872{
535 * will always try all possible matches. If you don't like 873 int i, j;
536 * that then feel free to fix it. 874 int command_mode;
537 */ 875 int c, c2;
538 876 int int_buf[BUFSIZ + 1];
539 /* Make a local copy of the string -- up 877 int pos_buf[BUFSIZ + 1];
540 * to the position of the cursor */ 878
541 memset(matchBuf, 0, BUFSIZ); 879 /* set to integer dimension characters and own positions */
542 tmp = strncpy(matchBuf, command_ps, cursor); 880 for (i = 0;; i++) {
881 int_buf[i] = (int) ((unsigned char) matchBuf[i]);
882 if (int_buf[i] == 0) {
883 pos_buf[i] = -1; /* indicator end line */
884 break;
885 } else
886 pos_buf[i] = i;
887 }
543 888
544 /* skip past any command seperator tokens */ 889 /* mask \+symbol and convert '\t' to ' ' */
545 while ( (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { 890 for (i = j = 0; matchBuf[i]; i++, j++)
546 tmp = ++tmp1; 891 if (matchBuf[i] == '\\') {
892 collapse_pos(j, j + 1);
893 int_buf[j] |= QUOT;
894 i++;
895#ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT
896 if (matchBuf[i] == '\t') /* algorithm equivalent */
897 int_buf[j] = ' ' | QUOT;
898#endif
547 } 899 }
900#ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT
901 else if (matchBuf[i] == '\t')
902 int_buf[j] = ' ';
903#endif
548 904
549 /* skip any leading white space */ 905 /* mask "symbols" or 'symbols' */
550 while (*tmp == ' ') 906 c2 = 0;
551 tmp++; 907 for (i = 0; int_buf[i]; i++) {
908 c = int_buf[i];
909 if (c == '\'' || c == '"') {
910 if (c2 == 0)
911 c2 = c;
912 else {
913 if (c == c2)
914 c2 = 0;
915 else
916 int_buf[i] |= QUOT;
917 }
918 } else if (c2 != 0 && c != '$')
919 int_buf[i] |= QUOT;
920 }
552 921
553 if(strncmp(tmp, "cd ", 3)==0) 922 /* skip commands with arguments if line have commands delimiters */
554 find_type = FIND_DIR_ONLY; 923 /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
555 else if(strchr(tmp, ' ')==NULL) 924 for (i = 0; int_buf[i]; i++) {
556 find_type = FIND_EXE_ONLY; 925 c = int_buf[i];
926 c2 = int_buf[i + 1];
927 j = i ? int_buf[i - 1] : -1;
928 command_mode = 0;
929 if (c == ';' || c == '&' || c == '|') {
930 command_mode = 1 + (c == c2);
931 if (c == '&') {
932 if (j == '>' || j == '<')
933 command_mode = 0;
934 } else if (c == '|' && j == '>')
935 command_mode = 0;
936 }
937 if (command_mode) {
938 collapse_pos(0, i + command_mode);
939 i = -1; /* hack incremet */
940 }
941 }
942 /* collapse `command...` */
943 for (i = 0; int_buf[i]; i++)
944 if (int_buf[i] == '`') {
945 for (j = i + 1; int_buf[j]; j++)
946 if (int_buf[j] == '`') {
947 collapse_pos(i, j + 1);
948 j = 0;
949 break;
950 }
951 if (j) {
952 /* not found close ` - command mode, collapse all previous */
953 collapse_pos(0, i + 1);
954 break;
955 } else
956 i--; /* hack incremet */
957 }
557 958
558 /* find begin curent word */ 959 /* collapse (command...(command...)...) or {command...{command...}...} */
559 if( (tmp1=strrchr(tmp, ' ')) != NULL) { 960 c = 0; /* "recursive" level */
560 tmp = ++tmp1; 961 c2 = 0;
962 for (i = 0; int_buf[i]; i++)
963 if (int_buf[i] == '(' || int_buf[i] == '{') {
964 if (int_buf[i] == '(')
965 c++;
966 else
967 c2++;
968 collapse_pos(0, i + 1);
969 i = -1; /* hack incremet */
970 }
971 for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
972 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
973 if (int_buf[i] == ')')
974 c--;
975 else
976 c2--;
977 collapse_pos(0, i + 1);
978 i = -1; /* hack incremet */
561 } 979 }
562 strcpy(matchBuf, tmp);
563 980
564 /* Free up any memory already allocated */ 981 /* skip first not quote space */
982 for (i = 0; int_buf[i]; i++)
983 if (int_buf[i] != ' ')
984 break;
985 if (i)
986 collapse_pos(0, i);
987
988 /* set find mode for completion */
989 command_mode = FIND_EXE_ONLY;
990 for (i = 0; int_buf[i]; i++)
991 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
992 if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
993 && strncmp(&matchBuf[pos_buf[0]], "cd", 2) == 0)
994 command_mode = FIND_DIR_ONLY;
995 else {
996 command_mode = FIND_FILE_ONLY;
997 break;
998 }
999 }
1000 /* "strlen" */
1001 for (i = 0; int_buf[i]; i++);
1002 /* find last word */
1003 for (--i; i >= 0; i--) {
1004 c = int_buf[i];
1005 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
1006 collapse_pos(0, i + 1);
1007 break;
1008 }
1009 }
1010 /* skip first not quoted '\'' or '"' */
1011 for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
1012 /* collapse quote or unquote // or /~ */
1013 while ((int_buf[i] & ~QUOT) == '/' && (
1014 (int_buf[i + 1] & ~QUOT) == '/'
1015 || (int_buf[i + 1] & ~QUOT) ==
1016 '~')) i++;
1017 if (i)
1018 collapse_pos(0, i);
1019
1020 /* set only match and destroy quotes */
1021 j = 0;
1022 for (i = 0; pos_buf[i] >= 0; i++) {
1023 matchBuf[i] = matchBuf[pos_buf[i]];
1024 j = pos_buf[i] + 1;
1025 }
1026 matchBuf[i] = 0;
1027 /* old lenght matchBuf with quotes symbols */
1028 *len_with_quotes = j ? j - pos_buf[0] : 0;
1029
1030 return command_mode;
1031}
1032
1033
1034static void input_tab(int *lastWasTab)
1035{
1036 /* Do TAB completion */
1037 static int num_matches;
1038 static char **matches;
1039
1040 if (lastWasTab == 0) { /* free all memory */
565 if (matches) { 1041 if (matches) {
566 while(num_matches>0) 1042 while (num_matches > 0)
567 free(matches[--num_matches]); 1043 free(matches[--num_matches]);
568 free(matches); 1044 free(matches);
569 matches = (char **) NULL; 1045 matches = (char **) NULL;
570 } 1046 }
1047 return;
1048 }
1049 if (*lastWasTab == FALSE) {
1050
1051 char *tmp;
1052 int len_found;
1053 char matchBuf[BUFSIZ];
1054 int find_type;
1055 int recalc_pos;
1056
1057 *lastWasTab = TRUE; /* flop trigger */
1058
1059 /* Make a local copy of the string -- up
1060 * to the position of the cursor */
1061 tmp = strncpy(matchBuf, command_ps, cursor);
1062 tmp[cursor] = 0;
1063
1064 find_type = find_match(matchBuf, &recalc_pos);
1065
1066 /* Free up any memory already allocated */
1067 input_tab(0);
571 1068
572#ifdef BB_FEATURE_USERNAME_COMPLETION 1069#ifdef BB_FEATURE_USERNAME_COMPLETION
573 /* If the word starts with `~' and there is no slash in the word, 1070 /* If the word starts with `~' and there is no slash in the word,
574 * then try completing this word as a username. */ 1071 * then try completing this word as a username. */
575 1072
576 if (matchBuf[0]=='~' && strchr(matchBuf, '/')==0) { 1073 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
577 matches = username_tab_completion(matchBuf, &num_matches); 1074 matches = username_tab_completion(matchBuf, &num_matches);
578 }
579#endif 1075#endif
580 /* Try to match any executable in our path and everything 1076 /* Try to match any executable in our path and everything
581 * in the current working directory that matches. */ 1077 * in the current working directory that matches. */
582 if (!matches) 1078 if (!matches)
583 matches = exe_n_cwd_tab_completion(matchBuf, &num_matches, find_type); 1079 matches =
1080 exe_n_cwd_tab_completion(matchBuf, &num_matches,
1081 find_type);
584 1082
585 /* Did we find exactly one match? */ 1083 /* Did we find exactly one match? */
586 if(!matches || num_matches>1) { 1084 if (!matches || num_matches > 1) {
1085 char *tmp1;
1086
587 beep(); 1087 beep();
588 return; 1088 if (!matches)
1089 return; /* not found */
1090 /* sort */
1091 qsort(matches, num_matches, sizeof(char *), match_compare);
1092
1093 /* find minimal match */
1094 tmp = xstrdup(matches[0]);
1095 for (tmp1 = tmp; *tmp1; tmp1++)
1096 for (len_found = 1; len_found < num_matches; len_found++)
1097 if (matches[len_found][(tmp1 - tmp)] != *tmp1) {
1098 *tmp1 = 0;
1099 break;
1100 }
1101 if (*tmp == 0) { /* have unique */
1102 free(tmp);
1103 return;
1104 }
1105 } else { /* one match */
1106 tmp = matches[0];
1107 /* for next completion current found */
1108 *lastWasTab = FALSE;
589 } 1109 }
590 1110
591 len_found = strlen(matches[0]); 1111 len_found = strlen(tmp);
592
593 /* have space to placed match? */ 1112 /* have space to placed match? */
594 if ( (len_found-strlen(matchBuf)+len) < BUFSIZ ) { 1113 if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
595 1114
596 int recalc_pos = len; 1115 /* before word for match */
597 1116 command_ps[cursor - recalc_pos] = 0;
598 /* before word for match */ 1117 /* save tail line */
599 command_ps[pos-strlen(matchBuf)]=0; 1118 strcpy(matchBuf, command_ps + cursor);
600 1119 /* add match */
601 /* tail line */ 1120 strcat(command_ps, tmp);
602 strcpy(matchBuf, command_ps+pos); 1121 /* add tail */
603
604 /* add match */
605 strcat(command_ps, matches[0]);
606 /* add tail */
607 strcat(command_ps, matchBuf); 1122 strcat(command_ps, matchBuf);
608 1123 /* back to begin word for match */
609 /* write out the matched command */ 1124 input_backward(recalc_pos);
610 len=strlen(command_ps); 1125 /* new pos */
611 recalc_pos = len-recalc_pos+pos; 1126 recalc_pos = cursor + len_found;
612 input_end(); /* write */ 1127 /* new len */
613 while(recalc_pos<cursor) 1128 len = strlen(command_ps);
614 input_backward(); 1129 /* write out the matched command */
615 return; 1130 input_end();
1131 input_backward(cursor - recalc_pos);
616 } 1132 }
1133 if (tmp != matches[0])
1134 free(tmp);
617 } else { 1135 } else {
618 /* Ok -- the last char was a TAB. Since they 1136 /* Ok -- the last char was a TAB. Since they
619 * just hit TAB again, print a list of all the 1137 * just hit TAB again, print a list of all the
620 * available choices... */ 1138 * available choices... */
621 if ( matches && num_matches>0 ) { 1139 if (matches && num_matches > 0) {
622 int i, col; 1140 int i, col, l;
623 int sav_cursor = cursor; 1141 int sav_cursor = cursor; /* change goto_new_line() */
624 1142
625 /* Go to the next line */ 1143 /* Go to the next line */
626 goto_new_line(); 1144 goto_new_line();
627 for (i=0,col=0; i<num_matches; i++) { 1145 for (i = 0, col = 0; i < num_matches; i++) {
628 printf("%s ", matches[i]); 1146 l = strlen(matches[i]);
629 col += strlen(matches[i])+2; 1147 if (l < 14)
630 col -= (col/cmdedit_termw)*cmdedit_termw; 1148 l = 14;
631 if (col > 60 && matches[i+1] != NULL) { 1149 printf("%-14s ", matches[i]);
1150 if ((l += 2) > 16)
1151 while (l % 16) {
1152 putchar(' ');
1153 l++;
1154 }
1155 col += l;
1156 col -= (col / cmdedit_termw) * cmdedit_termw;
1157 if (col > 60 && matches[i + 1] != NULL) {
632 putchar('\n'); 1158 putchar('\n');
633 col = 0; 1159 col = 0;
634 } 1160 }
635 } 1161 }
636 /* Go to the next line and rewrite the prompt */ 1162 /* Go to the next line and rewrite */
637 printf("\n%s", cmdedit_prompt); 1163 putchar('\n');
638 cmdedit_x = cmdedit_prmt_len; 1164 redraw(0, len - sav_cursor);
639 cmdedit_y = 0;
640 cursor = 0;
641 input_end(); /* Rewrite the command */
642 /* Put the cursor back to where it used to be */
643 while (sav_cursor < cursor)
644 input_backward();
645 } 1165 }
646 } 1166 }
647} 1167}
648#endif 1168#endif /* BB_FEATURE_SH_TAB_COMPLETION */
649 1169
650static void get_previous_history(struct history **hp, char* command) 1170static void get_previous_history(struct history **hp, struct history *p)
651{ 1171{
652 if ((*hp)->s) 1172 if ((*hp)->s)
653 free((*hp)->s); 1173 free((*hp)->s);
654 (*hp)->s = strdup(command); 1174 (*hp)->s = xstrdup(command_ps);
655 *hp = (*hp)->p; 1175 *hp = p;
656} 1176}
657 1177
658static void get_next_history(struct history **hp, char* command) 1178static inline void get_next_history(struct history **hp)
659{ 1179{
660 if ((*hp)->s) 1180 get_previous_history(hp, (*hp)->n);
661 free((*hp)->s);
662 (*hp)->s = strdup(command);
663 *hp = (*hp)->n;
664} 1181}
665 1182
1183enum {
1184 ESC = 27,
1185 DEL = 127,
1186};
1187
1188
666/* 1189/*
667 * This function is used to grab a character buffer 1190 * This function is used to grab a character buffer
668 * from the input file descriptor and allows you to 1191 * from the input file descriptor and allows you to
@@ -678,95 +1201,78 @@ static void get_next_history(struct history **hp, char* command)
678 * 1201 *
679 * Furthermore, the "vi" command editing keys are not implemented. 1202 * Furthermore, the "vi" command editing keys are not implemented.
680 * 1203 *
681 * TODO: implement TAB command completion. :)
682 */ 1204 */
683extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) 1205extern void cmdedit_read_input(char *prompt, char command[BUFSIZ])
684{ 1206{
685 1207
686 int inputFd=fileno(stdin); 1208 int inputFd = fileno(stdin);
687 1209
688 int j = 0;
689 int break_out = 0; 1210 int break_out = 0;
690 int ret = 0;
691 int lastWasTab = FALSE; 1211 int lastWasTab = FALSE;
692 char c = 0; 1212 char c = 0;
693 struct history *hp = his_end; 1213 struct history *hp = his_end;
694 1214
1215 /* prepare before init handlers */
1216 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
695 len = 0; 1217 len = 0;
696 cursor = 0;
697 command_ps = command; 1218 command_ps = command;
698 1219
699 if (new_settings.c_cc[VMIN]==0) { 1220 if (new_settings.c_cc[VMIN] == 0) { /* first call */
700 1221
701 getTermSettings(inputFd, (void*) &initial_settings); 1222 getTermSettings(inputFd, (void *) &initial_settings);
702 memcpy(&new_settings, &initial_settings, sizeof(struct termios)); 1223 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
1224
703 new_settings.c_cc[VMIN] = 1; 1225 new_settings.c_cc[VMIN] = 1;
704 new_settings.c_cc[VTIME] = 0; 1226 new_settings.c_cc[VTIME] = 0;
705 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */ 1227 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */
706 new_settings.c_lflag &= ~ICANON; /* unbuffered input */ 1228 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
707 new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */ 1229 new_settings.c_lflag &= ~(ECHO | ECHOCTL | ECHONL); /* Turn off echoing */
708 } 1230 }
709 setTermSettings(inputFd, (void*) &new_settings);
710 handlers_sets |= SET_RESET_TERM;
711 1231
712 memset(command, 0, BUFSIZ); 1232 command[0] = 0;
713 1233
714 cmdedit_init(); 1234 setTermSettings(inputFd, (void *) &new_settings);
1235 handlers_sets |= SET_RESET_TERM;
715 1236
1237 cmdedit_init();
716 /* Print out the command prompt */ 1238 /* Print out the command prompt */
717 cmdedit_prompt = prompt; 1239 parse_prompt(prompt);
718 cmdedit_prmt_len = strlen(prompt);
719 printf("%s", prompt);
720 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
721 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
722
723 1240
724 while (1) { 1241 while (1) {
725 1242
726 fflush(stdout); /* buffered out to fast */ 1243 fflush(stdout); /* buffered out to fast */
727 1244
728 if ((ret = read(inputFd, &c, 1)) < 1) 1245 if (read(inputFd, &c, 1) < 1)
729 return; 1246 return;
730 //fprintf(stderr, "got a '%c' (%d)\n", c, c);
731 1247
732 switch (c) { 1248 switch (c) {
733 case '\n': 1249 case '\n':
734 case '\r': 1250 case '\r':
735 /* Enter */ 1251 /* Enter */
736 *(command + len) = c; 1252 goto_new_line();
737 len++;
738 input_end ();
739 break_out = 1; 1253 break_out = 1;
740 break; 1254 break;
741 case 1: 1255 case 1:
742 /* Control-a -- Beginning of line */ 1256 /* Control-a -- Beginning of line */
743 input_home(); 1257 input_backward(cursor);
744 break; 1258 break;
745 case 2: 1259 case 2:
746 /* Control-b -- Move back one character */ 1260 /* Control-b -- Move back one character */
747 input_backward(); 1261 input_backward(1);
748 break; 1262 break;
749 case 3: 1263 case 3:
750 /* Control-c -- stop gathering input */ 1264 /* Control-c -- stop gathering input */
751 1265
752 /* Link into lash to reset context to 0 on ^C and such */ 1266 /* Link into lash to reset context to 0 on ^C and such */
753 shell_context = 0; 1267 shell_context = 0;
754 1268
755 /* Go to the next line */ 1269 /* Go to the next line */
756 goto_new_line(); 1270 goto_new_line();
1271 command[0] = 0;
757 1272
758#if 0
759 /* Rewrite the prompt */
760 printf("%s", prompt);
761
762 /* Reset the command string */
763 memset(command, 0, BUFSIZ);
764 len = cursor = 0;
765#endif
766 return; 1273 return;
767
768 case 4: 1274 case 4:
769 /* Control-d -- Delete one character, or exit 1275 /* Control-d -- Delete one character, or exit
770 * if the len=0 and no chars to delete */ 1276 * if the len=0 and no chars to delete */
771 if (len == 0) { 1277 if (len == 0) {
772 printf("exit"); 1278 printf("exit");
@@ -790,13 +1296,13 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
790 break; 1296 break;
791 case '\t': 1297 case '\t':
792#ifdef BB_FEATURE_SH_TAB_COMPLETION 1298#ifdef BB_FEATURE_SH_TAB_COMPLETION
793 input_tab(lastWasTab); 1299 input_tab(&lastWasTab);
794#endif 1300#endif
795 break; 1301 break;
796 case 14: 1302 case 14:
797 /* Control-n -- Get next command in history */ 1303 /* Control-n -- Get next command in history */
798 if (hp && hp->n && hp->n->s) { 1304 if (hp && hp->n && hp->n->s) {
799 get_next_history(&hp, command); 1305 get_next_history(&hp);
800 goto rewrite_line; 1306 goto rewrite_line;
801 } else { 1307 } else {
802 beep(); 1308 beep();
@@ -805,114 +1311,111 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
805 case 16: 1311 case 16:
806 /* Control-p -- Get previous command from history */ 1312 /* Control-p -- Get previous command from history */
807 if (hp && hp->p) { 1313 if (hp && hp->p) {
808 get_previous_history(&hp, command); 1314 get_previous_history(&hp, hp->p);
809 goto rewrite_line; 1315 goto rewrite_line;
810 } else { 1316 } else {
811 beep(); 1317 beep();
812 } 1318 }
813 break; 1319 break;
1320 case 21:
1321 /* Control-U -- Clear line before cursor */
1322 if (cursor) {
1323 strcpy(command, command + cursor);
1324 redraw(cmdedit_y, len -= cursor);
1325 }
1326 break;
1327
814 case ESC:{ 1328 case ESC:{
815 /* escape sequence follows */ 1329 /* escape sequence follows */
816 if ((ret = read(inputFd, &c, 1)) < 1) 1330 if (read(inputFd, &c, 1) < 1)
1331 return;
1332 /* different vt100 emulations */
1333 if (c == '[' || c == 'O') {
1334 if (read(inputFd, &c, 1) < 1)
817 return; 1335 return;
1336 }
1337 switch (c) {
1338#ifdef BB_FEATURE_SH_TAB_COMPLETION
1339 case '\t': /* Alt-Tab */
818 1340
819 if (c == '[') { /* 91 */ 1341 input_tab(&lastWasTab);
820 if ((ret = read(inputFd, &c, 1)) < 1) 1342 break;
821 return; 1343#endif
822 1344 case 'A':
823 switch (c) { 1345 /* Up Arrow -- Get previous command from history */
824 case 'A': 1346 if (hp && hp->p) {
825 /* Up Arrow -- Get previous command from history */ 1347 get_previous_history(&hp, hp->p);
826 if (hp && hp->p) { 1348 goto rewrite_line;
827 get_previous_history(&hp, command); 1349 } else {
828 goto rewrite_line; 1350 beep();
829 } else {
830 beep();
831 }
832 break;
833 case 'B':
834 /* Down Arrow -- Get next command in history */
835 if (hp && hp->n && hp->n->s) {
836 get_next_history(&hp, command);
837 goto rewrite_line;
838 } else {
839 beep();
840 }
841 break;
842
843 /* Rewrite the line with the selected history item */
844 rewrite_line:
845 /* return to begin of line */
846 input_home ();
847 /* for next memmoves without set '\0' */
848 memset (command, 0, BUFSIZ);
849 /* change command */
850 strcpy (command, hp->s);
851 /* write new command */
852 for (j=0; command[j]; j++)
853 cmdedit_set_out_char(command[j], 0);
854 ret = cursor;
855 /* erase tail if required */
856 for (j = ret; j < len; j++)
857 cmdedit_set_out_char(' ', 0);
858 /* and backward cursor */
859 for (j = ret; j < len; j++)
860 input_backward();
861 len = cursor; /* set new len */
862 break;
863 case 'C':
864 /* Right Arrow -- Move forward one character */
865 input_forward();
866 break;
867 case 'D':
868 /* Left Arrow -- Move back one character */
869 input_backward();
870 break;
871 case '3':
872 /* Delete */
873 input_delete();
874 break;
875 case '1':
876 /* Home (Ctrl-A) */
877 input_home();
878 break;
879 case '4':
880 /* End (Ctrl-E) */
881 input_end();
882 break;
883 default:
884 beep();
885 }
886 if (c == '1' || c == '3' || c == '4')
887 if ((ret = read(inputFd, &c, 1)) < 1)
888 return; /* read 126 (~) */
889 } 1351 }
890 if (c == 'O') { 1352 break;
891 /* 79 */ 1353 case 'B':
892 if ((ret = read(inputFd, &c, 1)) < 1) 1354 /* Down Arrow -- Get next command in history */
893 return; 1355 if (hp && hp->n && hp->n->s) {
894 switch (c) { 1356 get_next_history(&hp);
895 case 'H': 1357 goto rewrite_line;
896 /* Home (xterm) */ 1358 } else {
897 input_home(); 1359 beep();
898 break;
899 case 'F':
900 /* End (xterm) */
901 input_end();
902 break;
903 default:
904 beep();
905 }
906 } 1360 }
907 c = 0;
908 break; 1361 break;
1362
1363 /* Rewrite the line with the selected history item */
1364 rewrite_line:
1365 /* change command */
1366 len = strlen(strcpy(command, hp->s));
1367 /* redraw and go to end line */
1368 redraw(cmdedit_y, 0);
1369 break;
1370 case 'C':
1371 /* Right Arrow -- Move forward one character */
1372 input_forward();
1373 break;
1374 case 'D':
1375 /* Left Arrow -- Move back one character */
1376 input_backward(1);
1377 break;
1378 case '3':
1379 /* Delete */
1380 input_delete();
1381 break;
1382 case '1':
1383 case 'H':
1384 /* Home (Ctrl-A) */
1385 input_backward(cursor);
1386 break;
1387 case '4':
1388 case 'F':
1389 /* End (Ctrl-E) */
1390 input_end();
1391 break;
1392 default:
1393 if (!(c >= '1' && c <= '9'))
1394 c = 0;
1395 beep();
909 } 1396 }
1397 if (c >= '1' && c <= '9')
1398 do
1399 if (read(inputFd, &c, 1) < 1)
1400 return;
1401 while (c != '~');
1402 break;
1403 }
910 1404
911 default: /* If it's regular input, do the normal thing */ 1405 default: /* If it's regular input, do the normal thing */
912 1406#ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT
913 if (!isprint(c)) { /* Skip non-printable characters */ 1407 /* Control-V -- Add non-printable symbol */
1408 if (c == 22) {
1409 if (read(inputFd, &c, 1) < 1)
1410 return;
1411 if (c == 0) {
1412 beep();
1413 break;
1414 }
1415 } else
1416#endif
1417 if (!isprint(c)) /* Skip non-printable characters */
914 break; 1418 break;
915 }
916 1419
917 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ 1420 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
918 break; 1421 break;
@@ -921,45 +1424,41 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
921 1424
922 if (cursor == (len - 1)) { /* Append if at the end of the line */ 1425 if (cursor == (len - 1)) { /* Append if at the end of the line */
923 *(command + cursor) = c; 1426 *(command + cursor) = c;
924 cmdedit_set_out_char(c, command[cursor+1]); 1427 *(command + cursor + 1) = 0;
1428 cmdedit_set_out_char(0);
925 } else { /* Insert otherwise */ 1429 } else { /* Insert otherwise */
926 memmove(command + cursor + 1, command + cursor, 1430 int sc = cursor;
927 len - cursor - 1);
928 1431
929 *(command + cursor) = c; 1432 memmove(command + sc + 1, command + sc, len - sc);
930 j = cursor+1; 1433 *(command + sc) = c;
1434 sc++;
931 /* rewrite from cursor */ 1435 /* rewrite from cursor */
932 input_end (); 1436 input_end();
933 /* to prev x pos + 1 */ 1437 /* to prev x pos + 1 */
934 while(cursor > j) 1438 input_backward(cursor - sc);
935 input_backward();
936 } 1439 }
937 1440
938 break; 1441 break;
939 } 1442 }
940 if (c == '\t')
941 lastWasTab = TRUE;
942 else
943 lastWasTab = FALSE;
944
945 if (break_out) /* Enter is the command terminator, no more input. */ 1443 if (break_out) /* Enter is the command terminator, no more input. */
946 break; 1444 break;
1445
1446 if (c != '\t')
1447 lastWasTab = FALSE;
947 } 1448 }
948 1449
949 setTermSettings (inputFd, (void *) &initial_settings); 1450 setTermSettings(inputFd, (void *) &initial_settings);
950 handlers_sets &= ~SET_RESET_TERM; 1451 handlers_sets &= ~SET_RESET_TERM;
951 1452
952 /* Handle command history log */ 1453 /* Handle command history log */
953 if (len>1) { /* no put empty line (only '\n') */ 1454 if (len) { /* no put empty line */
954 1455
955 struct history *h = his_end; 1456 struct history *h = his_end;
956 char *ss; 1457 char *ss;
957 1458
958 command[len-1] = 0; /* destroy end '\n' */ 1459 ss = xstrdup(command); /* duplicate */
959 ss = strdup(command); /* duplicate without '\n' */
960 command[len-1] = '\n'; /* restore '\n' */
961 1460
962 if (!h) { 1461 if (h == 0) {
963 /* No previous history -- this memory is never freed */ 1462 /* No previous history -- this memory is never freed */
964 h = his_front = xmalloc(sizeof(struct history)); 1463 h = his_front = xmalloc(sizeof(struct history));
965 h->n = xmalloc(sizeof(struct history)); 1464 h->n = xmalloc(sizeof(struct history));
@@ -994,8 +1493,18 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
994 history_counter++; 1493 history_counter++;
995 } 1494 }
996 } 1495 }
1496#if defined(BB_FEATURE_BASH_STYLE_PROMT)
1497 num_ok_lines++;
1498#endif
997 } 1499 }
998 1500 command[len++] = '\n'; /* set '\n' */
1501 command[len] = 0;
1502#if defined(BB_FEATURE_CLEAN_UP) && defined(BB_FEATURE_SH_TAB_COMPLETION)
1503 input_tab(0); /* strong free */
1504#endif
1505#if defined(BB_FEATURE_BASH_STYLE_PROMT)
1506 free(cmdedit_prompt);
1507#endif
999 return; 1508 return;
1000} 1509}
1001 1510
@@ -1004,7 +1513,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
1004extern void cmdedit_terminate(void) 1513extern void cmdedit_terminate(void)
1005{ 1514{
1006 cmdedit_reset_term(); 1515 cmdedit_reset_term();
1007 if((handlers_sets & SET_TERM_HANDLERS)!=0) { 1516 if ((handlers_sets & SET_TERM_HANDLERS) != 0) {
1008 signal(SIGKILL, SIG_DFL); 1517 signal(SIGKILL, SIG_DFL);
1009 signal(SIGINT, SIG_DFL); 1518 signal(SIGINT, SIG_DFL);
1010 signal(SIGQUIT, SIG_DFL); 1519 signal(SIGQUIT, SIG_DFL);
@@ -1014,6 +1523,32 @@ extern void cmdedit_terminate(void)
1014 } 1523 }
1015} 1524}
1016 1525
1526#endif /* BB_FEATURE_SH_COMMAND_EDITING */
1527
1528
1529#ifdef TEST
1530
1531unsigned int shell_context;
1017 1532
1533int main(int argc, char **argv)
1534{
1535 char buff[BUFSIZ];
1536 char *prompt =
1537#if defined(BB_FEATURE_BASH_STYLE_PROMT)
1538 "\\[\\033[32;1m\\]\\u@\\[\\033[33;1m\\]\\h:\
1539\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
1540\\!\\[\\033[36;1m\\]\\$ \\[\\033[0m\\]";
1541#else
1542 "% ";
1543#endif
1544
1545 shell_context = 1;
1546 do {
1547 cmdedit_read_input(prompt, buff);
1548 printf("*** cmdedit_read_input() returned line =%s=\n", buff);
1549 } while (shell_context);
1550 printf("*** cmdedit_read_input() detect ^C\n");
1551 return 0;
1552}
1018 1553
1019#endif /* BB_FEATURE_SH_COMMAND_EDITING */ 1554#endif /* TEST */