aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Whitley <markw@lineo.com>2001-01-26 20:42:23 +0000
committerMark Whitley <markw@lineo.com>2001-01-26 20:42:23 +0000
commit4e33875759b3c13118ecf349bfe302e0d8374b78 (patch)
treeb0976625da2bd861146fe16dff9a9dfbf50f8a83
parenta68b21a28e62a86afdbbc57b5a4694a17d7dfd1e (diff)
downloadbusybox-w32-4e33875759b3c13118ecf349bfe302e0d8374b78.tar.gz
busybox-w32-4e33875759b3c13118ecf349bfe302e0d8374b78.tar.bz2
busybox-w32-4e33875759b3c13118ecf349bfe302e0d8374b78.zip
Applied patch from Vladimir N. Oleynik <dzo@simtreas.ru> to do tab-completion
in Busybox shell. (Thanks, Vlad.)
-rw-r--r--Config.h6
-rw-r--r--cmdedit.c756
-rw-r--r--shell/cmdedit.c756
3 files changed, 1007 insertions, 511 deletions
diff --git a/Config.h b/Config.h
index cff23e4e7..5e14b4054 100644
--- a/Config.h
+++ b/Config.h
@@ -260,7 +260,11 @@
260// Enable tab completion in the shell (not yet 260// Enable tab completion in the shell (not yet
261// working very well -- so don't turn this on) 261// working very well -- so don't turn this on)
262// Only relevant if BB_SH is enabled. 262// Only relevant if BB_SH is enabled.
263//#define BB_FEATURE_SH_TAB_COMPLETION 263#define BB_FEATURE_SH_TAB_COMPLETION
264//
265// Attempts to match usernames in a ~-prefixed path
266// XXX: Doesn't work without NSS, off by default
267//#define BB_FEATURE_USERNAME_COMPLETION /* require NSS */
264// 268//
265//Turn on extra fbset options 269//Turn on extra fbset options
266//#define BB_FEATURE_FBSET_FANCY 270//#define BB_FEATURE_FBSET_FANCY
diff --git a/cmdedit.c b/cmdedit.c
index 12c78dc76..c2b4123db 100644
--- a/cmdedit.c
+++ b/cmdedit.c
@@ -25,13 +25,20 @@
25 need to be added. This version was created on Debian GNU/Linux 2.x. 25 need to be added. This version was created on Debian GNU/Linux 2.x.
26 Delete, Backspace, Home, End, and the arrow keys were tested 26 Delete, Backspace, Home, End, and the arrow keys were tested
27 to work in an Xterm and console. Ctrl-A also works as Home. 27 to work in an Xterm and console. Ctrl-A also works as Home.
28 Ctrl-E also works as End. The binary size increase is <3K. 28 Ctrl-E also works as End.
29 29
30 Editting will not display correctly for lines greater then the 30
31 terminal width. (more then one line.) However, history will. 31 Editor with vertical scrolling and completion by
32 Vladimir Oleynik. vodz@usa.net (c) 2001
33
34 Small bug: not true work if terminal size (x*y symbols) less
35 size (prompt + editor`s line + 2 symbols)
32 */ 36 */
33 37
38
39
34#include "busybox.h" 40#include "busybox.h"
41
35#ifdef BB_FEATURE_SH_COMMAND_EDITING 42#ifdef BB_FEATURE_SH_COMMAND_EDITING
36 43
37#include <stdio.h> 44#include <stdio.h>
@@ -43,6 +50,13 @@
43#include <ctype.h> 50#include <ctype.h>
44#include <signal.h> 51#include <signal.h>
45 52
53#ifdef BB_FEATURE_SH_TAB_COMPLETION
54#include <sys/stat.h>
55#endif
56
57#ifdef BB_FEATURE_USERNAME_COMPLETION
58#include <pwd.h>
59#endif
46 60
47static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */ 61static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */
48 62
@@ -58,7 +72,6 @@ static struct history *his_front = NULL; /* First element in command line list *
58static struct history *his_end = NULL; /* Last element in command line list */ 72static struct history *his_end = NULL; /* Last element in command line list */
59 73
60/* ED: sparc termios is broken: revert back to old termio handling. */ 74/* ED: sparc termios is broken: revert back to old termio handling. */
61#ifdef BB_FEATURE_USE_TERMIOS
62 75
63#if #cpu(sparc) 76#if #cpu(sparc)
64# include <termio.h> 77# include <termio.h>
@@ -79,16 +92,35 @@ static struct termios initial_settings, new_settings;
79#define _POSIX_VDISABLE '\0' 92#define _POSIX_VDISABLE '\0'
80#endif 93#endif
81 94
82#endif
83 95
96static
97volatile int cmdedit_termw; /* actual terminal width */
98static int history_counter = 0; /* Number of commands in history list */
84 99
100static
101volatile int handlers_sets = 0; /* Set next bites
102 when atexit() has been called
103 and set many "terminates" signal handlers
104 and winchg signal handler
105 and if the terminal needs to be reset upon exit
106 */
107enum {
108 SET_ATEXIT = 1,
109 SET_TERM_HANDLERS = 2,
110 SET_WCHG_HANDLERS = 4,
111 SET_RESET_TERM = 8,
112};
85 113
86static int cmdedit_termw = 80; /* actual terminal width */
87static int cmdedit_scroll = 27; /* width of EOL scrolling region */
88static int history_counter = 0; /* Number of commands in history list */
89static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */
90static int exithandler_set = 0; /* Set to true when atexit() has been called */
91 114
115static int cmdedit_x; /* real x terminal position,
116 require put prompt in start x position */
117static int cmdedit_y; /* pseudoreal y terminal position */
118static int cmdedit_prmt_len; /* for fast running, without duplicate calculate */
119
120static int cursor; /* required global for signal handler */
121static int len; /* --- "" - - "" - -"- --""-- --""--- */
122static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */
123static const char *cmdedit_prompt;/* --- "" - - "" - -"- --""-- --""--- */
92 124
93/* Link into lash to reset context to 0 125/* Link into lash to reset context to 0
94 * on ^C and such */ 126 * on ^C and such */
@@ -101,38 +133,43 @@ struct history {
101 struct history *n; 133 struct history *n;
102}; 134};
103 135
104#define xwrite write 136static void cmdedit_setwidth(int w, int redraw_flg);
105 137
106/* 138static void win_changed(int nsig)
107 * TODO: Someday we want to implement 'horizontal scrolling' of the
108 * command-line when the user has typed more than the current width. This
109 * would allow the user to see a 'window' of what he has typed.
110 */
111static void cmdedit_setwidth(int w)
112{
113 if (w > 20) {
114 cmdedit_termw = w;
115 cmdedit_scroll = w / 3;
116 } else {
117 error_msg("\n*** Error: minimum screen width is 21\n");
118 }
119}
120
121static void win_changed(int junk)
122{ 139{
123 struct winsize win = { 0, 0, 0, 0 }; 140 struct winsize win = { 0, 0, 0, 0 };
124 ioctl(0, TIOCGWINSZ, &win); 141 static __sighandler_t previous_SIGWINCH_handler; /* for reset */
125 if (win.ws_col > 0) { 142
126 cmdedit_setwidth( win.ws_col - 1); 143 /* emulate || signal call */
144 if(nsig == -SIGWINCH || nsig == SIGWINCH) {
145 ioctl(0, TIOCGWINSZ, &win);
146 if (win.ws_col > 0) {
147 cmdedit_setwidth( win.ws_col, nsig == SIGWINCH );
148 }
127 } 149 }
150 /* Unix not all standart in recall signal */
151
152 if(nsig == -SIGWINCH) /* save previous handler */
153 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
154 else if(nsig == SIGWINCH) /* signaled called handler */
155 signal(SIGWINCH, win_changed); /* set for next call */
156 else /* set previous handler */
157 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
128} 158}
129 159
130
131static void cmdedit_reset_term(void) 160static void cmdedit_reset_term(void)
132{ 161{
133 if (reset_term) 162 if((handlers_sets & SET_RESET_TERM)!=0) {
134 /* sparc and other have broken termios support: use old termio handling. */ 163 /* sparc and other have broken termios support: use old termio handling. */
135 setTermSettings(fileno(stdin), (void*) &initial_settings); 164 setTermSettings(fileno(stdin), (void*) &initial_settings);
165 handlers_sets &= ~SET_RESET_TERM;
166 }
167 if((handlers_sets & SET_WCHG_HANDLERS)!=0) {
168 /* reset SIGWINCH handler to previous (default) */
169 win_changed(0);
170 handlers_sets &= ~SET_WCHG_HANDLERS;
171 }
172 fflush(stdout);
136#ifdef BB_FEATURE_CLEAN_UP 173#ifdef BB_FEATURE_CLEAN_UP
137 if (his_front) { 174 if (his_front) {
138 struct history *n; 175 struct history *n;
@@ -147,169 +184,348 @@ static void cmdedit_reset_term(void)
147#endif 184#endif
148} 185}
149 186
150static void clean_up_and_die(int sig)
151{
152 cmdedit_reset_term();
153 printf("\n");
154 if (sig!=SIGINT)
155 exit(EXIT_SUCCESS);
156}
157 187
158/* Go to HOME position */
159static void input_home(int outputFd, int *cursor)
160{
161 while (*cursor > 0) {
162 xwrite(outputFd, "\b", 1);
163 --*cursor;
164 }
165}
166 188
167/* Go to END position */ 189/* special for recount position for scroll and remove terminal margin effect */
168static void input_end(int outputFd, int *cursor, int len) 190static void cmdedit_set_out_char(int c, int next_char) {
169{ 191 putchar(c);
170 while (*cursor < len) { 192 if(++cmdedit_x>=cmdedit_termw) {
171 xwrite(outputFd, "\033[C", 3); 193 /* terminal is scrolled down */
172 ++*cursor; 194 cmdedit_y++;
195 cmdedit_x=0;
196
197 if(!next_char)
198 next_char = ' ';
199 /* destroy "(auto)margin" */
200 putchar(next_char);
201 putchar('\b');
173 } 202 }
203 cursor++;
174} 204}
175 205
176/* Delete the char in back of the cursor */ 206/* Move to end line. Bonus: rewrite line from cursor without use
177static void input_backspace(char* command, int outputFd, int *cursor, int *len) 207 special control terminal strings, also saved size and speed! */
178{ 208static void input_end (void) {
179 int j = 0; 209 while(cursor < len)
180 210 cmdedit_set_out_char(command_ps[cursor], 0);
181/* Debug crap */ 211}
182//fprintf(stderr, "\nerik: len=%d, cursor=%d, strlen(command)='%d'\n", *len, *cursor, strlen(command));
183//xwrite(outputFd, command, *len);
184//*cursor = *len;
185 212
213/* Go to the next line */
214static void goto_new_line(void) {
215 input_end();
216 cmdedit_set_out_char('\n', 0);
217}
186 218
187 if (*cursor > 0) {
188 xwrite(outputFd, "\b \b", 3);
189 --*cursor;
190 memmove(command + *cursor, command + *cursor + 1,
191 BUFSIZ - *cursor + 1);
192 219
193 for (j = *cursor; j < (BUFSIZ - 1); j++) { 220static inline void out1str(const char *s) { fputs (s, stdout); }
194 if (!*(command + j)) 221static inline void beep (void) { putchar('\007'); }
195 break;
196 else
197 xwrite(outputFd, (command + j), 1);
198 }
199 222
200 xwrite(outputFd, " \b", 2); 223/* Go to HOME position */
224static void input_home(void)
225{
226 while(cmdedit_y>0) { /* up to start y */
227 out1str("\033[A");
228 cmdedit_y--;
229 }
230 putchar('\r');
231 cursor = 0;
232 out1str(cmdedit_prompt);
233 cmdedit_x = cmdedit_prmt_len;
201 234
202 while (j-- > *cursor) 235}
203 xwrite(outputFd, "\b", 1);
204 236
205 --*len; 237/* Move back one charactor */
238static void input_backward(void) {
239 if (cursor > 0) {
240 cursor--;
241 if(cmdedit_x!=0) { /* no first position in terminal line */
242 putchar('\b');
243 cmdedit_x--;
244 }
245 else {
246 out1str("\033[A"); /* up */
247 cmdedit_y--;
248
249 /* to end in current terminal line */
250 while(cmdedit_x<(cmdedit_termw-1)) {
251 out1str("\033[C");
252 cmdedit_x++;
253 }
254 }
206 } 255 }
207} 256}
208 257
209/* Delete the char in front of the cursor */ 258/* Delete the char in front of the cursor */
210static void input_delete(char* command, int outputFd, int cursor, int *len) 259static void input_delete(void)
211{ 260{
212 int j = 0; 261 int j = cursor;
213 262
214 if (cursor == *len) 263 if (j == len)
215 return; 264 return;
216 265
217 memmove(command + cursor, command + cursor + 1, 266 memmove (command_ps + j, command_ps + j + 1, BUFSIZ - j - 1);
218 BUFSIZ - cursor - 1); 267 len--;
219 for (j = cursor; j < (BUFSIZ - 1); j++) { 268 input_end(); /* rewtite new line */
220 if (!*(command + j)) 269 cmdedit_set_out_char(' ', 0); /* destroy end char */
221 break; 270 while (j < cursor)
222 else 271 input_backward(); /* back to old pos cursor */
223 xwrite(outputFd, (command + j), 1); 272}
273
274/* Delete the char in back of the cursor */
275static void input_backspace(void)
276{
277 if (cursor > 0) {
278 input_backward();
279 input_delete ();
224 } 280 }
281}
225 282
226 xwrite(outputFd, " \b", 2);
227 283
228 while (j-- > cursor) 284/* Move forward one charactor */
229 xwrite(outputFd, "\b", 1); 285static void input_forward(void)
230 --*len; 286{
287 if (cursor < len)
288 cmdedit_set_out_char(command_ps[cursor], command_ps[cursor + 1]);
231} 289}
232 290
233/* Move forward one charactor */ 291
234static void input_forward(int outputFd, int *cursor, int len) 292static void clean_up_and_die(int sig)
235{ 293{
236 if (*cursor < len) { 294 goto_new_line();
237 xwrite(outputFd, "\033[C", 3); 295 if (sig!=SIGINT)
238 ++*cursor; 296 exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */
239 } 297 cmdedit_reset_term();
240} 298}
241 299
242/* Move back one charactor */ 300static void cmdedit_setwidth(int w, int redraw_flg)
243static void input_backward(int outputFd, int *cursor)
244{ 301{
245 if (*cursor > 0) { 302 cmdedit_termw = cmdedit_prmt_len+2;
246 xwrite(outputFd, "\033[D", 3); 303 if (w > cmdedit_termw) {
247 --*cursor; 304
305 cmdedit_termw = w;
306
307 if(redraw_flg) {
308 int sav_cursor = cursor;
309
310 /* set variables for new terminal size */
311 cmdedit_y = sav_cursor/w;
312 cmdedit_x = sav_cursor-cmdedit_y*w;
313
314 /* redraw */
315 input_home();
316 input_end();
317 while(sav_cursor<cursor)
318 input_backward();
319 }
320 } else {
321 error_msg("\n*** Error: minimum screen width is %d\n", cmdedit_termw);
248 } 322 }
249} 323}
250 324
325extern void cmdedit_init(void)
326{
327 if((handlers_sets & SET_WCHG_HANDLERS)==0) {
328 /* emulate usage handler to set handler and call yours work */
329 win_changed(-SIGWINCH);
330 handlers_sets |= SET_WCHG_HANDLERS;
331 }
251 332
333 if((handlers_sets & SET_ATEXIT)==0) {
334 atexit(cmdedit_reset_term); /* be sure to do this only once */
335 handlers_sets |= SET_ATEXIT;
336 }
337 if((handlers_sets & SET_TERM_HANDLERS)==0) {
338 signal(SIGKILL, clean_up_and_die);
339 signal(SIGINT, clean_up_and_die);
340 signal(SIGQUIT, clean_up_and_die);
341 signal(SIGTERM, clean_up_and_die);
342 handlers_sets |= SET_TERM_HANDLERS;
343 }
344}
252 345
253#ifdef BB_FEATURE_SH_TAB_COMPLETION 346#ifdef BB_FEATURE_SH_TAB_COMPLETION
254static char** username_tab_completion(char* command, int *num_matches) 347
348#ifdef BB_FEATURE_USERNAME_COMPLETION
349static char** username_tab_completion(char *ud, int *num_matches)
255{ 350{
351 static struct passwd *entry;
352 int userlen;
256 char **matches = (char **) NULL; 353 char **matches = (char **) NULL;
257 *num_matches=0; 354 char *temp;
258 fprintf(stderr, "\nin username_tab_completion\n"); 355 int nm = 0;
356
357 setpwent ();
358 userlen = strlen (ud + 1);
359
360 while ((entry = getpwent ()) != NULL) {
361 /* Null usernames should result in all users as possible completions. */
362 if (!userlen || !strncmp (ud + 1, entry->pw_name, userlen)) {
363
364 temp = xmalloc (3 + strlen (entry->pw_name));
365 sprintf(temp, "~%s/", entry->pw_name);
366
367 matches = xrealloc(matches, (nm+1)*sizeof(char *));
368 matches[nm++] = temp;
369 }
370 }
371
372 endpwent ();
373 (*num_matches) = nm;
259 return (matches); 374 return (matches);
260} 375}
376#endif
377
378enum {
379 FIND_EXE_ONLY = 0,
380 FIND_DIR_ONLY = 1,
381 FIND_FILE_ONLY = 2,
382};
261 383
262#include <dirent.h> 384#include <dirent.h>
263static char** exe_n_cwd_tab_completion(char* command, int *num_matches) 385
386static int path_parse(char ***p, int flags)
387{
388 int npth;
389 char *tmp;
390 char *pth;
391
392 if(flags!=FIND_EXE_ONLY || (pth=getenv("PATH"))==0) {
393 /* if not setenv PATH variable, to search cur dir "." */
394 (*p) = xmalloc(sizeof(char *));
395 (*p)[0] = xstrdup(".");
396 return 1;
397 }
398
399 tmp = pth;
400 npth=0;
401
402 for(;;) {
403 npth++; /* count words is + 1 count ':' */
404 tmp = strchr(tmp, ':');
405 if(tmp)
406 tmp++;
407 else
408 break;
409 }
410
411 *p = xmalloc(npth*sizeof(char *));
412
413 tmp = pth;
414 (*p)[0] = xstrdup(tmp);
415 npth=1; /* count words is + 1 count ':' */
416
417 for(;;) {
418 tmp = strchr(tmp, ':');
419 if(tmp) {
420 (*p)[0][(tmp-pth)]=0; /* ':' -> '\0'*/
421 tmp++;
422 } else
423 break;
424 (*p)[npth++] = &(*p)[0][(tmp-pth)]; /* p[next]=p[0][&'\0'+1] */
425 }
426
427 return npth;
428}
429
430static char** exe_n_cwd_tab_completion(char* command, int *num_matches, int type)
264{ 431{
265 char *dirName; 432 char *dirName;
266 char **matches; 433 char **matches = 0;
267 DIR *dir; 434 DIR *dir;
268 struct dirent *next; 435 struct dirent *next;
436 char cmd [BUFSIZ+4];
437 char *dirbuf;
438 char found [BUFSIZ+4];
439 int nm = *num_matches;
440 struct stat st;
441 char **paths;
442 int npaths;
443 int i;
444 char full_pth[BUFSIZ+4+PATH_MAX];
445
446
447 strcpy(cmd, command); /* save for change (last '/' to '\0') */
448
449 dirName = strrchr(cmd, '/');
450 if(dirName==NULL) {
451 /* no dir, if flags==EXE_ONLY - get paths, else "." */
452 npaths = path_parse(&paths, type);
453 if(npaths==0)
454 return 0;
455 } else {
456 /* with dir */
457
458 /* save dir */
459 dirbuf = xstrdup(cmd);
460 /* set only dirname */
461 dirbuf[(dirName-cmd)+1]=0;
462
463 /* strip dirname in cmd */
464 strcpy(cmd, dirName+1);
269 465
270 matches = xmalloc( sizeof(char*)*50); 466 paths = xmalloc(sizeof(char*));
467 paths[0] = dirbuf;
468 npaths = 1; /* only 1 dir */
469 }
271 470
272 /* Stick a wildcard onto the command, for later use */ 471 for(i=0; i < npaths; i++) {
273 strcat( command, "*");
274 472
275 /* Now wall the current directory */ 473 dir = opendir(paths[i]);
276 dirName = get_current_dir_name();
277 dir = opendir(dirName);
278 if (!dir) { 474 if (!dir) {
279 /* Don't print an error, just shut up and return */ 475 /* Don't print an error, just shut up and return */
280 *num_matches=0;
281 return (matches); 476 return (matches);
282 } 477 }
283 while ((next = readdir(dir)) != NULL) { 478 while ((next = readdir(dir)) != NULL) {
284 479 /* matched ? */
285 /* Some quick sanity checks */ 480 if(strncmp(next->d_name, cmd, strlen(cmd)))
286 if ((strcmp(next->d_name, "..") == 0) 481 continue;
287 || (strcmp(next->d_name, ".") == 0)) { 482 /* not see .name without .match */
483 if(*next->d_name == '.' && *cmd != '.')
484 continue;
485 sprintf(full_pth, "%s/%s", paths[i], next->d_name);
486 /* hmm, remover in progress? */
487 if(stat(full_pth, &st)<0)
488 continue;
489 /* Cool, found a match. */
490 if (S_ISDIR(st.st_mode)) {
491 /* name is directory */
492 strcpy(found, next->d_name);
493 strcat(found, "/");
494 if(type==FIND_DIR_ONLY)
495 strcat(found, " ");
496 } else {
497 /* not put found file if search only dirs for cd */
498 if(type==FIND_DIR_ONLY)
288 continue; 499 continue;
500 strcpy(found, next->d_name);
501 strcat(found, " ");
289 } 502 }
290 /* See if this matches */ 503 /* Add it to the list */
291 if (check_wildcard_match(next->d_name, command) == TRUE) { 504 matches = xrealloc(matches, (nm+1)*sizeof(char *));
292 /* Cool, found a match. Add it to the list */ 505 matches[nm++] = xstrdup(found);
293 matches[*num_matches] = xmalloc(strlen(next->d_name)+1);
294 strcpy( matches[*num_matches], next->d_name);
295 ++*num_matches;
296 //matches = realloc( matches, sizeof(char*)*(*num_matches));
297 } 506 }
298 } 507 }
299 508 free(paths[0]); /* allocate memory only in first member */
509 free(paths);
510 *num_matches = nm;
300 return (matches); 511 return (matches);
301} 512}
302 513
303static void input_tab(char* command, char* prompt, int outputFd, int *cursor, int *len, int lastWasTab) 514static void input_tab(int lastWasTab)
304{ 515{
305 /* Do TAB completion */ 516 /* Do TAB completion */
306 static int num_matches=0; 517 static int num_matches;
307 static char **matches = (char **) NULL; 518 static char **matches;
308 int pos = *cursor; 519
520 char matchBuf[BUFSIZ];
521
522 int pos = cursor;
523 int find_type=FIND_FILE_ONLY;
309 524
310 525
311 if (lastWasTab == FALSE) { 526 if (lastWasTab == FALSE) {
312 char *tmp, *tmp1, *matchBuf; 527 char *tmp, *tmp1;
528 int len_found;
313 529
314 /* For now, we will not bother with trying to distinguish 530 /* For now, we will not bother with trying to distinguish
315 * whether the cursor is in/at a command extression -- we 531 * whether the cursor is in/at a command extression -- we
@@ -319,50 +535,80 @@ static void input_tab(char* command, char* prompt, int outputFd, int *cursor, in
319 535
320 /* Make a local copy of the string -- up 536 /* Make a local copy of the string -- up
321 * to the position of the cursor */ 537 * to the position of the cursor */
322 matchBuf = (char *) xcalloc(BUFSIZ, sizeof(char)); 538 memset(matchBuf, 0, BUFSIZ);
323 strncpy(matchBuf, command, *cursor); 539 tmp = strncpy(matchBuf, command_ps, cursor);
324 tmp=matchBuf;
325 540
326 /* skip past any command seperator tokens */ 541 /* skip past any command seperator tokens */
327 while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { 542 while ( (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
328 tmp=++tmp1; 543 tmp = ++tmp1;
329 /* skip any leading white space */
330 while (*tmp && isspace(*tmp))
331 ++tmp;
332 } 544 }
333 545
334 /* skip any leading white space */ 546 /* skip any leading white space */
335 while (*tmp && isspace(*tmp)) 547 while (*tmp == ' ')
336 ++tmp; 548 tmp++;
549
550 if(strncmp(tmp, "cd ", 3)==0)
551 find_type = FIND_DIR_ONLY;
552 else if(strchr(tmp, ' ')==NULL)
553 find_type = FIND_EXE_ONLY;
554
555 /* find begin curent word */
556 if( (tmp1=strrchr(tmp, ' ')) != NULL) {
557 tmp = ++tmp1;
558 }
559 strcpy(matchBuf, tmp);
337 560
338 /* Free up any memory already allocated */ 561 /* Free up any memory already allocated */
339 if (matches) { 562 if (matches) {
563 while(num_matches>0)
564 free(matches[--num_matches]);
340 free(matches); 565 free(matches);
341 matches = (char **) NULL; 566 matches = (char **) NULL;
342 } 567 }
343 568
569#ifdef BB_FEATURE_USERNAME_COMPLETION
344 /* If the word starts with `~' and there is no slash in the word, 570 /* If the word starts with `~' and there is no slash in the word,
345 * then try completing this word as a username. */ 571 * then try completing this word as a username. */
346 572
347 /* FIXME -- this check is broken! */ 573 if (matchBuf[0]=='~' && strchr(matchBuf, '/')==0) {
348 if (*tmp == '~' && !strchr(tmp, '/')) 574 matches = username_tab_completion(matchBuf, &num_matches);
349 matches = username_tab_completion(tmp, &num_matches); 575 }
350 576#endif
351 /* Try to match any executable in our path and everything 577 /* Try to match any executable in our path and everything
352 * in the current working directory that matches. */ 578 * in the current working directory that matches. */
353 if (!matches) 579 if (!matches)
354 matches = exe_n_cwd_tab_completion(tmp, &num_matches); 580 matches = exe_n_cwd_tab_completion(matchBuf, &num_matches, find_type);
355
356 /* Don't leak memory */
357 free( matchBuf);
358 581
359 /* Did we find exactly one match? */ 582 /* Did we find exactly one match? */
360 if (matches && num_matches==1) { 583 if(!matches || num_matches>1) {
584 beep();
585 return;
586 }
587
588 len_found = strlen(matches[0]);
589
590 /* have space to placed match? */
591 if ( (len_found-strlen(matchBuf)+len) < BUFSIZ ) {
592
593 int recalc_pos = len;
594
595 /* before word for match */
596 command_ps[pos-strlen(matchBuf)]=0;
597
598 /* tail line */
599 strcpy(matchBuf, command_ps+pos);
600
601 /* add match */
602 strcat(command_ps, matches[0]);
603 /* add tail */
604 strcat(command_ps, matchBuf);
605
361 /* write out the matched command */ 606 /* write out the matched command */
362 strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos); 607 len=strlen(command_ps);
363 *len=strlen(command); 608 recalc_pos = len-recalc_pos+pos;
364 *cursor=*len; 609 input_end(); /* write */
365 xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos); 610 while(recalc_pos<cursor)
611 input_backward();
366 return; 612 return;
367 } 613 }
368 } else { 614 } else {
@@ -371,28 +617,28 @@ static void input_tab(char* command, char* prompt, int outputFd, int *cursor, in
371 * available choices... */ 617 * available choices... */
372 if ( matches && num_matches>0 ) { 618 if ( matches && num_matches>0 ) {
373 int i, col; 619 int i, col;
620 int sav_cursor = cursor;
374 621
375 /* Go to the next line */ 622 /* Go to the next line */
376 xwrite(outputFd, "\n", 1); 623 goto_new_line();
377 /* Print the list of matches */
378 for (i=0,col=0; i<num_matches; i++) { 624 for (i=0,col=0; i<num_matches; i++) {
379 char foo[17]; 625 printf("%s ", matches[i]);
380 sprintf(foo, "%-14s ", matches[i]); 626 col += strlen(matches[i])+2;
381 col += xwrite(outputFd, foo, strlen(foo)); 627 col -= (col/cmdedit_termw)*cmdedit_termw;
382 if (col > 60 && matches[i+1] != NULL) { 628 if (col > 60 && matches[i+1] != NULL) {
383 xwrite(outputFd, "\n", 1); 629 putchar('\n');
384 col = 0; 630 col = 0;
385 } 631 }
386 } 632 }
387 /* Go to the next line */ 633 /* Go to the next line and rewrite the prompt */
388 xwrite(outputFd, "\n", 1); 634 printf("\n%s", cmdedit_prompt);
389 /* Rewrite the prompt */ 635 cmdedit_x = cmdedit_prmt_len;
390 xwrite(outputFd, prompt, strlen(prompt)); 636 cmdedit_y = 0;
391 /* Rewrite the command */ 637 cursor = 0;
392 xwrite(outputFd, command, *len); 638 input_end(); /* Rewrite the command */
393 /* Put the cursor back to where it used to be */ 639 /* Put the cursor back to where it used to be */
394 for (cursor=len; *cursor > pos; cursor--) 640 while (sav_cursor < cursor)
395 xwrite(outputFd, "\b", 1); 641 input_backward();
396 } 642 }
397 } 643 }
398} 644}
@@ -435,18 +681,19 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
435{ 681{
436 682
437 int inputFd=fileno(stdin); 683 int inputFd=fileno(stdin);
438 int outputFd=fileno(stdout); 684
439 int nr = 0;
440 int len = 0;
441 int j = 0; 685 int j = 0;
442 int cursor = 0;
443 int break_out = 0; 686 int break_out = 0;
444 int ret = 0; 687 int ret = 0;
445 int lastWasTab = FALSE; 688 int lastWasTab = FALSE;
446 char c = 0; 689 char c = 0;
447 struct history *hp = his_end; 690 struct history *hp = his_end;
448 691
449 if (!reset_term) { 692 len = 0;
693 cursor = 0;
694 command_ps = command;
695
696 if (new_settings.c_cc[VMIN]==0) {
450 697
451 getTermSettings(inputFd, (void*) &initial_settings); 698 getTermSettings(inputFd, (void*) &initial_settings);
452 memcpy(&new_settings, &initial_settings, sizeof(struct termios)); 699 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
@@ -455,17 +702,26 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
455 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */ 702 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */
456 new_settings.c_lflag &= ~ICANON; /* unbuffered input */ 703 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
457 new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */ 704 new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */
458 reset_term = 1;
459 } 705 }
460 setTermSettings(inputFd, (void*) &new_settings); 706 setTermSettings(inputFd, (void*) &new_settings);
707 handlers_sets |= SET_RESET_TERM;
461 708
462 memset(command, 0, BUFSIZ); 709 memset(command, 0, BUFSIZ);
463 710
711 cmdedit_init();
712
464 /* Print out the command prompt */ 713 /* Print out the command prompt */
465 xwrite(outputFd, prompt, strlen(prompt)); 714 cmdedit_prompt = prompt;
715 cmdedit_prmt_len = strlen(prompt);
716 printf("%s", prompt);
717 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
718 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
719
466 720
467 while (1) { 721 while (1) {
468 722
723 fflush(stdout); /* buffered out to fast */
724
469 if ((ret = read(inputFd, &c, 1)) < 1) 725 if ((ret = read(inputFd, &c, 1)) < 1)
470 return; 726 return;
471 //fprintf(stderr, "got a '%c' (%d)\n", c, c); 727 //fprintf(stderr, "got a '%c' (%d)\n", c, c);
@@ -474,16 +730,18 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
474 case '\n': 730 case '\n':
475 case '\r': 731 case '\r':
476 /* Enter */ 732 /* Enter */
477 *(command + len++ + 1) = c; 733 *(command + len) = c;
478 xwrite(outputFd, &c, 1); 734 len++;
735 input_end ();
479 break_out = 1; 736 break_out = 1;
480 break; 737 break;
481 case 1: 738 case 1:
482 /* Control-a -- Beginning of line */ 739 /* Control-a -- Beginning of line */
483 input_home(outputFd, &cursor); 740 input_home();
741 break;
484 case 2: 742 case 2:
485 /* Control-b -- Move back one character */ 743 /* Control-b -- Move back one character */
486 input_backward(outputFd, &cursor); 744 input_backward();
487 break; 745 break;
488 case 3: 746 case 3:
489 /* Control-c -- stop gathering input */ 747 /* Control-c -- stop gathering input */
@@ -492,11 +750,11 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
492 shell_context = 0; 750 shell_context = 0;
493 751
494 /* Go to the next line */ 752 /* Go to the next line */
495 xwrite(outputFd, "\n", 1); 753 goto_new_line();
496 754
497#if 0 755#if 0
498 /* Rewrite the prompt */ 756 /* Rewrite the prompt */
499 xwrite(outputFd, prompt, strlen(prompt)); 757 printf("%s", prompt);
500 758
501 /* Reset the command string */ 759 /* Reset the command string */
502 memset(command, 0, BUFSIZ); 760 memset(command, 0, BUFSIZ);
@@ -508,29 +766,28 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
508 /* Control-d -- Delete one character, or exit 766 /* Control-d -- Delete one character, or exit
509 * if the len=0 and no chars to delete */ 767 * if the len=0 and no chars to delete */
510 if (len == 0) { 768 if (len == 0) {
511 xwrite(outputFd, "exit", 4); 769 printf("exit");
512 clean_up_and_die(0); 770 clean_up_and_die(0);
513 } else { 771 } else {
514 input_delete(command, outputFd, cursor, &len); 772 input_delete();
515 } 773 }
516 break; 774 break;
517 case 5: 775 case 5:
518 /* Control-e -- End of line */ 776 /* Control-e -- End of line */
519 input_end(outputFd, &cursor, len); 777 input_end();
520 break; 778 break;
521 case 6: 779 case 6:
522 /* Control-f -- Move forward one character */ 780 /* Control-f -- Move forward one character */
523 input_forward(outputFd, &cursor, len); 781 input_forward();
524 break; 782 break;
525 case '\b': 783 case '\b':
526 case DEL: 784 case DEL:
527 /* Control-h and DEL */ 785 /* Control-h and DEL */
528 input_backspace(command, outputFd, &cursor, &len); 786 input_backspace();
529 break; 787 break;
530 case '\t': 788 case '\t':
531#ifdef BB_FEATURE_SH_TAB_COMPLETION 789#ifdef BB_FEATURE_SH_TAB_COMPLETION
532 input_tab(command, prompt, outputFd, &cursor, 790 input_tab(lastWasTab);
533&len, lastWasTab);
534#endif 791#endif
535 break; 792 break;
536 case 14: 793 case 14:
@@ -539,7 +796,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
539 get_next_history(&hp, command); 796 get_next_history(&hp, command);
540 goto rewrite_line; 797 goto rewrite_line;
541 } else { 798 } else {
542 xwrite(outputFd, "\007", 1); 799 beep();
543 } 800 }
544 break; 801 break;
545 case 16: 802 case 16:
@@ -548,7 +805,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
548 get_previous_history(&hp, command); 805 get_previous_history(&hp, command);
549 goto rewrite_line; 806 goto rewrite_line;
550 } else { 807 } else {
551 xwrite(outputFd, "\007", 1); 808 beep();
552 } 809 }
553 break; 810 break;
554 case ESC:{ 811 case ESC:{
@@ -567,7 +824,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
567 get_previous_history(&hp, command); 824 get_previous_history(&hp, command);
568 goto rewrite_line; 825 goto rewrite_line;
569 } else { 826 } else {
570 xwrite(outputFd, "\007", 1); 827 beep();
571 } 828 }
572 break; 829 break;
573 case 'B': 830 case 'B':
@@ -576,49 +833,52 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
576 get_next_history(&hp, command); 833 get_next_history(&hp, command);
577 goto rewrite_line; 834 goto rewrite_line;
578 } else { 835 } else {
579 xwrite(outputFd, "\007", 1); 836 beep();
580 } 837 }
581 break; 838 break;
582 839
583 /* Rewrite the line with the selected history item */ 840 /* Rewrite the line with the selected history item */
584 rewrite_line: 841 rewrite_line:
585 /* erase old command from command line */ 842 /* return to begin of line */
586 len = strlen(command)-strlen(hp->s); 843 input_home ();
587 844 /* for next memmoves without set '\0' */
588 while (len>cursor) 845 memset (command, 0, BUFSIZ);
589 input_delete(command, outputFd, cursor, &len); 846 /* change command */
590 while (cursor>0) 847 strcpy (command, hp->s);
591 input_backspace(command, outputFd, &cursor, &len);
592 input_home(outputFd, &cursor);
593
594 /* write new command */ 848 /* write new command */
595 strcpy(command, hp->s); 849 for (j=0; command[j]; j++)
596 len = strlen(hp->s); 850 cmdedit_set_out_char(command[j], 0);
597 xwrite(outputFd, command, len); 851 ret = cursor;
598 cursor = len; 852 /* erase tail if required */
853 for (j = ret; j < len; j++)
854 cmdedit_set_out_char(' ', 0);
855 /* and backward cursor */
856 for (j = ret; j < len; j++)
857 input_backward();
858 len = cursor; /* set new len */
599 break; 859 break;
600 case 'C': 860 case 'C':
601 /* Right Arrow -- Move forward one character */ 861 /* Right Arrow -- Move forward one character */
602 input_forward(outputFd, &cursor, len); 862 input_forward();
603 break; 863 break;
604 case 'D': 864 case 'D':
605 /* Left Arrow -- Move back one character */ 865 /* Left Arrow -- Move back one character */
606 input_backward(outputFd, &cursor); 866 input_backward();
607 break; 867 break;
608 case '3': 868 case '3':
609 /* Delete */ 869 /* Delete */
610 input_delete(command, outputFd, cursor, &len); 870 input_delete();
611 break; 871 break;
612 case '1': 872 case '1':
613 /* Home (Ctrl-A) */ 873 /* Home (Ctrl-A) */
614 input_home(outputFd, &cursor); 874 input_home();
615 break; 875 break;
616 case '4': 876 case '4':
617 /* End (Ctrl-E) */ 877 /* End (Ctrl-E) */
618 input_end(outputFd, &cursor, len); 878 input_end();
619 break; 879 break;
620 default: 880 default:
621 xwrite(outputFd, "\007", 1); 881 beep();
622 } 882 }
623 if (c == '1' || c == '3' || c == '4') 883 if (c == '1' || c == '3' || c == '4')
624 if ((ret = read(inputFd, &c, 1)) < 1) 884 if ((ret = read(inputFd, &c, 1)) < 1)
@@ -631,14 +891,14 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
631 switch (c) { 891 switch (c) {
632 case 'H': 892 case 'H':
633 /* Home (xterm) */ 893 /* Home (xterm) */
634 input_home(outputFd, &cursor); 894 input_home();
635 break; 895 break;
636 case 'F': 896 case 'F':
637 /* End (xterm) */ 897 /* End (xterm) */
638 input_end(outputFd, &cursor, len); 898 input_end();
639 break; 899 break;
640 default: 900 default:
641 xwrite(outputFd, "\007", 1); 901 beep();
642 } 902 }
643 } 903 }
644 c = 0; 904 c = 0;
@@ -658,20 +918,20 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
658 918
659 if (cursor == (len - 1)) { /* Append if at the end of the line */ 919 if (cursor == (len - 1)) { /* Append if at the end of the line */
660 *(command + cursor) = c; 920 *(command + cursor) = c;
921 cmdedit_set_out_char(c, command[cursor+1]);
661 } else { /* Insert otherwise */ 922 } else { /* Insert otherwise */
662 memmove(command + cursor + 1, command + cursor, 923 memmove(command + cursor + 1, command + cursor,
663 len - cursor - 1); 924 len - cursor - 1);
664 925
665 *(command + cursor) = c; 926 *(command + cursor) = c;
666 927 j = cursor+1;
667 for (j = cursor; j < len; j++) 928 /* rewrite from cursor */
668 xwrite(outputFd, command + j, 1); 929 input_end ();
669 for (; j > cursor; j--) 930 /* to prev x pos + 1 */
670 xwrite(outputFd, "\033[D", 3); 931 while(cursor > j)
932 input_backward();
671 } 933 }
672 934
673 cursor++;
674 xwrite(outputFd, &c, 1);
675 break; 935 break;
676 } 936 }
677 if (c == '\t') 937 if (c == '\t')
@@ -683,15 +943,18 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
683 break; 943 break;
684 } 944 }
685 945
686 nr = len + 1; 946 setTermSettings (inputFd, (void *) &initial_settings);
687 setTermSettings(inputFd, (void *) &initial_settings); 947 handlers_sets &= ~SET_RESET_TERM;
688 reset_term = 0;
689
690 948
691 /* Handle command history log */ 949 /* Handle command history log */
692 if (*(command)) { 950 if (len>1) { /* no put empty line (only '\n') */
693 951
694 struct history *h = his_end; 952 struct history *h = his_end;
953 char *ss;
954
955 command[len-1] = 0; /* destroy end '\n' */
956 ss = strdup(command); /* duplicate without '\n' */
957 command[len-1] = '\n'; /* restore '\n' */
695 958
696 if (!h) { 959 if (!h) {
697 /* No previous history -- this memory is never freed */ 960 /* No previous history -- this memory is never freed */
@@ -699,7 +962,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
699 h->n = xmalloc(sizeof(struct history)); 962 h->n = xmalloc(sizeof(struct history));
700 963
701 h->p = NULL; 964 h->p = NULL;
702 h->s = strdup(command); 965 h->s = ss;
703 h->n->p = h; 966 h->n->p = h;
704 h->n->n = NULL; 967 h->n->n = NULL;
705 h->n->s = NULL; 968 h->n->s = NULL;
@@ -712,7 +975,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
712 h->n->p = h; 975 h->n->p = h;
713 h->n->n = NULL; 976 h->n->n = NULL;
714 h->n->s = NULL; 977 h->n->s = NULL;
715 h->s = strdup(command); 978 h->s = ss;
716 his_end = h->n; 979 his_end = h->n;
717 980
718 /* After max history, remove the oldest command */ 981 /* After max history, remove the oldest command */
@@ -733,38 +996,21 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
733 return; 996 return;
734} 997}
735 998
736extern void cmdedit_init(void)
737{
738 win_changed(0);
739 signal(SIGWINCH, win_changed);
740 999
741 if(exithandler_set == 0) { 1000/* Undo the effects of cmdedit_init(). */
742 atexit(cmdedit_reset_term); /* be sure to do this only once */
743 exithandler_set = 1;
744 }
745 signal(SIGKILL, clean_up_and_die);
746 signal(SIGINT, clean_up_and_die);
747 signal(SIGQUIT, clean_up_and_die);
748 signal(SIGTERM, clean_up_and_die);
749}
750
751/*
752** Undo the effects of cmdedit_init() as good as we can:
753** I am not aware of a way to revoke an atexit() handler,
754** but, fortunately, our particular handler can be made
755** a no-op by setting reset_term = 0.
756*/
757extern void cmdedit_terminate(void) 1001extern void cmdedit_terminate(void)
758{ 1002{
759 cmdedit_reset_term(); 1003 cmdedit_reset_term();
760 reset_term = 0; 1004 if((handlers_sets & SET_TERM_HANDLERS)!=0) {
761 signal(SIGKILL, SIG_DFL); 1005 signal(SIGKILL, SIG_DFL);
762 signal(SIGINT, SIG_DFL); 1006 signal(SIGINT, SIG_DFL);
763 signal(SIGQUIT, SIG_DFL); 1007 signal(SIGQUIT, SIG_DFL);
764 signal(SIGTERM, SIG_DFL); 1008 signal(SIGTERM, SIG_DFL);
765 signal(SIGWINCH, SIG_DFL); 1009 signal(SIGWINCH, SIG_DFL);
1010 handlers_sets &= ~SET_TERM_HANDLERS;
1011 }
766} 1012}
767 1013
768 1014
769 1015
770#endif /* BB_FEATURE_SH_COMMAND_EDITING */ 1016#endif /* BB_FEATURE_SH_COMMAND_EDITING */
diff --git a/shell/cmdedit.c b/shell/cmdedit.c
index 12c78dc76..c2b4123db 100644
--- a/shell/cmdedit.c
+++ b/shell/cmdedit.c
@@ -25,13 +25,20 @@
25 need to be added. This version was created on Debian GNU/Linux 2.x. 25 need to be added. This version was created on Debian GNU/Linux 2.x.
26 Delete, Backspace, Home, End, and the arrow keys were tested 26 Delete, Backspace, Home, End, and the arrow keys were tested
27 to work in an Xterm and console. Ctrl-A also works as Home. 27 to work in an Xterm and console. Ctrl-A also works as Home.
28 Ctrl-E also works as End. The binary size increase is <3K. 28 Ctrl-E also works as End.
29 29
30 Editting will not display correctly for lines greater then the 30
31 terminal width. (more then one line.) However, history will. 31 Editor with vertical scrolling and completion by
32 Vladimir Oleynik. vodz@usa.net (c) 2001
33
34 Small bug: not true work if terminal size (x*y symbols) less
35 size (prompt + editor`s line + 2 symbols)
32 */ 36 */
33 37
38
39
34#include "busybox.h" 40#include "busybox.h"
41
35#ifdef BB_FEATURE_SH_COMMAND_EDITING 42#ifdef BB_FEATURE_SH_COMMAND_EDITING
36 43
37#include <stdio.h> 44#include <stdio.h>
@@ -43,6 +50,13 @@
43#include <ctype.h> 50#include <ctype.h>
44#include <signal.h> 51#include <signal.h>
45 52
53#ifdef BB_FEATURE_SH_TAB_COMPLETION
54#include <sys/stat.h>
55#endif
56
57#ifdef BB_FEATURE_USERNAME_COMPLETION
58#include <pwd.h>
59#endif
46 60
47static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */ 61static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */
48 62
@@ -58,7 +72,6 @@ static struct history *his_front = NULL; /* First element in command line list *
58static struct history *his_end = NULL; /* Last element in command line list */ 72static struct history *his_end = NULL; /* Last element in command line list */
59 73
60/* ED: sparc termios is broken: revert back to old termio handling. */ 74/* ED: sparc termios is broken: revert back to old termio handling. */
61#ifdef BB_FEATURE_USE_TERMIOS
62 75
63#if #cpu(sparc) 76#if #cpu(sparc)
64# include <termio.h> 77# include <termio.h>
@@ -79,16 +92,35 @@ static struct termios initial_settings, new_settings;
79#define _POSIX_VDISABLE '\0' 92#define _POSIX_VDISABLE '\0'
80#endif 93#endif
81 94
82#endif
83 95
96static
97volatile int cmdedit_termw; /* actual terminal width */
98static int history_counter = 0; /* Number of commands in history list */
84 99
100static
101volatile int handlers_sets = 0; /* Set next bites
102 when atexit() has been called
103 and set many "terminates" signal handlers
104 and winchg signal handler
105 and if the terminal needs to be reset upon exit
106 */
107enum {
108 SET_ATEXIT = 1,
109 SET_TERM_HANDLERS = 2,
110 SET_WCHG_HANDLERS = 4,
111 SET_RESET_TERM = 8,
112};
85 113
86static int cmdedit_termw = 80; /* actual terminal width */
87static int cmdedit_scroll = 27; /* width of EOL scrolling region */
88static int history_counter = 0; /* Number of commands in history list */
89static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */
90static int exithandler_set = 0; /* Set to true when atexit() has been called */
91 114
115static int cmdedit_x; /* real x terminal position,
116 require put prompt in start x position */
117static int cmdedit_y; /* pseudoreal y terminal position */
118static int cmdedit_prmt_len; /* for fast running, without duplicate calculate */
119
120static int cursor; /* required global for signal handler */
121static int len; /* --- "" - - "" - -"- --""-- --""--- */
122static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */
123static const char *cmdedit_prompt;/* --- "" - - "" - -"- --""-- --""--- */
92 124
93/* Link into lash to reset context to 0 125/* Link into lash to reset context to 0
94 * on ^C and such */ 126 * on ^C and such */
@@ -101,38 +133,43 @@ struct history {
101 struct history *n; 133 struct history *n;
102}; 134};
103 135
104#define xwrite write 136static void cmdedit_setwidth(int w, int redraw_flg);
105 137
106/* 138static void win_changed(int nsig)
107 * TODO: Someday we want to implement 'horizontal scrolling' of the
108 * command-line when the user has typed more than the current width. This
109 * would allow the user to see a 'window' of what he has typed.
110 */
111static void cmdedit_setwidth(int w)
112{
113 if (w > 20) {
114 cmdedit_termw = w;
115 cmdedit_scroll = w / 3;
116 } else {
117 error_msg("\n*** Error: minimum screen width is 21\n");
118 }
119}
120
121static void win_changed(int junk)
122{ 139{
123 struct winsize win = { 0, 0, 0, 0 }; 140 struct winsize win = { 0, 0, 0, 0 };
124 ioctl(0, TIOCGWINSZ, &win); 141 static __sighandler_t previous_SIGWINCH_handler; /* for reset */
125 if (win.ws_col > 0) { 142
126 cmdedit_setwidth( win.ws_col - 1); 143 /* emulate || signal call */
144 if(nsig == -SIGWINCH || nsig == SIGWINCH) {
145 ioctl(0, TIOCGWINSZ, &win);
146 if (win.ws_col > 0) {
147 cmdedit_setwidth( win.ws_col, nsig == SIGWINCH );
148 }
127 } 149 }
150 /* Unix not all standart in recall signal */
151
152 if(nsig == -SIGWINCH) /* save previous handler */
153 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
154 else if(nsig == SIGWINCH) /* signaled called handler */
155 signal(SIGWINCH, win_changed); /* set for next call */
156 else /* set previous handler */
157 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
128} 158}
129 159
130
131static void cmdedit_reset_term(void) 160static void cmdedit_reset_term(void)
132{ 161{
133 if (reset_term) 162 if((handlers_sets & SET_RESET_TERM)!=0) {
134 /* sparc and other have broken termios support: use old termio handling. */ 163 /* sparc and other have broken termios support: use old termio handling. */
135 setTermSettings(fileno(stdin), (void*) &initial_settings); 164 setTermSettings(fileno(stdin), (void*) &initial_settings);
165 handlers_sets &= ~SET_RESET_TERM;
166 }
167 if((handlers_sets & SET_WCHG_HANDLERS)!=0) {
168 /* reset SIGWINCH handler to previous (default) */
169 win_changed(0);
170 handlers_sets &= ~SET_WCHG_HANDLERS;
171 }
172 fflush(stdout);
136#ifdef BB_FEATURE_CLEAN_UP 173#ifdef BB_FEATURE_CLEAN_UP
137 if (his_front) { 174 if (his_front) {
138 struct history *n; 175 struct history *n;
@@ -147,169 +184,348 @@ static void cmdedit_reset_term(void)
147#endif 184#endif
148} 185}
149 186
150static void clean_up_and_die(int sig)
151{
152 cmdedit_reset_term();
153 printf("\n");
154 if (sig!=SIGINT)
155 exit(EXIT_SUCCESS);
156}
157 187
158/* Go to HOME position */
159static void input_home(int outputFd, int *cursor)
160{
161 while (*cursor > 0) {
162 xwrite(outputFd, "\b", 1);
163 --*cursor;
164 }
165}
166 188
167/* Go to END position */ 189/* special for recount position for scroll and remove terminal margin effect */
168static void input_end(int outputFd, int *cursor, int len) 190static void cmdedit_set_out_char(int c, int next_char) {
169{ 191 putchar(c);
170 while (*cursor < len) { 192 if(++cmdedit_x>=cmdedit_termw) {
171 xwrite(outputFd, "\033[C", 3); 193 /* terminal is scrolled down */
172 ++*cursor; 194 cmdedit_y++;
195 cmdedit_x=0;
196
197 if(!next_char)
198 next_char = ' ';
199 /* destroy "(auto)margin" */
200 putchar(next_char);
201 putchar('\b');
173 } 202 }
203 cursor++;
174} 204}
175 205
176/* Delete the char in back of the cursor */ 206/* Move to end line. Bonus: rewrite line from cursor without use
177static void input_backspace(char* command, int outputFd, int *cursor, int *len) 207 special control terminal strings, also saved size and speed! */
178{ 208static void input_end (void) {
179 int j = 0; 209 while(cursor < len)
180 210 cmdedit_set_out_char(command_ps[cursor], 0);
181/* Debug crap */ 211}
182//fprintf(stderr, "\nerik: len=%d, cursor=%d, strlen(command)='%d'\n", *len, *cursor, strlen(command));
183//xwrite(outputFd, command, *len);
184//*cursor = *len;
185 212
213/* Go to the next line */
214static void goto_new_line(void) {
215 input_end();
216 cmdedit_set_out_char('\n', 0);
217}
186 218
187 if (*cursor > 0) {
188 xwrite(outputFd, "\b \b", 3);
189 --*cursor;
190 memmove(command + *cursor, command + *cursor + 1,
191 BUFSIZ - *cursor + 1);
192 219
193 for (j = *cursor; j < (BUFSIZ - 1); j++) { 220static inline void out1str(const char *s) { fputs (s, stdout); }
194 if (!*(command + j)) 221static inline void beep (void) { putchar('\007'); }
195 break;
196 else
197 xwrite(outputFd, (command + j), 1);
198 }
199 222
200 xwrite(outputFd, " \b", 2); 223/* Go to HOME position */
224static void input_home(void)
225{
226 while(cmdedit_y>0) { /* up to start y */
227 out1str("\033[A");
228 cmdedit_y--;
229 }
230 putchar('\r');
231 cursor = 0;
232 out1str(cmdedit_prompt);
233 cmdedit_x = cmdedit_prmt_len;
201 234
202 while (j-- > *cursor) 235}
203 xwrite(outputFd, "\b", 1);
204 236
205 --*len; 237/* Move back one charactor */
238static void input_backward(void) {
239 if (cursor > 0) {
240 cursor--;
241 if(cmdedit_x!=0) { /* no first position in terminal line */
242 putchar('\b');
243 cmdedit_x--;
244 }
245 else {
246 out1str("\033[A"); /* up */
247 cmdedit_y--;
248
249 /* to end in current terminal line */
250 while(cmdedit_x<(cmdedit_termw-1)) {
251 out1str("\033[C");
252 cmdedit_x++;
253 }
254 }
206 } 255 }
207} 256}
208 257
209/* Delete the char in front of the cursor */ 258/* Delete the char in front of the cursor */
210static void input_delete(char* command, int outputFd, int cursor, int *len) 259static void input_delete(void)
211{ 260{
212 int j = 0; 261 int j = cursor;
213 262
214 if (cursor == *len) 263 if (j == len)
215 return; 264 return;
216 265
217 memmove(command + cursor, command + cursor + 1, 266 memmove (command_ps + j, command_ps + j + 1, BUFSIZ - j - 1);
218 BUFSIZ - cursor - 1); 267 len--;
219 for (j = cursor; j < (BUFSIZ - 1); j++) { 268 input_end(); /* rewtite new line */
220 if (!*(command + j)) 269 cmdedit_set_out_char(' ', 0); /* destroy end char */
221 break; 270 while (j < cursor)
222 else 271 input_backward(); /* back to old pos cursor */
223 xwrite(outputFd, (command + j), 1); 272}
273
274/* Delete the char in back of the cursor */
275static void input_backspace(void)
276{
277 if (cursor > 0) {
278 input_backward();
279 input_delete ();
224 } 280 }
281}
225 282
226 xwrite(outputFd, " \b", 2);
227 283
228 while (j-- > cursor) 284/* Move forward one charactor */
229 xwrite(outputFd, "\b", 1); 285static void input_forward(void)
230 --*len; 286{
287 if (cursor < len)
288 cmdedit_set_out_char(command_ps[cursor], command_ps[cursor + 1]);
231} 289}
232 290
233/* Move forward one charactor */ 291
234static void input_forward(int outputFd, int *cursor, int len) 292static void clean_up_and_die(int sig)
235{ 293{
236 if (*cursor < len) { 294 goto_new_line();
237 xwrite(outputFd, "\033[C", 3); 295 if (sig!=SIGINT)
238 ++*cursor; 296 exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */
239 } 297 cmdedit_reset_term();
240} 298}
241 299
242/* Move back one charactor */ 300static void cmdedit_setwidth(int w, int redraw_flg)
243static void input_backward(int outputFd, int *cursor)
244{ 301{
245 if (*cursor > 0) { 302 cmdedit_termw = cmdedit_prmt_len+2;
246 xwrite(outputFd, "\033[D", 3); 303 if (w > cmdedit_termw) {
247 --*cursor; 304
305 cmdedit_termw = w;
306
307 if(redraw_flg) {
308 int sav_cursor = cursor;
309
310 /* set variables for new terminal size */
311 cmdedit_y = sav_cursor/w;
312 cmdedit_x = sav_cursor-cmdedit_y*w;
313
314 /* redraw */
315 input_home();
316 input_end();
317 while(sav_cursor<cursor)
318 input_backward();
319 }
320 } else {
321 error_msg("\n*** Error: minimum screen width is %d\n", cmdedit_termw);
248 } 322 }
249} 323}
250 324
325extern void cmdedit_init(void)
326{
327 if((handlers_sets & SET_WCHG_HANDLERS)==0) {
328 /* emulate usage handler to set handler and call yours work */
329 win_changed(-SIGWINCH);
330 handlers_sets |= SET_WCHG_HANDLERS;
331 }
251 332
333 if((handlers_sets & SET_ATEXIT)==0) {
334 atexit(cmdedit_reset_term); /* be sure to do this only once */
335 handlers_sets |= SET_ATEXIT;
336 }
337 if((handlers_sets & SET_TERM_HANDLERS)==0) {
338 signal(SIGKILL, clean_up_and_die);
339 signal(SIGINT, clean_up_and_die);
340 signal(SIGQUIT, clean_up_and_die);
341 signal(SIGTERM, clean_up_and_die);
342 handlers_sets |= SET_TERM_HANDLERS;
343 }
344}
252 345
253#ifdef BB_FEATURE_SH_TAB_COMPLETION 346#ifdef BB_FEATURE_SH_TAB_COMPLETION
254static char** username_tab_completion(char* command, int *num_matches) 347
348#ifdef BB_FEATURE_USERNAME_COMPLETION
349static char** username_tab_completion(char *ud, int *num_matches)
255{ 350{
351 static struct passwd *entry;
352 int userlen;
256 char **matches = (char **) NULL; 353 char **matches = (char **) NULL;
257 *num_matches=0; 354 char *temp;
258 fprintf(stderr, "\nin username_tab_completion\n"); 355 int nm = 0;
356
357 setpwent ();
358 userlen = strlen (ud + 1);
359
360 while ((entry = getpwent ()) != NULL) {
361 /* Null usernames should result in all users as possible completions. */
362 if (!userlen || !strncmp (ud + 1, entry->pw_name, userlen)) {
363
364 temp = xmalloc (3 + strlen (entry->pw_name));
365 sprintf(temp, "~%s/", entry->pw_name);
366
367 matches = xrealloc(matches, (nm+1)*sizeof(char *));
368 matches[nm++] = temp;
369 }
370 }
371
372 endpwent ();
373 (*num_matches) = nm;
259 return (matches); 374 return (matches);
260} 375}
376#endif
377
378enum {
379 FIND_EXE_ONLY = 0,
380 FIND_DIR_ONLY = 1,
381 FIND_FILE_ONLY = 2,
382};
261 383
262#include <dirent.h> 384#include <dirent.h>
263static char** exe_n_cwd_tab_completion(char* command, int *num_matches) 385
386static int path_parse(char ***p, int flags)
387{
388 int npth;
389 char *tmp;
390 char *pth;
391
392 if(flags!=FIND_EXE_ONLY || (pth=getenv("PATH"))==0) {
393 /* if not setenv PATH variable, to search cur dir "." */
394 (*p) = xmalloc(sizeof(char *));
395 (*p)[0] = xstrdup(".");
396 return 1;
397 }
398
399 tmp = pth;
400 npth=0;
401
402 for(;;) {
403 npth++; /* count words is + 1 count ':' */
404 tmp = strchr(tmp, ':');
405 if(tmp)
406 tmp++;
407 else
408 break;
409 }
410
411 *p = xmalloc(npth*sizeof(char *));
412
413 tmp = pth;
414 (*p)[0] = xstrdup(tmp);
415 npth=1; /* count words is + 1 count ':' */
416
417 for(;;) {
418 tmp = strchr(tmp, ':');
419 if(tmp) {
420 (*p)[0][(tmp-pth)]=0; /* ':' -> '\0'*/
421 tmp++;
422 } else
423 break;
424 (*p)[npth++] = &(*p)[0][(tmp-pth)]; /* p[next]=p[0][&'\0'+1] */
425 }
426
427 return npth;
428}
429
430static char** exe_n_cwd_tab_completion(char* command, int *num_matches, int type)
264{ 431{
265 char *dirName; 432 char *dirName;
266 char **matches; 433 char **matches = 0;
267 DIR *dir; 434 DIR *dir;
268 struct dirent *next; 435 struct dirent *next;
436 char cmd [BUFSIZ+4];
437 char *dirbuf;
438 char found [BUFSIZ+4];
439 int nm = *num_matches;
440 struct stat st;
441 char **paths;
442 int npaths;
443 int i;
444 char full_pth[BUFSIZ+4+PATH_MAX];
445
446
447 strcpy(cmd, command); /* save for change (last '/' to '\0') */
448
449 dirName = strrchr(cmd, '/');
450 if(dirName==NULL) {
451 /* no dir, if flags==EXE_ONLY - get paths, else "." */
452 npaths = path_parse(&paths, type);
453 if(npaths==0)
454 return 0;
455 } else {
456 /* with dir */
457
458 /* save dir */
459 dirbuf = xstrdup(cmd);
460 /* set only dirname */
461 dirbuf[(dirName-cmd)+1]=0;
462
463 /* strip dirname in cmd */
464 strcpy(cmd, dirName+1);
269 465
270 matches = xmalloc( sizeof(char*)*50); 466 paths = xmalloc(sizeof(char*));
467 paths[0] = dirbuf;
468 npaths = 1; /* only 1 dir */
469 }
271 470
272 /* Stick a wildcard onto the command, for later use */ 471 for(i=0; i < npaths; i++) {
273 strcat( command, "*");
274 472
275 /* Now wall the current directory */ 473 dir = opendir(paths[i]);
276 dirName = get_current_dir_name();
277 dir = opendir(dirName);
278 if (!dir) { 474 if (!dir) {
279 /* Don't print an error, just shut up and return */ 475 /* Don't print an error, just shut up and return */
280 *num_matches=0;
281 return (matches); 476 return (matches);
282 } 477 }
283 while ((next = readdir(dir)) != NULL) { 478 while ((next = readdir(dir)) != NULL) {
284 479 /* matched ? */
285 /* Some quick sanity checks */ 480 if(strncmp(next->d_name, cmd, strlen(cmd)))
286 if ((strcmp(next->d_name, "..") == 0) 481 continue;
287 || (strcmp(next->d_name, ".") == 0)) { 482 /* not see .name without .match */
483 if(*next->d_name == '.' && *cmd != '.')
484 continue;
485 sprintf(full_pth, "%s/%s", paths[i], next->d_name);
486 /* hmm, remover in progress? */
487 if(stat(full_pth, &st)<0)
488 continue;
489 /* Cool, found a match. */
490 if (S_ISDIR(st.st_mode)) {
491 /* name is directory */
492 strcpy(found, next->d_name);
493 strcat(found, "/");
494 if(type==FIND_DIR_ONLY)
495 strcat(found, " ");
496 } else {
497 /* not put found file if search only dirs for cd */
498 if(type==FIND_DIR_ONLY)
288 continue; 499 continue;
500 strcpy(found, next->d_name);
501 strcat(found, " ");
289 } 502 }
290 /* See if this matches */ 503 /* Add it to the list */
291 if (check_wildcard_match(next->d_name, command) == TRUE) { 504 matches = xrealloc(matches, (nm+1)*sizeof(char *));
292 /* Cool, found a match. Add it to the list */ 505 matches[nm++] = xstrdup(found);
293 matches[*num_matches] = xmalloc(strlen(next->d_name)+1);
294 strcpy( matches[*num_matches], next->d_name);
295 ++*num_matches;
296 //matches = realloc( matches, sizeof(char*)*(*num_matches));
297 } 506 }
298 } 507 }
299 508 free(paths[0]); /* allocate memory only in first member */
509 free(paths);
510 *num_matches = nm;
300 return (matches); 511 return (matches);
301} 512}
302 513
303static void input_tab(char* command, char* prompt, int outputFd, int *cursor, int *len, int lastWasTab) 514static void input_tab(int lastWasTab)
304{ 515{
305 /* Do TAB completion */ 516 /* Do TAB completion */
306 static int num_matches=0; 517 static int num_matches;
307 static char **matches = (char **) NULL; 518 static char **matches;
308 int pos = *cursor; 519
520 char matchBuf[BUFSIZ];
521
522 int pos = cursor;
523 int find_type=FIND_FILE_ONLY;
309 524
310 525
311 if (lastWasTab == FALSE) { 526 if (lastWasTab == FALSE) {
312 char *tmp, *tmp1, *matchBuf; 527 char *tmp, *tmp1;
528 int len_found;
313 529
314 /* For now, we will not bother with trying to distinguish 530 /* For now, we will not bother with trying to distinguish
315 * whether the cursor is in/at a command extression -- we 531 * whether the cursor is in/at a command extression -- we
@@ -319,50 +535,80 @@ static void input_tab(char* command, char* prompt, int outputFd, int *cursor, in
319 535
320 /* Make a local copy of the string -- up 536 /* Make a local copy of the string -- up
321 * to the position of the cursor */ 537 * to the position of the cursor */
322 matchBuf = (char *) xcalloc(BUFSIZ, sizeof(char)); 538 memset(matchBuf, 0, BUFSIZ);
323 strncpy(matchBuf, command, *cursor); 539 tmp = strncpy(matchBuf, command_ps, cursor);
324 tmp=matchBuf;
325 540
326 /* skip past any command seperator tokens */ 541 /* skip past any command seperator tokens */
327 while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { 542 while ( (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
328 tmp=++tmp1; 543 tmp = ++tmp1;
329 /* skip any leading white space */
330 while (*tmp && isspace(*tmp))
331 ++tmp;
332 } 544 }
333 545
334 /* skip any leading white space */ 546 /* skip any leading white space */
335 while (*tmp && isspace(*tmp)) 547 while (*tmp == ' ')
336 ++tmp; 548 tmp++;
549
550 if(strncmp(tmp, "cd ", 3)==0)
551 find_type = FIND_DIR_ONLY;
552 else if(strchr(tmp, ' ')==NULL)
553 find_type = FIND_EXE_ONLY;
554
555 /* find begin curent word */
556 if( (tmp1=strrchr(tmp, ' ')) != NULL) {
557 tmp = ++tmp1;
558 }
559 strcpy(matchBuf, tmp);
337 560
338 /* Free up any memory already allocated */ 561 /* Free up any memory already allocated */
339 if (matches) { 562 if (matches) {
563 while(num_matches>0)
564 free(matches[--num_matches]);
340 free(matches); 565 free(matches);
341 matches = (char **) NULL; 566 matches = (char **) NULL;
342 } 567 }
343 568
569#ifdef BB_FEATURE_USERNAME_COMPLETION
344 /* If the word starts with `~' and there is no slash in the word, 570 /* If the word starts with `~' and there is no slash in the word,
345 * then try completing this word as a username. */ 571 * then try completing this word as a username. */
346 572
347 /* FIXME -- this check is broken! */ 573 if (matchBuf[0]=='~' && strchr(matchBuf, '/')==0) {
348 if (*tmp == '~' && !strchr(tmp, '/')) 574 matches = username_tab_completion(matchBuf, &num_matches);
349 matches = username_tab_completion(tmp, &num_matches); 575 }
350 576#endif
351 /* Try to match any executable in our path and everything 577 /* Try to match any executable in our path and everything
352 * in the current working directory that matches. */ 578 * in the current working directory that matches. */
353 if (!matches) 579 if (!matches)
354 matches = exe_n_cwd_tab_completion(tmp, &num_matches); 580 matches = exe_n_cwd_tab_completion(matchBuf, &num_matches, find_type);
355
356 /* Don't leak memory */
357 free( matchBuf);
358 581
359 /* Did we find exactly one match? */ 582 /* Did we find exactly one match? */
360 if (matches && num_matches==1) { 583 if(!matches || num_matches>1) {
584 beep();
585 return;
586 }
587
588 len_found = strlen(matches[0]);
589
590 /* have space to placed match? */
591 if ( (len_found-strlen(matchBuf)+len) < BUFSIZ ) {
592
593 int recalc_pos = len;
594
595 /* before word for match */
596 command_ps[pos-strlen(matchBuf)]=0;
597
598 /* tail line */
599 strcpy(matchBuf, command_ps+pos);
600
601 /* add match */
602 strcat(command_ps, matches[0]);
603 /* add tail */
604 strcat(command_ps, matchBuf);
605
361 /* write out the matched command */ 606 /* write out the matched command */
362 strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos); 607 len=strlen(command_ps);
363 *len=strlen(command); 608 recalc_pos = len-recalc_pos+pos;
364 *cursor=*len; 609 input_end(); /* write */
365 xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos); 610 while(recalc_pos<cursor)
611 input_backward();
366 return; 612 return;
367 } 613 }
368 } else { 614 } else {
@@ -371,28 +617,28 @@ static void input_tab(char* command, char* prompt, int outputFd, int *cursor, in
371 * available choices... */ 617 * available choices... */
372 if ( matches && num_matches>0 ) { 618 if ( matches && num_matches>0 ) {
373 int i, col; 619 int i, col;
620 int sav_cursor = cursor;
374 621
375 /* Go to the next line */ 622 /* Go to the next line */
376 xwrite(outputFd, "\n", 1); 623 goto_new_line();
377 /* Print the list of matches */
378 for (i=0,col=0; i<num_matches; i++) { 624 for (i=0,col=0; i<num_matches; i++) {
379 char foo[17]; 625 printf("%s ", matches[i]);
380 sprintf(foo, "%-14s ", matches[i]); 626 col += strlen(matches[i])+2;
381 col += xwrite(outputFd, foo, strlen(foo)); 627 col -= (col/cmdedit_termw)*cmdedit_termw;
382 if (col > 60 && matches[i+1] != NULL) { 628 if (col > 60 && matches[i+1] != NULL) {
383 xwrite(outputFd, "\n", 1); 629 putchar('\n');
384 col = 0; 630 col = 0;
385 } 631 }
386 } 632 }
387 /* Go to the next line */ 633 /* Go to the next line and rewrite the prompt */
388 xwrite(outputFd, "\n", 1); 634 printf("\n%s", cmdedit_prompt);
389 /* Rewrite the prompt */ 635 cmdedit_x = cmdedit_prmt_len;
390 xwrite(outputFd, prompt, strlen(prompt)); 636 cmdedit_y = 0;
391 /* Rewrite the command */ 637 cursor = 0;
392 xwrite(outputFd, command, *len); 638 input_end(); /* Rewrite the command */
393 /* Put the cursor back to where it used to be */ 639 /* Put the cursor back to where it used to be */
394 for (cursor=len; *cursor > pos; cursor--) 640 while (sav_cursor < cursor)
395 xwrite(outputFd, "\b", 1); 641 input_backward();
396 } 642 }
397 } 643 }
398} 644}
@@ -435,18 +681,19 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
435{ 681{
436 682
437 int inputFd=fileno(stdin); 683 int inputFd=fileno(stdin);
438 int outputFd=fileno(stdout); 684
439 int nr = 0;
440 int len = 0;
441 int j = 0; 685 int j = 0;
442 int cursor = 0;
443 int break_out = 0; 686 int break_out = 0;
444 int ret = 0; 687 int ret = 0;
445 int lastWasTab = FALSE; 688 int lastWasTab = FALSE;
446 char c = 0; 689 char c = 0;
447 struct history *hp = his_end; 690 struct history *hp = his_end;
448 691
449 if (!reset_term) { 692 len = 0;
693 cursor = 0;
694 command_ps = command;
695
696 if (new_settings.c_cc[VMIN]==0) {
450 697
451 getTermSettings(inputFd, (void*) &initial_settings); 698 getTermSettings(inputFd, (void*) &initial_settings);
452 memcpy(&new_settings, &initial_settings, sizeof(struct termios)); 699 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
@@ -455,17 +702,26 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
455 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */ 702 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */
456 new_settings.c_lflag &= ~ICANON; /* unbuffered input */ 703 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
457 new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */ 704 new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */
458 reset_term = 1;
459 } 705 }
460 setTermSettings(inputFd, (void*) &new_settings); 706 setTermSettings(inputFd, (void*) &new_settings);
707 handlers_sets |= SET_RESET_TERM;
461 708
462 memset(command, 0, BUFSIZ); 709 memset(command, 0, BUFSIZ);
463 710
711 cmdedit_init();
712
464 /* Print out the command prompt */ 713 /* Print out the command prompt */
465 xwrite(outputFd, prompt, strlen(prompt)); 714 cmdedit_prompt = prompt;
715 cmdedit_prmt_len = strlen(prompt);
716 printf("%s", prompt);
717 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
718 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
719
466 720
467 while (1) { 721 while (1) {
468 722
723 fflush(stdout); /* buffered out to fast */
724
469 if ((ret = read(inputFd, &c, 1)) < 1) 725 if ((ret = read(inputFd, &c, 1)) < 1)
470 return; 726 return;
471 //fprintf(stderr, "got a '%c' (%d)\n", c, c); 727 //fprintf(stderr, "got a '%c' (%d)\n", c, c);
@@ -474,16 +730,18 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
474 case '\n': 730 case '\n':
475 case '\r': 731 case '\r':
476 /* Enter */ 732 /* Enter */
477 *(command + len++ + 1) = c; 733 *(command + len) = c;
478 xwrite(outputFd, &c, 1); 734 len++;
735 input_end ();
479 break_out = 1; 736 break_out = 1;
480 break; 737 break;
481 case 1: 738 case 1:
482 /* Control-a -- Beginning of line */ 739 /* Control-a -- Beginning of line */
483 input_home(outputFd, &cursor); 740 input_home();
741 break;
484 case 2: 742 case 2:
485 /* Control-b -- Move back one character */ 743 /* Control-b -- Move back one character */
486 input_backward(outputFd, &cursor); 744 input_backward();
487 break; 745 break;
488 case 3: 746 case 3:
489 /* Control-c -- stop gathering input */ 747 /* Control-c -- stop gathering input */
@@ -492,11 +750,11 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
492 shell_context = 0; 750 shell_context = 0;
493 751
494 /* Go to the next line */ 752 /* Go to the next line */
495 xwrite(outputFd, "\n", 1); 753 goto_new_line();
496 754
497#if 0 755#if 0
498 /* Rewrite the prompt */ 756 /* Rewrite the prompt */
499 xwrite(outputFd, prompt, strlen(prompt)); 757 printf("%s", prompt);
500 758
501 /* Reset the command string */ 759 /* Reset the command string */
502 memset(command, 0, BUFSIZ); 760 memset(command, 0, BUFSIZ);
@@ -508,29 +766,28 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
508 /* Control-d -- Delete one character, or exit 766 /* Control-d -- Delete one character, or exit
509 * if the len=0 and no chars to delete */ 767 * if the len=0 and no chars to delete */
510 if (len == 0) { 768 if (len == 0) {
511 xwrite(outputFd, "exit", 4); 769 printf("exit");
512 clean_up_and_die(0); 770 clean_up_and_die(0);
513 } else { 771 } else {
514 input_delete(command, outputFd, cursor, &len); 772 input_delete();
515 } 773 }
516 break; 774 break;
517 case 5: 775 case 5:
518 /* Control-e -- End of line */ 776 /* Control-e -- End of line */
519 input_end(outputFd, &cursor, len); 777 input_end();
520 break; 778 break;
521 case 6: 779 case 6:
522 /* Control-f -- Move forward one character */ 780 /* Control-f -- Move forward one character */
523 input_forward(outputFd, &cursor, len); 781 input_forward();
524 break; 782 break;
525 case '\b': 783 case '\b':
526 case DEL: 784 case DEL:
527 /* Control-h and DEL */ 785 /* Control-h and DEL */
528 input_backspace(command, outputFd, &cursor, &len); 786 input_backspace();
529 break; 787 break;
530 case '\t': 788 case '\t':
531#ifdef BB_FEATURE_SH_TAB_COMPLETION 789#ifdef BB_FEATURE_SH_TAB_COMPLETION
532 input_tab(command, prompt, outputFd, &cursor, 790 input_tab(lastWasTab);
533&len, lastWasTab);
534#endif 791#endif
535 break; 792 break;
536 case 14: 793 case 14:
@@ -539,7 +796,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
539 get_next_history(&hp, command); 796 get_next_history(&hp, command);
540 goto rewrite_line; 797 goto rewrite_line;
541 } else { 798 } else {
542 xwrite(outputFd, "\007", 1); 799 beep();
543 } 800 }
544 break; 801 break;
545 case 16: 802 case 16:
@@ -548,7 +805,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
548 get_previous_history(&hp, command); 805 get_previous_history(&hp, command);
549 goto rewrite_line; 806 goto rewrite_line;
550 } else { 807 } else {
551 xwrite(outputFd, "\007", 1); 808 beep();
552 } 809 }
553 break; 810 break;
554 case ESC:{ 811 case ESC:{
@@ -567,7 +824,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
567 get_previous_history(&hp, command); 824 get_previous_history(&hp, command);
568 goto rewrite_line; 825 goto rewrite_line;
569 } else { 826 } else {
570 xwrite(outputFd, "\007", 1); 827 beep();
571 } 828 }
572 break; 829 break;
573 case 'B': 830 case 'B':
@@ -576,49 +833,52 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
576 get_next_history(&hp, command); 833 get_next_history(&hp, command);
577 goto rewrite_line; 834 goto rewrite_line;
578 } else { 835 } else {
579 xwrite(outputFd, "\007", 1); 836 beep();
580 } 837 }
581 break; 838 break;
582 839
583 /* Rewrite the line with the selected history item */ 840 /* Rewrite the line with the selected history item */
584 rewrite_line: 841 rewrite_line:
585 /* erase old command from command line */ 842 /* return to begin of line */
586 len = strlen(command)-strlen(hp->s); 843 input_home ();
587 844 /* for next memmoves without set '\0' */
588 while (len>cursor) 845 memset (command, 0, BUFSIZ);
589 input_delete(command, outputFd, cursor, &len); 846 /* change command */
590 while (cursor>0) 847 strcpy (command, hp->s);
591 input_backspace(command, outputFd, &cursor, &len);
592 input_home(outputFd, &cursor);
593
594 /* write new command */ 848 /* write new command */
595 strcpy(command, hp->s); 849 for (j=0; command[j]; j++)
596 len = strlen(hp->s); 850 cmdedit_set_out_char(command[j], 0);
597 xwrite(outputFd, command, len); 851 ret = cursor;
598 cursor = len; 852 /* erase tail if required */
853 for (j = ret; j < len; j++)
854 cmdedit_set_out_char(' ', 0);
855 /* and backward cursor */
856 for (j = ret; j < len; j++)
857 input_backward();
858 len = cursor; /* set new len */
599 break; 859 break;
600 case 'C': 860 case 'C':
601 /* Right Arrow -- Move forward one character */ 861 /* Right Arrow -- Move forward one character */
602 input_forward(outputFd, &cursor, len); 862 input_forward();
603 break; 863 break;
604 case 'D': 864 case 'D':
605 /* Left Arrow -- Move back one character */ 865 /* Left Arrow -- Move back one character */
606 input_backward(outputFd, &cursor); 866 input_backward();
607 break; 867 break;
608 case '3': 868 case '3':
609 /* Delete */ 869 /* Delete */
610 input_delete(command, outputFd, cursor, &len); 870 input_delete();
611 break; 871 break;
612 case '1': 872 case '1':
613 /* Home (Ctrl-A) */ 873 /* Home (Ctrl-A) */
614 input_home(outputFd, &cursor); 874 input_home();
615 break; 875 break;
616 case '4': 876 case '4':
617 /* End (Ctrl-E) */ 877 /* End (Ctrl-E) */
618 input_end(outputFd, &cursor, len); 878 input_end();
619 break; 879 break;
620 default: 880 default:
621 xwrite(outputFd, "\007", 1); 881 beep();
622 } 882 }
623 if (c == '1' || c == '3' || c == '4') 883 if (c == '1' || c == '3' || c == '4')
624 if ((ret = read(inputFd, &c, 1)) < 1) 884 if ((ret = read(inputFd, &c, 1)) < 1)
@@ -631,14 +891,14 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
631 switch (c) { 891 switch (c) {
632 case 'H': 892 case 'H':
633 /* Home (xterm) */ 893 /* Home (xterm) */
634 input_home(outputFd, &cursor); 894 input_home();
635 break; 895 break;
636 case 'F': 896 case 'F':
637 /* End (xterm) */ 897 /* End (xterm) */
638 input_end(outputFd, &cursor, len); 898 input_end();
639 break; 899 break;
640 default: 900 default:
641 xwrite(outputFd, "\007", 1); 901 beep();
642 } 902 }
643 } 903 }
644 c = 0; 904 c = 0;
@@ -658,20 +918,20 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
658 918
659 if (cursor == (len - 1)) { /* Append if at the end of the line */ 919 if (cursor == (len - 1)) { /* Append if at the end of the line */
660 *(command + cursor) = c; 920 *(command + cursor) = c;
921 cmdedit_set_out_char(c, command[cursor+1]);
661 } else { /* Insert otherwise */ 922 } else { /* Insert otherwise */
662 memmove(command + cursor + 1, command + cursor, 923 memmove(command + cursor + 1, command + cursor,
663 len - cursor - 1); 924 len - cursor - 1);
664 925
665 *(command + cursor) = c; 926 *(command + cursor) = c;
666 927 j = cursor+1;
667 for (j = cursor; j < len; j++) 928 /* rewrite from cursor */
668 xwrite(outputFd, command + j, 1); 929 input_end ();
669 for (; j > cursor; j--) 930 /* to prev x pos + 1 */
670 xwrite(outputFd, "\033[D", 3); 931 while(cursor > j)
932 input_backward();
671 } 933 }
672 934
673 cursor++;
674 xwrite(outputFd, &c, 1);
675 break; 935 break;
676 } 936 }
677 if (c == '\t') 937 if (c == '\t')
@@ -683,15 +943,18 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
683 break; 943 break;
684 } 944 }
685 945
686 nr = len + 1; 946 setTermSettings (inputFd, (void *) &initial_settings);
687 setTermSettings(inputFd, (void *) &initial_settings); 947 handlers_sets &= ~SET_RESET_TERM;
688 reset_term = 0;
689
690 948
691 /* Handle command history log */ 949 /* Handle command history log */
692 if (*(command)) { 950 if (len>1) { /* no put empty line (only '\n') */
693 951
694 struct history *h = his_end; 952 struct history *h = his_end;
953 char *ss;
954
955 command[len-1] = 0; /* destroy end '\n' */
956 ss = strdup(command); /* duplicate without '\n' */
957 command[len-1] = '\n'; /* restore '\n' */
695 958
696 if (!h) { 959 if (!h) {
697 /* No previous history -- this memory is never freed */ 960 /* No previous history -- this memory is never freed */
@@ -699,7 +962,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
699 h->n = xmalloc(sizeof(struct history)); 962 h->n = xmalloc(sizeof(struct history));
700 963
701 h->p = NULL; 964 h->p = NULL;
702 h->s = strdup(command); 965 h->s = ss;
703 h->n->p = h; 966 h->n->p = h;
704 h->n->n = NULL; 967 h->n->n = NULL;
705 h->n->s = NULL; 968 h->n->s = NULL;
@@ -712,7 +975,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
712 h->n->p = h; 975 h->n->p = h;
713 h->n->n = NULL; 976 h->n->n = NULL;
714 h->n->s = NULL; 977 h->n->s = NULL;
715 h->s = strdup(command); 978 h->s = ss;
716 his_end = h->n; 979 his_end = h->n;
717 980
718 /* After max history, remove the oldest command */ 981 /* After max history, remove the oldest command */
@@ -733,38 +996,21 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
733 return; 996 return;
734} 997}
735 998
736extern void cmdedit_init(void)
737{
738 win_changed(0);
739 signal(SIGWINCH, win_changed);
740 999
741 if(exithandler_set == 0) { 1000/* Undo the effects of cmdedit_init(). */
742 atexit(cmdedit_reset_term); /* be sure to do this only once */
743 exithandler_set = 1;
744 }
745 signal(SIGKILL, clean_up_and_die);
746 signal(SIGINT, clean_up_and_die);
747 signal(SIGQUIT, clean_up_and_die);
748 signal(SIGTERM, clean_up_and_die);
749}
750
751/*
752** Undo the effects of cmdedit_init() as good as we can:
753** I am not aware of a way to revoke an atexit() handler,
754** but, fortunately, our particular handler can be made
755** a no-op by setting reset_term = 0.
756*/
757extern void cmdedit_terminate(void) 1001extern void cmdedit_terminate(void)
758{ 1002{
759 cmdedit_reset_term(); 1003 cmdedit_reset_term();
760 reset_term = 0; 1004 if((handlers_sets & SET_TERM_HANDLERS)!=0) {
761 signal(SIGKILL, SIG_DFL); 1005 signal(SIGKILL, SIG_DFL);
762 signal(SIGINT, SIG_DFL); 1006 signal(SIGINT, SIG_DFL);
763 signal(SIGQUIT, SIG_DFL); 1007 signal(SIGQUIT, SIG_DFL);
764 signal(SIGTERM, SIG_DFL); 1008 signal(SIGTERM, SIG_DFL);
765 signal(SIGWINCH, SIG_DFL); 1009 signal(SIGWINCH, SIG_DFL);
1010 handlers_sets &= ~SET_TERM_HANDLERS;
1011 }
766} 1012}
767 1013
768 1014
769 1015
770#endif /* BB_FEATURE_SH_COMMAND_EDITING */ 1016#endif /* BB_FEATURE_SH_COMMAND_EDITING */