aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Andersen <andersen@codepoet.org>2000-04-12 17:49:52 +0000
committerErik Andersen <andersen@codepoet.org>2000-04-12 17:49:52 +0000
commitf0657d322937ad2ff865be9f89cc8c979693088e (patch)
treea82cc2f19f97a86d394b66a87af8321d91b4d9c4
parenta2685735b1ace4323f0c6fae6c31e0888ed62c34 (diff)
downloadbusybox-w32-f0657d322937ad2ff865be9f89cc8c979693088e.tar.gz
busybox-w32-f0657d322937ad2ff865be9f89cc8c979693088e.tar.bz2
busybox-w32-f0657d322937ad2ff865be9f89cc8c979693088e.zip
Some enhancements I've been working on over the weekend,
-Erik
-rw-r--r--busybox.def.h2
-rw-r--r--cmdedit.c619
-rw-r--r--cmdedit.h46
-rw-r--r--internal.h6
-rw-r--r--lash.c39
-rw-r--r--sh.c39
-rw-r--r--shell/cmdedit.c619
-rw-r--r--shell/cmdedit.h46
-rw-r--r--shell/lash.c39
9 files changed, 783 insertions, 672 deletions
diff --git a/busybox.def.h b/busybox.def.h
index eebe9b973..9a2ba3f69 100644
--- a/busybox.def.h
+++ b/busybox.def.h
@@ -187,7 +187,7 @@
187//#define BB_FEATURE_SORT_REVERSE 187//#define BB_FEATURE_SORT_REVERSE
188// 188//
189// Enable command line editing in the shell 189// Enable command line editing in the shell
190#define BB_FEATURE_SH_COMMAND_EDITING 190//#define BB_FEATURE_SH_COMMAND_EDITING
191// 191//
192// Enable tab completion in the shell (not yet working very well) 192// Enable tab completion in the shell (not yet working very well)
193//#define BB_FEATURE_SH_TAB_COMPLETION 193//#define BB_FEATURE_SH_TAB_COMPLETION
diff --git a/cmdedit.c b/cmdedit.c
index d15c69497..9800dd1c6 100644
--- a/cmdedit.c
+++ b/cmdedit.c
@@ -53,9 +53,10 @@ static struct history *his_front = NULL; /* First element in command line list *
53static struct history *his_end = NULL; /* Last element in command line list */ 53static struct history *his_end = NULL; /* Last element in command line list */
54static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */ 54static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */
55 55
56static int cmdedit_termw = 80; /* actual terminal width */
57static int cmdedit_scroll = 27; /* width of EOL scrolling region */
56static int history_counter = 0; /* Number of commands in history list */ 58static int history_counter = 0; /* Number of commands in history list */
57static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ 59static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */
58char *parsenextc; /* copy of parsefile->nextc */
59 60
60struct history { 61struct history {
61 char *s; 62 char *s;
@@ -63,85 +64,42 @@ struct history {
63 struct history *n; 64 struct history *n;
64}; 65};
65 66
67#define xwrite write
66 68
67/* Version of write which resumes after a signal is caught. */ 69void
68int xwrite(int fd, char *buf, int nbytes) 70cmdedit_setwidth(int w)
69{ 71{
70 int ntry; 72 if (w > 20) {
71 int i; 73 cmdedit_termw = w;
72 int n; 74 cmdedit_scroll = w / 3;
73 75 } else {
74 n = nbytes; 76 errorMsg("\n*** Error: minimum screen width is 21\n");
75 ntry = 0; 77 }
76 for (;;) {
77 i = write(fd, buf, n);
78 if (i > 0) {
79 if ((n -= i) <= 0)
80 return nbytes;
81 buf += i;
82 ntry = 0;
83 } else if (i == 0) {
84 if (++ntry > 10)
85 return nbytes - n;
86 } else if (errno != EINTR) {
87 return -1;
88 }
89 }
90} 78}
91 79
92
93/* Version of ioctl that retries after a signal is caught. */
94int xioctl(int fd, unsigned long request, char *arg)
95{
96 int i;
97
98 while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
99 return i;
100}
101
102
103void cmdedit_reset_term(void) 80void cmdedit_reset_term(void)
104{ 81{
105 if (reset_term) 82 if (reset_term)
106 xioctl(fileno(stdin), TCSETA, (void *) &old_term); 83 ioctl(fileno(stdin), TCSETA, (void *) &old_term);
107} 84}
108 85
109void prepareToDie(int sig) 86void clean_up_and_die(int sig)
110{ 87{
111 cmdedit_reset_term(); 88 cmdedit_reset_term();
112 fprintf(stdout, "\n"); 89 fprintf(stdout, "\n");
113 exit(TRUE); 90 exit(TRUE);
114} 91}
115 92
93/* Go to HOME position */
116void input_home(int outputFd, int *cursor) 94void input_home(int outputFd, int *cursor)
117{ /* Command line input routines */ 95{
118 while (*cursor > 0) { 96 while (*cursor > 0) {
119 xwrite(outputFd, "\b", 1); 97 xwrite(outputFd, "\b", 1);
120 --*cursor; 98 --*cursor;
121 } 99 }
122} 100}
123 101
124 102/* Go to END position */
125void input_delete(int outputFd, int cursor)
126{
127 int j = 0;
128
129 memmove(parsenextc + cursor, parsenextc + cursor + 1,
130 BUFSIZ - cursor - 1);
131 for (j = cursor; j < (BUFSIZ - 1); j++) {
132 if (!*(parsenextc + j))
133 break;
134 else
135 xwrite(outputFd, (parsenextc + j), 1);
136 }
137
138 xwrite(outputFd, " \b", 2);
139
140 while (j-- > cursor)
141 xwrite(outputFd, "\b", 1);
142}
143
144
145void input_end(int outputFd, int *cursor, int len) 103void input_end(int outputFd, int *cursor, int len)
146{ 104{
147 while (*cursor < len) { 105 while (*cursor < len) {
@@ -150,22 +108,22 @@ void input_end(int outputFd, int *cursor, int len)
150 } 108 }
151} 109}
152 110
153 111/* Delete the char in back of the cursor */
154void input_backspace(int outputFd, int *cursor, int *len) 112void input_backspace(char* command, int outputFd, int *cursor, int *len)
155{ 113{
156 int j = 0; 114 int j = 0;
157 115
158 if (*cursor > 0) { 116 if (*cursor > 0) {
159 xwrite(outputFd, "\b \b", 3); 117 xwrite(outputFd, "\b \b", 3);
160 --*cursor; 118 --*cursor;
161 memmove(parsenextc + *cursor, parsenextc + *cursor + 1, 119 memmove(command + *cursor, command + *cursor + 1,
162 BUFSIZ - *cursor + 1); 120 BUFSIZ - *cursor + 1);
163 121
164 for (j = *cursor; j < (BUFSIZ - 1); j++) { 122 for (j = *cursor; j < (BUFSIZ - 1); j++) {
165 if (!*(parsenextc + j)) 123 if (!*(command + j))
166 break; 124 break;
167 else 125 else
168 xwrite(outputFd, (parsenextc + j), 1); 126 xwrite(outputFd, (command + j), 1);
169 } 127 }
170 128
171 xwrite(outputFd, " \b", 2); 129 xwrite(outputFd, " \b", 2);
@@ -177,18 +135,61 @@ void input_backspace(int outputFd, int *cursor, int *len)
177 } 135 }
178} 136}
179 137
180#ifdef BB_FEATURE_SH_TAB_COMPLETION 138/* Delete the char in front of the cursor */
139void input_delete(char* command, int outputFd, int cursor, int *len)
140{
141 int j = 0;
181 142
182char** username_completion_matches(char* command, int *num_matches) 143 if (cursor == *len)
144 return;
145
146 memmove(command + cursor, command + cursor + 1,
147 BUFSIZ - cursor - 1);
148 for (j = cursor; j < (BUFSIZ - 1); j++) {
149 if (!*(command + j))
150 break;
151 else
152 xwrite(outputFd, (command + j), 1);
153 }
154
155 xwrite(outputFd, " \b", 2);
156
157 while (j-- > cursor)
158 xwrite(outputFd, "\b", 1);
159 --*len;
160}
161
162/* Move forward one charactor */
163void input_forward(int outputFd, int *cursor, int len)
164{
165 if (*cursor < len) {
166 xwrite(outputFd, "\033[C", 3);
167 ++*cursor;
168 }
169}
170
171/* Move back one charactor */
172void input_backward(int outputFd, int *cursor)
173{
174 if (*cursor > 0) {
175 xwrite(outputFd, "\033[D", 3);
176 --*cursor;
177 }
178}
179
180
181
182#ifdef BB_FEATURE_SH_TAB_COMPLETION
183char** username_tab_completion(char* command, int *num_matches)
183{ 184{
184 char **matches = (char **) NULL; 185 char **matches = (char **) NULL;
185 *num_matches=0; 186 *num_matches=0;
186 fprintf(stderr, "\nin username_completion_matches\n"); 187 fprintf(stderr, "\nin username_tab_completion\n");
187 return (matches); 188 return (matches);
188} 189}
189 190
190#include <dirent.h> 191#include <dirent.h>
191char** find_path_executable_n_cwd_matches(char* command, int *num_matches) 192char** exe_n_cwd_tab_completion(char* command, int *num_matches)
192{ 193{
193 char *dirName; 194 char *dirName;
194 char **matches = (char **) NULL; 195 char **matches = (char **) NULL;
@@ -227,6 +228,170 @@ char** find_path_executable_n_cwd_matches(char* command, int *num_matches)
227 228
228 return (matches); 229 return (matches);
229} 230}
231
232void input_tab(char* command, int outputFd, int *cursor, int *len)
233{
234 /* Do TAB completion */
235 static int num_matches=0;
236 static char **matches = (char **) NULL;
237 int pos = cursor;
238
239
240 if (lastWasTab == FALSE) {
241 char *tmp, *tmp1, *matchBuf;
242
243 /* For now, we will not bother with trying to distinguish
244 * whether the cursor is in/at a command extression -- we
245 * will always try all possable matches. If you don't like
246 * that then feel free to fix it.
247 */
248
249 /* Make a local copy of the string -- up
250 * to the position of the cursor */
251 matchBuf = (char *) calloc(BUFSIZ, sizeof(char));
252 strncpy(matchBuf, command, cursor);
253 tmp=matchBuf;
254
255 /* skip past any command seperator tokens */
256 while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
257 tmp=++tmp1;
258 /* skip any leading white space */
259 while (*tmp && isspace(*tmp))
260 ++tmp;
261 }
262
263 /* skip any leading white space */
264 while (*tmp && isspace(*tmp))
265 ++tmp;
266
267 /* Free up any memory already allocated */
268 if (matches) {
269 free(matches);
270 matches = (char **) NULL;
271 }
272
273 /* If the word starts with `~' and there is no slash in the word,
274 * then try completing this word as a username. */
275
276 /* FIXME -- this check is broken! */
277 if (*tmp == '~' && !strchr(tmp, '/'))
278 matches = username_tab_completion(tmp, &num_matches);
279
280 /* Try to match any executable in our path and everything
281 * in the current working directory that matches. */
282 if (!matches)
283 matches = exe_n_cwd_tab_completion(tmp, &num_matches);
284
285 /* Don't leak memory */
286 free( matchBuf);
287
288 /* Did we find exactly one match? */
289 if (matches && num_matches==1) {
290 /* write out the matched command */
291 strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos);
292 len=strlen(command);
293 cursor=len;
294 xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos);
295 break;
296 }
297 } else {
298 /* Ok -- the last char was a TAB. Since they
299 * just hit TAB again, print a list of all the
300 * available choices... */
301 if ( matches && num_matches>0 ) {
302 int i, col;
303
304 /* Go to the next line */
305 xwrite(outputFd, "\n", 1);
306 /* Print the list of matches */
307 for (i=0,col=0; i<num_matches; i++) {
308 char foo[17];
309 sprintf(foo, "%-14s ", matches[i]);
310 col += xwrite(outputFd, foo, strlen(foo));
311 if (col > 60 && matches[i+1] != NULL) {
312 xwrite(outputFd, "\n", 1);
313 col = 0;
314 }
315 }
316 /* Go to the next line */
317 xwrite(outputFd, "\n", 1);
318 /* Rewrite the prompt */
319 xwrite(outputFd, prompt, strlen(prompt));
320 /* Rewrite the command */
321 xwrite(outputFd, command, len);
322 /* Put the cursor back to where it used to be */
323 for (cursor=len; cursor > pos; cursor--)
324 xwrite(outputFd, "\b", 1);
325 }
326 }
327}
328#endif
329
330void get_previous_history(struct history **hp, char* command)
331{
332 free((*hp)->s);
333 (*hp)->s = strdup(command);
334 *hp = (*hp)->p;
335}
336
337void get_next_history(struct history **hp, char* command)
338{
339 free((*hp)->s);
340 (*hp)->s = strdup(command);
341 *hp = (*hp)->n;
342
343 cmdedit_redraw( NULL, hp->s, -2, -2);
344}
345
346#if 0
347/* prompt : if !=NULL, print the prompt
348 * command: the command line to be displayed
349 * where : where to display changes from.
350 * -1 for no change, -2 for new line
351 * cursor : desired location for the cursor.
352 * -1 for Beginning of line.
353 * -2 for End of Line,
354 */
355static void
356cmdedit_redraw(char* prompt, char* command, int where, int cursor)
357{
358 static char* last_command;
359 int cmdedit_width;
360
361 if (where == -2) {
362 /* Rewrite the prompt and clean up static variables */
363 xwrite(outputFd, "\n", 1);
364 if (prompt) {
365 strcpy(last_command, prompt);
366 xwrite(outputFd, prompt, strlen(prompt));
367 } else {
368 last_command[0] = '\0';
369 xwrite(outputFd, "# ", 2);
370 }
371 cmdedit_width = cmdedit_termw - cmdedit_strlen(prompt);
372 } else if (strcmp(command, last_command) != 0) {
373 strcpy(last_command, prompt);
374 }
375
376 /* erase old command from command line */
377 len = strlen(command)-strlen(last_command);
378 while (len>0)
379 input_backspace(command, outputFd, &cursor, &len);
380 input_home(outputFd, &cursor);
381
382 /* Rewrite the command */
383 xwrite(outputFd, command+where, len);
384
385 /* Put the where it is supposed to be */
386 for (cursor=len; cursor > where; cursor--)
387 xwrite(outputFd, "\b", 1);
388
389 /* write new command */
390 strcpy(command, hp->s);
391 len = strlen(hp->s);
392 xwrite(outputFd, command+where, len);
393 cursor = len;
394}
230#endif 395#endif
231 396
232/* 397/*
@@ -245,12 +410,12 @@ char** find_path_executable_n_cwd_matches(char* command, int *num_matches)
245 * Furthermore, the "vi" command editing keys are not implemented. 410 * Furthermore, the "vi" command editing keys are not implemented.
246 * 411 *
247 * TODO: implement TAB command completion. :) 412 * TODO: implement TAB command completion. :)
248 *
249 */ 413 */
250extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, 414extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
251 char command[BUFSIZ])
252{ 415{
253 416
417 int inputFd=fileno(stdin);
418 int outputFd=fileno(stdout);
254 int nr = 0; 419 int nr = 0;
255 int len = 0; 420 int len = 0;
256 int j = 0; 421 int j = 0;
@@ -262,297 +427,162 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
262 struct history *hp = his_end; 427 struct history *hp = his_end;
263 428
264 memset(command, 0, sizeof(command)); 429 memset(command, 0, sizeof(command));
265 parsenextc = command;
266 if (!reset_term) { 430 if (!reset_term) {
267 xioctl(inputFd, TCGETA, (void *) &old_term); 431 ioctl(inputFd, TCGETA, (void *) &old_term);
268 memcpy(&new_term, &old_term, sizeof(struct termio)); 432 memcpy(&new_term, &old_term, sizeof(struct termio));
269 433
270 new_term.c_cc[VMIN] = 1; 434 new_term.c_cc[VMIN] = 1;
271 new_term.c_cc[VTIME] = 0; 435 new_term.c_cc[VTIME] = 0;
272 new_term.c_lflag &= ~ICANON; /* unbuffered input */ 436 new_term.c_lflag &= ~ICANON; /* unbuffered input */
273 new_term.c_lflag &= ~ECHO; 437 new_term.c_lflag &= ~ECHO;
274 xioctl(inputFd, TCSETA, (void *) &new_term);
275 reset_term = 1; 438 reset_term = 1;
439 ioctl(inputFd, TCSETA, (void *) &new_term);
276 } else { 440 } else {
277 xioctl(inputFd, TCSETA, (void *) &new_term); 441 ioctl(inputFd, TCSETA, (void *) &new_term);
278 } 442 }
279 443
280 memset(parsenextc, 0, BUFSIZ); 444 memset(command, 0, BUFSIZ);
281 445
282 while (1) { 446 while (1) {
283 447
284 if ((ret = read(inputFd, &c, 1)) < 1) 448 if ((ret = read(inputFd, &c, 1)) < 1)
285 return ret; 449 return;
286
287 fprintf(stderr, "\n\nkey=%d (%c)\n\n", c, c);
288 /* Go to the next line */
289 xwrite(outputFd, "\n", 1);
290 /* Rewrite the prompt */
291 xwrite(outputFd, prompt, strlen(prompt));
292 /* Rewrite the command */
293 xwrite(outputFd, parsenextc, len);
294 450
295 switch (c) { 451 switch (c) {
452 case '\n':
453 case '\r':
454 /* Enter */
455 *(command + len++ + 1) = c;
456 xwrite(outputFd, &c, 1);
457 break_out = 1;
458 break;
296 case 1: 459 case 1:
297 /* Control-a -- Beginning of line */ 460 /* Control-a -- Beginning of line */
298 input_home(outputFd, &cursor); 461 input_home(outputFd, &cursor);
299 case 5:
300 /* Control-e -- End of line */
301 input_end(outputFd, &cursor, len);
302 break;
303 case 2: 462 case 2:
304 /* Control-b -- Move back one character */ 463 /* Control-b -- Move back one character */
305 if (cursor > 0) { 464 input_backward(outputFd, &cursor);
306 xwrite(outputFd, "\033[D", 3); 465 break;
307 cursor--; 466 case 4:
467 /* Control-d -- Delete one character, or exit
468 * if the len=0 and no chars to delete */
469 if (len == 0) {
470 xwrite(outputFd, "exit", 4);
471 clean_up_and_die(0);
472 } else {
473 input_delete(command, outputFd, cursor, &len);
308 } 474 }
309 break; 475 break;
476 case 5:
477 /* Control-e -- End of line */
478 input_end(outputFd, &cursor, len);
479 break;
310 case 6: 480 case 6:
311 /* Control-f -- Move forward one character */ 481 /* Control-f -- Move forward one character */
312 if (cursor < len) { 482 input_forward(outputFd, &cursor, len);
313 xwrite(outputFd, "\033[C", 3);
314 cursor++;
315 }
316 break; 483 break;
317 case 4: 484 case '\b':
318 /* Control-d -- Delete one character */ 485 case DEL:
319 if (cursor != len) { 486 /* control-h and DEL */
320 input_delete(outputFd, cursor); 487 input_backspace(command, outputFd, &cursor, &len);
321 len--; 488 break;
322 } else if (len == 0) { 489 case '\t':
323 prepareToDie(0); 490#ifdef BB_FEATURE_SH_TAB_COMPLETION
324 exit(0); 491 input_tab(command, outputFd, &cursor, &len);
325 } 492#endif
326 break; 493 break;
327 case 14: 494 case 14:
328 /* Control-n -- Get next command */ 495 /* Control-n -- Get next command in history */
329 if (hp && hp->n && hp->n->s) { 496 if (hp && hp->n && hp->n->s) {
330 free(hp->s); 497 get_next_history(&hp, command);
331 hp->s = strdup(parsenextc); 498 goto rewrite_line;
332 hp = hp->n; 499 } else {
333 goto hop; 500 xwrite(outputFd, "\007", 1);
334 } 501 }
335 break; 502 break;
336 case 16: 503 case 16:
337 /* Control-p -- Get previous command */ 504 /* Control-p -- Get previous command from history */
338 if (hp && hp->p) { 505 if (hp && hp->p) {
339 free(hp->s); 506 get_previous_history(&hp, command);
340 hp->s = strdup(parsenextc); 507 goto rewrite_line;
341 hp = hp->p; 508 } else {
342 goto hop; 509 xwrite(outputFd, "\007", 1);
343 }
344 break;
345 case '\t':
346#ifdef BB_FEATURE_SH_TAB_COMPLETION
347 {
348 /* Do TAB completion */
349 static int num_matches=0;
350 static char **matches = (char **) NULL;
351 int pos = cursor;
352
353
354 if (lastWasTab == FALSE) {
355 char *tmp, *tmp1, *matchBuf;
356
357 /* For now, we will not bother with trying to distinguish
358 * whether the cursor is in/at a command extression -- we
359 * will always try all possable matches. If you don't like
360 * that then feel free to fix it.
361 */
362
363 /* Make a local copy of the string -- up
364 * to the position of the cursor */
365 matchBuf = (char *) calloc(BUFSIZ, sizeof(char));
366 strncpy(matchBuf, parsenextc, cursor);
367 tmp=matchBuf;
368
369 /* skip past any command seperator tokens */
370 while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
371 tmp=++tmp1;
372 /* skip any leading white space */
373 while (*tmp && isspace(*tmp))
374 ++tmp;
375 }
376
377 /* skip any leading white space */
378 while (*tmp && isspace(*tmp))
379 ++tmp;
380
381 /* Free up any memory already allocated */
382 if (matches) {
383 free(matches);
384 matches = (char **) NULL;
385 }
386
387 /* If the word starts with `~' and there is no slash in the word,
388 * then try completing this word as a username. */
389
390 /* FIXME -- this check is broken! */
391 if (*tmp == '~' && !strchr(tmp, '/'))
392 matches = username_completion_matches(tmp, &num_matches);
393
394 /* Try to match any executable in our path and everything
395 * in the current working directory that matches. */
396 if (!matches)
397 matches = find_path_executable_n_cwd_matches(tmp, &num_matches);
398
399 /* Don't leak memory */
400 free( matchBuf);
401
402 /* Did we find exactly one match? */
403 if (matches && num_matches==1) {
404 /* write out the matched command */
405 strncpy(parsenextc+pos, matches[0]+pos, strlen(matches[0])-pos);
406 len=strlen(parsenextc);
407 cursor=len;
408 xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos);
409 break;
410 }
411 } else {
412 /* Ok -- the last char was a TAB. Since they
413 * just hit TAB again, print a list of all the
414 * available choices... */
415 if ( matches && num_matches>0 ) {
416 int i, col;
417
418 /* Go to the next line */
419 xwrite(outputFd, "\n", 1);
420 /* Print the list of matches */
421 for (i=0,col=0; i<num_matches; i++) {
422 char foo[17];
423 sprintf(foo, "%-14s ", matches[i]);
424 col += xwrite(outputFd, foo, strlen(foo));
425 if (col > 60 && matches[i+1] != NULL) {
426 xwrite(outputFd, "\n", 1);
427 col = 0;
428 }
429 }
430 /* Go to the next line */
431 xwrite(outputFd, "\n", 1);
432 /* Rewrite the prompt */
433 xwrite(outputFd, prompt, strlen(prompt));
434 /* Rewrite the command */
435 xwrite(outputFd, parsenextc, len);
436 /* Put the cursor back to where it used to be */
437 for (cursor=len; cursor > pos; cursor--)
438 xwrite(outputFd, "\b", 1);
439 }
440 }
441 break;
442 } 510 }
443#else
444 break;
445#endif
446 case '\b':
447 case DEL:
448 /* Backspace */
449 input_backspace(outputFd, &cursor, &len);
450 break;
451 case '\n':
452 /* Enter */
453 *(parsenextc + len++ + 1) = c;
454 xwrite(outputFd, &c, 1);
455 break_out = 1;
456 break; 511 break;
457 case ESC:{ 512 case ESC:{
458 /* escape sequence follows */ 513 /* escape sequence follows */
459 if ((ret = read(inputFd, &c, 1)) < 1) 514 if ((ret = read(inputFd, &c, 1)) < 1)
460 return ret; 515 return;
461 516
462 if (c == '[') { /* 91 */ 517 if (c == '[') { /* 91 */
463 if ((ret = read(inputFd, &c, 1)) < 1) 518 if ((ret = read(inputFd, &c, 1)) < 1)
464 return ret; 519 return;
465 520
466 switch (c) { 521 switch (c) {
467 case 'A': 522 case 'A':
468 /* Up Arrow -- Get previous command */ 523 /* Up Arrow -- Get previous command from history */
469 if (hp && hp->p) { 524 if (hp && hp->p) {
470 free(hp->s); 525 get_previous_history(&hp, command);
471 hp->s = strdup(parsenextc); 526 goto rewrite_line;
472 hp = hp->p; 527 } else {
473 goto hop; 528 xwrite(outputFd, "\007", 1);
474 } 529 }
475 break; 530 break;
476 case 'B': 531 case 'B':
477 /* Down Arrow -- Get next command */ 532 /* Down Arrow -- Get next command in history */
478 if (hp && hp->n && hp->n->s) { 533 if (hp && hp->n && hp->n->s) {
479 free(hp->s); 534 get_next_history(&hp, command);
480 hp->s = strdup(parsenextc); 535 goto rewrite_line;
481 hp = hp->n; 536 } else {
482 goto hop; 537 xwrite(outputFd, "\007", 1);
483 } 538 }
484 break; 539 break;
485 540
486 /* This is where we rewrite the line 541 /* Rewrite the line with the selected history item */
487 * using the selected history item */ 542 rewrite_line:
488 hop: 543 /* erase old command from command line */
489 len = strlen(parsenextc); 544 len = strlen(command)-strlen(hp->s);
490 545 while (len>0)
491 /* return to begining of line */ 546 input_backspace(command, outputFd, &cursor, &len);
492 for (; cursor > 0; cursor--) 547 input_home(outputFd, &cursor);
493 xwrite(outputFd, "\b", 1); 548
494
495 /* erase old command */
496 for (j = 0; j < len; j++)
497 xwrite(outputFd, " ", 1);
498
499 /* return to begining of line */
500 for (j = len; j > 0; j--)
501 xwrite(outputFd, "\b", 1);
502
503 memset(parsenextc, 0, BUFSIZ);
504 len = strlen(parsenextc);
505 /* write new command */ 549 /* write new command */
506 strcpy(parsenextc, hp->s); 550 strcpy(command, hp->s);
507 len = strlen(hp->s); 551 len = strlen(hp->s);
508 xwrite(outputFd, parsenextc, len); 552 xwrite(outputFd, command, len);
509 cursor = len; 553 cursor = len;
510 break; 554 break;
511 case 'C': 555 case 'C':
512 /* Right Arrow -- Move forward one character */ 556 /* Right Arrow -- Move forward one character */
513 if (cursor < len) { 557 input_forward(outputFd, &cursor, len);
514 xwrite(outputFd, "\033[C", 3);
515 cursor++;
516 }
517 break; 558 break;
518 case 'D': 559 case 'D':
519 /* Left Arrow -- Move back one character */ 560 /* Left Arrow -- Move back one character */
520 if (cursor > 0) { 561 input_backward(outputFd, &cursor);
521 xwrite(outputFd, "\033[D", 3);
522 cursor--;
523 }
524 break; 562 break;
525 case '3': 563 case '3':
526 /* Delete */ 564 /* Delete */
527 if (cursor != len) { 565 input_delete(command, outputFd, cursor, &len);
528 input_delete(outputFd, cursor);
529 len--;
530 }
531 break; 566 break;
532
533 //case '5': case '6': /* pgup/pgdown */
534
535 case '7':
536 /* rxvt home */
537 case '1': 567 case '1':
538 /* Home (Ctrl-A) */ 568 /* Home (Ctrl-A) */
539 input_home(outputFd, &cursor); 569 input_home(outputFd, &cursor);
540 break; 570 break;
541 case '8':
542 /* rxvt END */
543 case '4': 571 case '4':
544 /* End (Ctrl-E) */ 572 /* End (Ctrl-E) */
545 input_end(outputFd, &cursor, len); 573 input_end(outputFd, &cursor, len);
546 break; 574 break;
575 default:
576 xwrite(outputFd, "\007", 1);
547 } 577 }
548 if (c == '1' || c == '3' || c == '4') 578 if (c == '1' || c == '3' || c == '4')
549 if ((ret = read(inputFd, &c, 1)) < 1) 579 if ((ret = read(inputFd, &c, 1)) < 1)
550 return ret; /* read 126 (~) */ 580 return; /* read 126 (~) */
551 } 581 }
552 if (c == 'O') { 582 if (c == 'O') {
553 /* 79 */ 583 /* 79 */
554 if ((ret = read(inputFd, &c, 1)) < 1) 584 if ((ret = read(inputFd, &c, 1)) < 1)
555 return ret; 585 return;
556 switch (c) { 586 switch (c) {
557 case 'H': 587 case 'H':
558 /* Home (xterm) */ 588 /* Home (xterm) */
@@ -562,6 +592,8 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
562 /* End (xterm) */ 592 /* End (xterm) */
563 input_end(outputFd, &cursor, len); 593 input_end(outputFd, &cursor, len);
564 break; 594 break;
595 default:
596 xwrite(outputFd, "\007", 1);
565 } 597 }
566 } 598 }
567 c = 0; 599 c = 0;
@@ -570,8 +602,9 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
570 602
571 default: /* If it's regular input, do the normal thing */ 603 default: /* If it's regular input, do the normal thing */
572 604
573 if (!isprint(c)) /* Skip non-printable characters */ 605 if (!isprint(c)) { /* Skip non-printable characters */
574 break; 606 break;
607 }
575 608
576 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ 609 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
577 break; 610 break;
@@ -579,15 +612,15 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
579 len++; 612 len++;
580 613
581 if (cursor == (len - 1)) { /* Append if at the end of the line */ 614 if (cursor == (len - 1)) { /* Append if at the end of the line */
582 *(parsenextc + cursor) = c; 615 *(command + cursor) = c;
583 } else { /* Insert otherwise */ 616 } else { /* Insert otherwise */
584 memmove(parsenextc + cursor + 1, parsenextc + cursor, 617 memmove(command + cursor + 1, command + cursor,
585 len - cursor - 1); 618 len - cursor - 1);
586 619
587 *(parsenextc + cursor) = c; 620 *(command + cursor) = c;
588 621
589 for (j = cursor; j < len; j++) 622 for (j = cursor; j < len; j++)
590 xwrite(outputFd, parsenextc + j, 1); 623 xwrite(outputFd, command + j, 1);
591 for (; j > cursor; j--) 624 for (; j > cursor; j--)
592 xwrite(outputFd, "\033[D", 3); 625 xwrite(outputFd, "\033[D", 3);
593 } 626 }
@@ -606,12 +639,12 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
606 } 639 }
607 640
608 nr = len + 1; 641 nr = len + 1;
609 xioctl(inputFd, TCSETA, (void *) &old_term); 642 ioctl(inputFd, TCSETA, (void *) &old_term);
610 reset_term = 0; 643 reset_term = 0;
611 644
612 645
613 /* Handle command history log */ 646 /* Handle command history log */
614 if (*(parsenextc)) { 647 if (*(command)) {
615 648
616 struct history *h = his_end; 649 struct history *h = his_end;
617 650
@@ -621,7 +654,7 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
621 h->n = malloc(sizeof(struct history)); 654 h->n = malloc(sizeof(struct history));
622 655
623 h->p = NULL; 656 h->p = NULL;
624 h->s = strdup(parsenextc); 657 h->s = strdup(command);
625 h->n->p = h; 658 h->n->p = h;
626 h->n->n = NULL; 659 h->n->n = NULL;
627 h->n->s = NULL; 660 h->n->s = NULL;
@@ -634,7 +667,7 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
634 h->n->p = h; 667 h->n->p = h;
635 h->n->n = NULL; 668 h->n->n = NULL;
636 h->n->s = NULL; 669 h->n->s = NULL;
637 h->s = strdup(parsenextc); 670 h->s = strdup(command);
638 his_end = h->n; 671 his_end = h->n;
639 672
640 /* After max history, remove the oldest command */ 673 /* After max history, remove the oldest command */
@@ -652,14 +685,14 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
652 } 685 }
653 } 686 }
654 687
655 return nr; 688 return;
656} 689}
657 690
658extern void cmdedit_init(void) 691extern void cmdedit_init(void)
659{ 692{
660 atexit(cmdedit_reset_term); 693 atexit(cmdedit_reset_term);
661 signal(SIGINT, prepareToDie); 694 signal(SIGINT, clean_up_and_die);
662 signal(SIGQUIT, prepareToDie); 695 signal(SIGQUIT, clean_up_and_die);
663 signal(SIGTERM, prepareToDie); 696 signal(SIGTERM, clean_up_and_die);
664} 697}
665#endif /* BB_FEATURE_SH_COMMAND_EDITING */ 698#endif /* BB_FEATURE_SH_COMMAND_EDITING */
diff --git a/cmdedit.h b/cmdedit.h
index 843a74070..0e465e50e 100644
--- a/cmdedit.h
+++ b/cmdedit.h
@@ -1,17 +1,35 @@
1/* 1#ifndef GETLINE_H
2 * Termios command line History and Editting for NetBSD sh (ash) 2#define GETLINE_H
3 * Copyright (c) 1999 3
4 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu> 4/* unix systems can #define POSIX to use termios, otherwise
5 * Etc: Dave Cinege <dcinege@psychosis.com> 5 * the bsd or sysv interface will be used
6 * Adjusted for busybox: Erik Andersen <andersee@debian.org>
7 *
8 * You may use this code as you wish, so long as the original author(s)
9 * are attributed in any redistributions of the source code.
10 * This code is 'as is' with no warranty.
11 * This code may safely be consumed by a BSD or GPL license.
12 *
13 */ 6 */
14 7
15extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]); 8#ifdef __STDC__
16extern void cmdedit_init(void); 9#include <stddef.h>
10
11typedef size_t (*cmdedit_strwidth_proc)(char *);
12
13void cmdedit_read_input(char* promptStr, char* command); /* read a line of input */
14void cmdedit_setwidth(int); /* specify width of screen */
15void cmdedit_histadd(char *); /* adds entries to hist */
16void cmdedit_strwidth(cmdedit_strwidth_proc); /* to bind cmdedit_strlen */
17
18extern int (*cmdedit_in_hook)(char *);
19extern int (*cmdedit_out_hook)(char *);
20extern int (*cmdedit_tab_hook)(char *, int, int *);
21
22#else /* not __STDC__ */
23
24void cmdedit_read_input(char* promptStr, char* command);
25void cmdedit_setwidth();
26void cmdedit_histadd();
27void cmdedit_strwidth();
28
29extern int (*cmdedit_in_hook)();
30extern int (*cmdedit_out_hook)();
31extern int (*cmdedit_tab_hook)();
32
33#endif /* __STDC__ */
17 34
35#endif /* GETLINE_H */
diff --git a/internal.h b/internal.h
index 1313f3644..18159b1d3 100644
--- a/internal.h
+++ b/internal.h
@@ -220,12 +220,6 @@ extern pid_t* findPidByName( char* pidName);
220extern void *xmalloc (size_t size); 220extern void *xmalloc (size_t size);
221extern int find_real_root_device_name(char* name); 221extern int find_real_root_device_name(char* name);
222 222
223#ifdef BB_FEATURE_SH_COMMAND_EDITING
224#include <stdio.h>
225extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]);
226extern void cmdedit_init(void);
227#endif
228
229#if defined BB_INIT || defined BB_SYSLOGD 223#if defined BB_INIT || defined BB_SYSLOGD
230extern int device_open(char *device, int mode); 224extern int device_open(char *device, int mode);
231#endif 225#endif
diff --git a/lash.c b/lash.c
index f17097c64..44ffe968f 100644
--- a/lash.c
+++ b/lash.c
@@ -37,6 +37,9 @@
37#include <sys/ioctl.h> 37#include <sys/ioctl.h>
38#include <sys/wait.h> 38#include <sys/wait.h>
39#include <unistd.h> 39#include <unistd.h>
40#ifdef BB_FEATURE_SH_COMMAND_EDITING
41#include "cmdedit.h"
42#endif
40 43
41 44
42#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" 45#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
@@ -132,6 +135,16 @@ static const char shell_usage[] =
132static char cwd[1024]; 135static char cwd[1024];
133static char *prompt = "# "; 136static char *prompt = "# ";
134 137
138#ifdef BB_FEATURE_SH_COMMAND_EDITING
139void win_changed(int sig)
140{
141 struct winsize win = { 0, 0 };
142 ioctl(0, TIOCGWINSZ, &win);
143 if (win.ws_col > 0) {
144 cmdedit_setwidth( win.ws_col - 1);
145 }
146}
147#endif
135 148
136 149
137/* built-in 'cd <path>' handler */ 150/* built-in 'cd <path>' handler */
@@ -398,7 +411,7 @@ static int getCommand(FILE * source, char *command)
398 fflush(stdout); 411 fflush(stdout);
399 promptStr=(char*)malloc(sizeof(char)*(len+1)); 412 promptStr=(char*)malloc(sizeof(char)*(len+1));
400 sprintf(promptStr, "%s %s", cwd, prompt); 413 sprintf(promptStr, "%s %s", cwd, prompt);
401 cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); 414 cmdedit_read_input(promptStr, command);
402 free( promptStr); 415 free( promptStr);
403 return 0; 416 return 0;
404#else 417#else
@@ -696,7 +709,6 @@ static int parseCommand(char **commandPtr, struct job *job, int *isBg)
696 strcpy(job->text, *commandPtr); 709 strcpy(job->text, *commandPtr);
697 } else { 710 } else {
698 /* This leaves any trailing spaces, which is a bit sloppy */ 711 /* This leaves any trailing spaces, which is a bit sloppy */
699
700 count = returnCommand - *commandPtr; 712 count = returnCommand - *commandPtr;
701 job->text = malloc(count + 1); 713 job->text = malloc(count + 1);
702 strncpy(job->text, *commandPtr, count); 714 strncpy(job->text, *commandPtr, count);
@@ -793,14 +805,12 @@ static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
793 if (inBg) { 805 if (inBg) {
794 /* we don't wait for background jobs to return -- append it 806 /* we don't wait for background jobs to return -- append it
795 to the list of backgrounded jobs and leave it alone */ 807 to the list of backgrounded jobs and leave it alone */
796
797 printf("[%d] %d\n", job->jobId, 808 printf("[%d] %d\n", job->jobId,
798 newJob.progs[newJob.numProgs - 1].pid); 809 newJob.progs[newJob.numProgs - 1].pid);
799 } else { 810 } else {
800 jobList->fg = job; 811 jobList->fg = job;
801 812
802 /* move the new process group into the foreground */ 813 /* move the new process group into the foreground */
803
804 if (tcsetpgrp(0, newJob.pgrp)) 814 if (tcsetpgrp(0, newJob.pgrp))
805 perror("tcsetpgrp"); 815 perror("tcsetpgrp");
806 } 816 }
@@ -938,29 +948,24 @@ int shell_main(int argc, char **argv)
938 /* initialize the cwd */ 948 /* initialize the cwd */
939 getcwd(cwd, sizeof(cwd)); 949 getcwd(cwd, sizeof(cwd));
940 950
951#ifdef BB_FEATURE_SH_COMMAND_EDITING
952 signal(SIGWINCH, win_changed);
953 win_changed(0);
954#endif
941 955
942 //if (argv[0] && argv[0][0] == '-') { 956 //if (argv[0] && argv[0][0] == '-') {
943 // shell_source("/etc/profile"); 957 // shell_source("/etc/profile");
944 //} 958 //}
945 959
946 if (argc < 2) { 960 if (argc < 2) {
947 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, 961 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
948 BB_BT); 962 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
949 fprintf(stdout,
950 "Enter 'help' for a list of built-in commands.\n\n");
951 } else { 963 } else {
952 input = fopen(argv[1], "r"); 964 input = fopen(argv[1], "r");
953 if (!input) 965 if (!input) {
954 fatalError("A: Couldn't open file '%s': %s\n", argv[1], 966 fatalError("A: Couldn't open file '%s': %s\n", argv[1],
955 strerror(errno)); 967 strerror(errno));
956// else 968 }
957// fatalError("Got it.\n");
958 //exit(shell_source(argv[1]));
959
960 /* Set terminal IO to canonical mode, and save old term settings. */
961#ifdef BB_FEATURE_SH_COMMAND_EDITING
962 cmdedit_init();
963#endif
964 } 969 }
965 970
966 return (busy_loop(input)); 971 return (busy_loop(input));
diff --git a/sh.c b/sh.c
index f17097c64..44ffe968f 100644
--- a/sh.c
+++ b/sh.c
@@ -37,6 +37,9 @@
37#include <sys/ioctl.h> 37#include <sys/ioctl.h>
38#include <sys/wait.h> 38#include <sys/wait.h>
39#include <unistd.h> 39#include <unistd.h>
40#ifdef BB_FEATURE_SH_COMMAND_EDITING
41#include "cmdedit.h"
42#endif
40 43
41 44
42#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" 45#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
@@ -132,6 +135,16 @@ static const char shell_usage[] =
132static char cwd[1024]; 135static char cwd[1024];
133static char *prompt = "# "; 136static char *prompt = "# ";
134 137
138#ifdef BB_FEATURE_SH_COMMAND_EDITING
139void win_changed(int sig)
140{
141 struct winsize win = { 0, 0 };
142 ioctl(0, TIOCGWINSZ, &win);
143 if (win.ws_col > 0) {
144 cmdedit_setwidth( win.ws_col - 1);
145 }
146}
147#endif
135 148
136 149
137/* built-in 'cd <path>' handler */ 150/* built-in 'cd <path>' handler */
@@ -398,7 +411,7 @@ static int getCommand(FILE * source, char *command)
398 fflush(stdout); 411 fflush(stdout);
399 promptStr=(char*)malloc(sizeof(char)*(len+1)); 412 promptStr=(char*)malloc(sizeof(char)*(len+1));
400 sprintf(promptStr, "%s %s", cwd, prompt); 413 sprintf(promptStr, "%s %s", cwd, prompt);
401 cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); 414 cmdedit_read_input(promptStr, command);
402 free( promptStr); 415 free( promptStr);
403 return 0; 416 return 0;
404#else 417#else
@@ -696,7 +709,6 @@ static int parseCommand(char **commandPtr, struct job *job, int *isBg)
696 strcpy(job->text, *commandPtr); 709 strcpy(job->text, *commandPtr);
697 } else { 710 } else {
698 /* This leaves any trailing spaces, which is a bit sloppy */ 711 /* This leaves any trailing spaces, which is a bit sloppy */
699
700 count = returnCommand - *commandPtr; 712 count = returnCommand - *commandPtr;
701 job->text = malloc(count + 1); 713 job->text = malloc(count + 1);
702 strncpy(job->text, *commandPtr, count); 714 strncpy(job->text, *commandPtr, count);
@@ -793,14 +805,12 @@ static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
793 if (inBg) { 805 if (inBg) {
794 /* we don't wait for background jobs to return -- append it 806 /* we don't wait for background jobs to return -- append it
795 to the list of backgrounded jobs and leave it alone */ 807 to the list of backgrounded jobs and leave it alone */
796
797 printf("[%d] %d\n", job->jobId, 808 printf("[%d] %d\n", job->jobId,
798 newJob.progs[newJob.numProgs - 1].pid); 809 newJob.progs[newJob.numProgs - 1].pid);
799 } else { 810 } else {
800 jobList->fg = job; 811 jobList->fg = job;
801 812
802 /* move the new process group into the foreground */ 813 /* move the new process group into the foreground */
803
804 if (tcsetpgrp(0, newJob.pgrp)) 814 if (tcsetpgrp(0, newJob.pgrp))
805 perror("tcsetpgrp"); 815 perror("tcsetpgrp");
806 } 816 }
@@ -938,29 +948,24 @@ int shell_main(int argc, char **argv)
938 /* initialize the cwd */ 948 /* initialize the cwd */
939 getcwd(cwd, sizeof(cwd)); 949 getcwd(cwd, sizeof(cwd));
940 950
951#ifdef BB_FEATURE_SH_COMMAND_EDITING
952 signal(SIGWINCH, win_changed);
953 win_changed(0);
954#endif
941 955
942 //if (argv[0] && argv[0][0] == '-') { 956 //if (argv[0] && argv[0][0] == '-') {
943 // shell_source("/etc/profile"); 957 // shell_source("/etc/profile");
944 //} 958 //}
945 959
946 if (argc < 2) { 960 if (argc < 2) {
947 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, 961 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
948 BB_BT); 962 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
949 fprintf(stdout,
950 "Enter 'help' for a list of built-in commands.\n\n");
951 } else { 963 } else {
952 input = fopen(argv[1], "r"); 964 input = fopen(argv[1], "r");
953 if (!input) 965 if (!input) {
954 fatalError("A: Couldn't open file '%s': %s\n", argv[1], 966 fatalError("A: Couldn't open file '%s': %s\n", argv[1],
955 strerror(errno)); 967 strerror(errno));
956// else 968 }
957// fatalError("Got it.\n");
958 //exit(shell_source(argv[1]));
959
960 /* Set terminal IO to canonical mode, and save old term settings. */
961#ifdef BB_FEATURE_SH_COMMAND_EDITING
962 cmdedit_init();
963#endif
964 } 969 }
965 970
966 return (busy_loop(input)); 971 return (busy_loop(input));
diff --git a/shell/cmdedit.c b/shell/cmdedit.c
index d15c69497..9800dd1c6 100644
--- a/shell/cmdedit.c
+++ b/shell/cmdedit.c
@@ -53,9 +53,10 @@ static struct history *his_front = NULL; /* First element in command line list *
53static struct history *his_end = NULL; /* Last element in command line list */ 53static struct history *his_end = NULL; /* Last element in command line list */
54static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */ 54static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */
55 55
56static int cmdedit_termw = 80; /* actual terminal width */
57static int cmdedit_scroll = 27; /* width of EOL scrolling region */
56static int history_counter = 0; /* Number of commands in history list */ 58static int history_counter = 0; /* Number of commands in history list */
57static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ 59static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */
58char *parsenextc; /* copy of parsefile->nextc */
59 60
60struct history { 61struct history {
61 char *s; 62 char *s;
@@ -63,85 +64,42 @@ struct history {
63 struct history *n; 64 struct history *n;
64}; 65};
65 66
67#define xwrite write
66 68
67/* Version of write which resumes after a signal is caught. */ 69void
68int xwrite(int fd, char *buf, int nbytes) 70cmdedit_setwidth(int w)
69{ 71{
70 int ntry; 72 if (w > 20) {
71 int i; 73 cmdedit_termw = w;
72 int n; 74 cmdedit_scroll = w / 3;
73 75 } else {
74 n = nbytes; 76 errorMsg("\n*** Error: minimum screen width is 21\n");
75 ntry = 0; 77 }
76 for (;;) {
77 i = write(fd, buf, n);
78 if (i > 0) {
79 if ((n -= i) <= 0)
80 return nbytes;
81 buf += i;
82 ntry = 0;
83 } else if (i == 0) {
84 if (++ntry > 10)
85 return nbytes - n;
86 } else if (errno != EINTR) {
87 return -1;
88 }
89 }
90} 78}
91 79
92
93/* Version of ioctl that retries after a signal is caught. */
94int xioctl(int fd, unsigned long request, char *arg)
95{
96 int i;
97
98 while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
99 return i;
100}
101
102
103void cmdedit_reset_term(void) 80void cmdedit_reset_term(void)
104{ 81{
105 if (reset_term) 82 if (reset_term)
106 xioctl(fileno(stdin), TCSETA, (void *) &old_term); 83 ioctl(fileno(stdin), TCSETA, (void *) &old_term);
107} 84}
108 85
109void prepareToDie(int sig) 86void clean_up_and_die(int sig)
110{ 87{
111 cmdedit_reset_term(); 88 cmdedit_reset_term();
112 fprintf(stdout, "\n"); 89 fprintf(stdout, "\n");
113 exit(TRUE); 90 exit(TRUE);
114} 91}
115 92
93/* Go to HOME position */
116void input_home(int outputFd, int *cursor) 94void input_home(int outputFd, int *cursor)
117{ /* Command line input routines */ 95{
118 while (*cursor > 0) { 96 while (*cursor > 0) {
119 xwrite(outputFd, "\b", 1); 97 xwrite(outputFd, "\b", 1);
120 --*cursor; 98 --*cursor;
121 } 99 }
122} 100}
123 101
124 102/* Go to END position */
125void input_delete(int outputFd, int cursor)
126{
127 int j = 0;
128
129 memmove(parsenextc + cursor, parsenextc + cursor + 1,
130 BUFSIZ - cursor - 1);
131 for (j = cursor; j < (BUFSIZ - 1); j++) {
132 if (!*(parsenextc + j))
133 break;
134 else
135 xwrite(outputFd, (parsenextc + j), 1);
136 }
137
138 xwrite(outputFd, " \b", 2);
139
140 while (j-- > cursor)
141 xwrite(outputFd, "\b", 1);
142}
143
144
145void input_end(int outputFd, int *cursor, int len) 103void input_end(int outputFd, int *cursor, int len)
146{ 104{
147 while (*cursor < len) { 105 while (*cursor < len) {
@@ -150,22 +108,22 @@ void input_end(int outputFd, int *cursor, int len)
150 } 108 }
151} 109}
152 110
153 111/* Delete the char in back of the cursor */
154void input_backspace(int outputFd, int *cursor, int *len) 112void input_backspace(char* command, int outputFd, int *cursor, int *len)
155{ 113{
156 int j = 0; 114 int j = 0;
157 115
158 if (*cursor > 0) { 116 if (*cursor > 0) {
159 xwrite(outputFd, "\b \b", 3); 117 xwrite(outputFd, "\b \b", 3);
160 --*cursor; 118 --*cursor;
161 memmove(parsenextc + *cursor, parsenextc + *cursor + 1, 119 memmove(command + *cursor, command + *cursor + 1,
162 BUFSIZ - *cursor + 1); 120 BUFSIZ - *cursor + 1);
163 121
164 for (j = *cursor; j < (BUFSIZ - 1); j++) { 122 for (j = *cursor; j < (BUFSIZ - 1); j++) {
165 if (!*(parsenextc + j)) 123 if (!*(command + j))
166 break; 124 break;
167 else 125 else
168 xwrite(outputFd, (parsenextc + j), 1); 126 xwrite(outputFd, (command + j), 1);
169 } 127 }
170 128
171 xwrite(outputFd, " \b", 2); 129 xwrite(outputFd, " \b", 2);
@@ -177,18 +135,61 @@ void input_backspace(int outputFd, int *cursor, int *len)
177 } 135 }
178} 136}
179 137
180#ifdef BB_FEATURE_SH_TAB_COMPLETION 138/* Delete the char in front of the cursor */
139void input_delete(char* command, int outputFd, int cursor, int *len)
140{
141 int j = 0;
181 142
182char** username_completion_matches(char* command, int *num_matches) 143 if (cursor == *len)
144 return;
145
146 memmove(command + cursor, command + cursor + 1,
147 BUFSIZ - cursor - 1);
148 for (j = cursor; j < (BUFSIZ - 1); j++) {
149 if (!*(command + j))
150 break;
151 else
152 xwrite(outputFd, (command + j), 1);
153 }
154
155 xwrite(outputFd, " \b", 2);
156
157 while (j-- > cursor)
158 xwrite(outputFd, "\b", 1);
159 --*len;
160}
161
162/* Move forward one charactor */
163void input_forward(int outputFd, int *cursor, int len)
164{
165 if (*cursor < len) {
166 xwrite(outputFd, "\033[C", 3);
167 ++*cursor;
168 }
169}
170
171/* Move back one charactor */
172void input_backward(int outputFd, int *cursor)
173{
174 if (*cursor > 0) {
175 xwrite(outputFd, "\033[D", 3);
176 --*cursor;
177 }
178}
179
180
181
182#ifdef BB_FEATURE_SH_TAB_COMPLETION
183char** username_tab_completion(char* command, int *num_matches)
183{ 184{
184 char **matches = (char **) NULL; 185 char **matches = (char **) NULL;
185 *num_matches=0; 186 *num_matches=0;
186 fprintf(stderr, "\nin username_completion_matches\n"); 187 fprintf(stderr, "\nin username_tab_completion\n");
187 return (matches); 188 return (matches);
188} 189}
189 190
190#include <dirent.h> 191#include <dirent.h>
191char** find_path_executable_n_cwd_matches(char* command, int *num_matches) 192char** exe_n_cwd_tab_completion(char* command, int *num_matches)
192{ 193{
193 char *dirName; 194 char *dirName;
194 char **matches = (char **) NULL; 195 char **matches = (char **) NULL;
@@ -227,6 +228,170 @@ char** find_path_executable_n_cwd_matches(char* command, int *num_matches)
227 228
228 return (matches); 229 return (matches);
229} 230}
231
232void input_tab(char* command, int outputFd, int *cursor, int *len)
233{
234 /* Do TAB completion */
235 static int num_matches=0;
236 static char **matches = (char **) NULL;
237 int pos = cursor;
238
239
240 if (lastWasTab == FALSE) {
241 char *tmp, *tmp1, *matchBuf;
242
243 /* For now, we will not bother with trying to distinguish
244 * whether the cursor is in/at a command extression -- we
245 * will always try all possable matches. If you don't like
246 * that then feel free to fix it.
247 */
248
249 /* Make a local copy of the string -- up
250 * to the position of the cursor */
251 matchBuf = (char *) calloc(BUFSIZ, sizeof(char));
252 strncpy(matchBuf, command, cursor);
253 tmp=matchBuf;
254
255 /* skip past any command seperator tokens */
256 while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
257 tmp=++tmp1;
258 /* skip any leading white space */
259 while (*tmp && isspace(*tmp))
260 ++tmp;
261 }
262
263 /* skip any leading white space */
264 while (*tmp && isspace(*tmp))
265 ++tmp;
266
267 /* Free up any memory already allocated */
268 if (matches) {
269 free(matches);
270 matches = (char **) NULL;
271 }
272
273 /* If the word starts with `~' and there is no slash in the word,
274 * then try completing this word as a username. */
275
276 /* FIXME -- this check is broken! */
277 if (*tmp == '~' && !strchr(tmp, '/'))
278 matches = username_tab_completion(tmp, &num_matches);
279
280 /* Try to match any executable in our path and everything
281 * in the current working directory that matches. */
282 if (!matches)
283 matches = exe_n_cwd_tab_completion(tmp, &num_matches);
284
285 /* Don't leak memory */
286 free( matchBuf);
287
288 /* Did we find exactly one match? */
289 if (matches && num_matches==1) {
290 /* write out the matched command */
291 strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos);
292 len=strlen(command);
293 cursor=len;
294 xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos);
295 break;
296 }
297 } else {
298 /* Ok -- the last char was a TAB. Since they
299 * just hit TAB again, print a list of all the
300 * available choices... */
301 if ( matches && num_matches>0 ) {
302 int i, col;
303
304 /* Go to the next line */
305 xwrite(outputFd, "\n", 1);
306 /* Print the list of matches */
307 for (i=0,col=0; i<num_matches; i++) {
308 char foo[17];
309 sprintf(foo, "%-14s ", matches[i]);
310 col += xwrite(outputFd, foo, strlen(foo));
311 if (col > 60 && matches[i+1] != NULL) {
312 xwrite(outputFd, "\n", 1);
313 col = 0;
314 }
315 }
316 /* Go to the next line */
317 xwrite(outputFd, "\n", 1);
318 /* Rewrite the prompt */
319 xwrite(outputFd, prompt, strlen(prompt));
320 /* Rewrite the command */
321 xwrite(outputFd, command, len);
322 /* Put the cursor back to where it used to be */
323 for (cursor=len; cursor > pos; cursor--)
324 xwrite(outputFd, "\b", 1);
325 }
326 }
327}
328#endif
329
330void get_previous_history(struct history **hp, char* command)
331{
332 free((*hp)->s);
333 (*hp)->s = strdup(command);
334 *hp = (*hp)->p;
335}
336
337void get_next_history(struct history **hp, char* command)
338{
339 free((*hp)->s);
340 (*hp)->s = strdup(command);
341 *hp = (*hp)->n;
342
343 cmdedit_redraw( NULL, hp->s, -2, -2);
344}
345
346#if 0
347/* prompt : if !=NULL, print the prompt
348 * command: the command line to be displayed
349 * where : where to display changes from.
350 * -1 for no change, -2 for new line
351 * cursor : desired location for the cursor.
352 * -1 for Beginning of line.
353 * -2 for End of Line,
354 */
355static void
356cmdedit_redraw(char* prompt, char* command, int where, int cursor)
357{
358 static char* last_command;
359 int cmdedit_width;
360
361 if (where == -2) {
362 /* Rewrite the prompt and clean up static variables */
363 xwrite(outputFd, "\n", 1);
364 if (prompt) {
365 strcpy(last_command, prompt);
366 xwrite(outputFd, prompt, strlen(prompt));
367 } else {
368 last_command[0] = '\0';
369 xwrite(outputFd, "# ", 2);
370 }
371 cmdedit_width = cmdedit_termw - cmdedit_strlen(prompt);
372 } else if (strcmp(command, last_command) != 0) {
373 strcpy(last_command, prompt);
374 }
375
376 /* erase old command from command line */
377 len = strlen(command)-strlen(last_command);
378 while (len>0)
379 input_backspace(command, outputFd, &cursor, &len);
380 input_home(outputFd, &cursor);
381
382 /* Rewrite the command */
383 xwrite(outputFd, command+where, len);
384
385 /* Put the where it is supposed to be */
386 for (cursor=len; cursor > where; cursor--)
387 xwrite(outputFd, "\b", 1);
388
389 /* write new command */
390 strcpy(command, hp->s);
391 len = strlen(hp->s);
392 xwrite(outputFd, command+where, len);
393 cursor = len;
394}
230#endif 395#endif
231 396
232/* 397/*
@@ -245,12 +410,12 @@ char** find_path_executable_n_cwd_matches(char* command, int *num_matches)
245 * Furthermore, the "vi" command editing keys are not implemented. 410 * Furthermore, the "vi" command editing keys are not implemented.
246 * 411 *
247 * TODO: implement TAB command completion. :) 412 * TODO: implement TAB command completion. :)
248 *
249 */ 413 */
250extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, 414extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
251 char command[BUFSIZ])
252{ 415{
253 416
417 int inputFd=fileno(stdin);
418 int outputFd=fileno(stdout);
254 int nr = 0; 419 int nr = 0;
255 int len = 0; 420 int len = 0;
256 int j = 0; 421 int j = 0;
@@ -262,297 +427,162 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
262 struct history *hp = his_end; 427 struct history *hp = his_end;
263 428
264 memset(command, 0, sizeof(command)); 429 memset(command, 0, sizeof(command));
265 parsenextc = command;
266 if (!reset_term) { 430 if (!reset_term) {
267 xioctl(inputFd, TCGETA, (void *) &old_term); 431 ioctl(inputFd, TCGETA, (void *) &old_term);
268 memcpy(&new_term, &old_term, sizeof(struct termio)); 432 memcpy(&new_term, &old_term, sizeof(struct termio));
269 433
270 new_term.c_cc[VMIN] = 1; 434 new_term.c_cc[VMIN] = 1;
271 new_term.c_cc[VTIME] = 0; 435 new_term.c_cc[VTIME] = 0;
272 new_term.c_lflag &= ~ICANON; /* unbuffered input */ 436 new_term.c_lflag &= ~ICANON; /* unbuffered input */
273 new_term.c_lflag &= ~ECHO; 437 new_term.c_lflag &= ~ECHO;
274 xioctl(inputFd, TCSETA, (void *) &new_term);
275 reset_term = 1; 438 reset_term = 1;
439 ioctl(inputFd, TCSETA, (void *) &new_term);
276 } else { 440 } else {
277 xioctl(inputFd, TCSETA, (void *) &new_term); 441 ioctl(inputFd, TCSETA, (void *) &new_term);
278 } 442 }
279 443
280 memset(parsenextc, 0, BUFSIZ); 444 memset(command, 0, BUFSIZ);
281 445
282 while (1) { 446 while (1) {
283 447
284 if ((ret = read(inputFd, &c, 1)) < 1) 448 if ((ret = read(inputFd, &c, 1)) < 1)
285 return ret; 449 return;
286
287 fprintf(stderr, "\n\nkey=%d (%c)\n\n", c, c);
288 /* Go to the next line */
289 xwrite(outputFd, "\n", 1);
290 /* Rewrite the prompt */
291 xwrite(outputFd, prompt, strlen(prompt));
292 /* Rewrite the command */
293 xwrite(outputFd, parsenextc, len);
294 450
295 switch (c) { 451 switch (c) {
452 case '\n':
453 case '\r':
454 /* Enter */
455 *(command + len++ + 1) = c;
456 xwrite(outputFd, &c, 1);
457 break_out = 1;
458 break;
296 case 1: 459 case 1:
297 /* Control-a -- Beginning of line */ 460 /* Control-a -- Beginning of line */
298 input_home(outputFd, &cursor); 461 input_home(outputFd, &cursor);
299 case 5:
300 /* Control-e -- End of line */
301 input_end(outputFd, &cursor, len);
302 break;
303 case 2: 462 case 2:
304 /* Control-b -- Move back one character */ 463 /* Control-b -- Move back one character */
305 if (cursor > 0) { 464 input_backward(outputFd, &cursor);
306 xwrite(outputFd, "\033[D", 3); 465 break;
307 cursor--; 466 case 4:
467 /* Control-d -- Delete one character, or exit
468 * if the len=0 and no chars to delete */
469 if (len == 0) {
470 xwrite(outputFd, "exit", 4);
471 clean_up_and_die(0);
472 } else {
473 input_delete(command, outputFd, cursor, &len);
308 } 474 }
309 break; 475 break;
476 case 5:
477 /* Control-e -- End of line */
478 input_end(outputFd, &cursor, len);
479 break;
310 case 6: 480 case 6:
311 /* Control-f -- Move forward one character */ 481 /* Control-f -- Move forward one character */
312 if (cursor < len) { 482 input_forward(outputFd, &cursor, len);
313 xwrite(outputFd, "\033[C", 3);
314 cursor++;
315 }
316 break; 483 break;
317 case 4: 484 case '\b':
318 /* Control-d -- Delete one character */ 485 case DEL:
319 if (cursor != len) { 486 /* control-h and DEL */
320 input_delete(outputFd, cursor); 487 input_backspace(command, outputFd, &cursor, &len);
321 len--; 488 break;
322 } else if (len == 0) { 489 case '\t':
323 prepareToDie(0); 490#ifdef BB_FEATURE_SH_TAB_COMPLETION
324 exit(0); 491 input_tab(command, outputFd, &cursor, &len);
325 } 492#endif
326 break; 493 break;
327 case 14: 494 case 14:
328 /* Control-n -- Get next command */ 495 /* Control-n -- Get next command in history */
329 if (hp && hp->n && hp->n->s) { 496 if (hp && hp->n && hp->n->s) {
330 free(hp->s); 497 get_next_history(&hp, command);
331 hp->s = strdup(parsenextc); 498 goto rewrite_line;
332 hp = hp->n; 499 } else {
333 goto hop; 500 xwrite(outputFd, "\007", 1);
334 } 501 }
335 break; 502 break;
336 case 16: 503 case 16:
337 /* Control-p -- Get previous command */ 504 /* Control-p -- Get previous command from history */
338 if (hp && hp->p) { 505 if (hp && hp->p) {
339 free(hp->s); 506 get_previous_history(&hp, command);
340 hp->s = strdup(parsenextc); 507 goto rewrite_line;
341 hp = hp->p; 508 } else {
342 goto hop; 509 xwrite(outputFd, "\007", 1);
343 }
344 break;
345 case '\t':
346#ifdef BB_FEATURE_SH_TAB_COMPLETION
347 {
348 /* Do TAB completion */
349 static int num_matches=0;
350 static char **matches = (char **) NULL;
351 int pos = cursor;
352
353
354 if (lastWasTab == FALSE) {
355 char *tmp, *tmp1, *matchBuf;
356
357 /* For now, we will not bother with trying to distinguish
358 * whether the cursor is in/at a command extression -- we
359 * will always try all possable matches. If you don't like
360 * that then feel free to fix it.
361 */
362
363 /* Make a local copy of the string -- up
364 * to the position of the cursor */
365 matchBuf = (char *) calloc(BUFSIZ, sizeof(char));
366 strncpy(matchBuf, parsenextc, cursor);
367 tmp=matchBuf;
368
369 /* skip past any command seperator tokens */
370 while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
371 tmp=++tmp1;
372 /* skip any leading white space */
373 while (*tmp && isspace(*tmp))
374 ++tmp;
375 }
376
377 /* skip any leading white space */
378 while (*tmp && isspace(*tmp))
379 ++tmp;
380
381 /* Free up any memory already allocated */
382 if (matches) {
383 free(matches);
384 matches = (char **) NULL;
385 }
386
387 /* If the word starts with `~' and there is no slash in the word,
388 * then try completing this word as a username. */
389
390 /* FIXME -- this check is broken! */
391 if (*tmp == '~' && !strchr(tmp, '/'))
392 matches = username_completion_matches(tmp, &num_matches);
393
394 /* Try to match any executable in our path and everything
395 * in the current working directory that matches. */
396 if (!matches)
397 matches = find_path_executable_n_cwd_matches(tmp, &num_matches);
398
399 /* Don't leak memory */
400 free( matchBuf);
401
402 /* Did we find exactly one match? */
403 if (matches && num_matches==1) {
404 /* write out the matched command */
405 strncpy(parsenextc+pos, matches[0]+pos, strlen(matches[0])-pos);
406 len=strlen(parsenextc);
407 cursor=len;
408 xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos);
409 break;
410 }
411 } else {
412 /* Ok -- the last char was a TAB. Since they
413 * just hit TAB again, print a list of all the
414 * available choices... */
415 if ( matches && num_matches>0 ) {
416 int i, col;
417
418 /* Go to the next line */
419 xwrite(outputFd, "\n", 1);
420 /* Print the list of matches */
421 for (i=0,col=0; i<num_matches; i++) {
422 char foo[17];
423 sprintf(foo, "%-14s ", matches[i]);
424 col += xwrite(outputFd, foo, strlen(foo));
425 if (col > 60 && matches[i+1] != NULL) {
426 xwrite(outputFd, "\n", 1);
427 col = 0;
428 }
429 }
430 /* Go to the next line */
431 xwrite(outputFd, "\n", 1);
432 /* Rewrite the prompt */
433 xwrite(outputFd, prompt, strlen(prompt));
434 /* Rewrite the command */
435 xwrite(outputFd, parsenextc, len);
436 /* Put the cursor back to where it used to be */
437 for (cursor=len; cursor > pos; cursor--)
438 xwrite(outputFd, "\b", 1);
439 }
440 }
441 break;
442 } 510 }
443#else
444 break;
445#endif
446 case '\b':
447 case DEL:
448 /* Backspace */
449 input_backspace(outputFd, &cursor, &len);
450 break;
451 case '\n':
452 /* Enter */
453 *(parsenextc + len++ + 1) = c;
454 xwrite(outputFd, &c, 1);
455 break_out = 1;
456 break; 511 break;
457 case ESC:{ 512 case ESC:{
458 /* escape sequence follows */ 513 /* escape sequence follows */
459 if ((ret = read(inputFd, &c, 1)) < 1) 514 if ((ret = read(inputFd, &c, 1)) < 1)
460 return ret; 515 return;
461 516
462 if (c == '[') { /* 91 */ 517 if (c == '[') { /* 91 */
463 if ((ret = read(inputFd, &c, 1)) < 1) 518 if ((ret = read(inputFd, &c, 1)) < 1)
464 return ret; 519 return;
465 520
466 switch (c) { 521 switch (c) {
467 case 'A': 522 case 'A':
468 /* Up Arrow -- Get previous command */ 523 /* Up Arrow -- Get previous command from history */
469 if (hp && hp->p) { 524 if (hp && hp->p) {
470 free(hp->s); 525 get_previous_history(&hp, command);
471 hp->s = strdup(parsenextc); 526 goto rewrite_line;
472 hp = hp->p; 527 } else {
473 goto hop; 528 xwrite(outputFd, "\007", 1);
474 } 529 }
475 break; 530 break;
476 case 'B': 531 case 'B':
477 /* Down Arrow -- Get next command */ 532 /* Down Arrow -- Get next command in history */
478 if (hp && hp->n && hp->n->s) { 533 if (hp && hp->n && hp->n->s) {
479 free(hp->s); 534 get_next_history(&hp, command);
480 hp->s = strdup(parsenextc); 535 goto rewrite_line;
481 hp = hp->n; 536 } else {
482 goto hop; 537 xwrite(outputFd, "\007", 1);
483 } 538 }
484 break; 539 break;
485 540
486 /* This is where we rewrite the line 541 /* Rewrite the line with the selected history item */
487 * using the selected history item */ 542 rewrite_line:
488 hop: 543 /* erase old command from command line */
489 len = strlen(parsenextc); 544 len = strlen(command)-strlen(hp->s);
490 545 while (len>0)
491 /* return to begining of line */ 546 input_backspace(command, outputFd, &cursor, &len);
492 for (; cursor > 0; cursor--) 547 input_home(outputFd, &cursor);
493 xwrite(outputFd, "\b", 1); 548
494
495 /* erase old command */
496 for (j = 0; j < len; j++)
497 xwrite(outputFd, " ", 1);
498
499 /* return to begining of line */
500 for (j = len; j > 0; j--)
501 xwrite(outputFd, "\b", 1);
502
503 memset(parsenextc, 0, BUFSIZ);
504 len = strlen(parsenextc);
505 /* write new command */ 549 /* write new command */
506 strcpy(parsenextc, hp->s); 550 strcpy(command, hp->s);
507 len = strlen(hp->s); 551 len = strlen(hp->s);
508 xwrite(outputFd, parsenextc, len); 552 xwrite(outputFd, command, len);
509 cursor = len; 553 cursor = len;
510 break; 554 break;
511 case 'C': 555 case 'C':
512 /* Right Arrow -- Move forward one character */ 556 /* Right Arrow -- Move forward one character */
513 if (cursor < len) { 557 input_forward(outputFd, &cursor, len);
514 xwrite(outputFd, "\033[C", 3);
515 cursor++;
516 }
517 break; 558 break;
518 case 'D': 559 case 'D':
519 /* Left Arrow -- Move back one character */ 560 /* Left Arrow -- Move back one character */
520 if (cursor > 0) { 561 input_backward(outputFd, &cursor);
521 xwrite(outputFd, "\033[D", 3);
522 cursor--;
523 }
524 break; 562 break;
525 case '3': 563 case '3':
526 /* Delete */ 564 /* Delete */
527 if (cursor != len) { 565 input_delete(command, outputFd, cursor, &len);
528 input_delete(outputFd, cursor);
529 len--;
530 }
531 break; 566 break;
532
533 //case '5': case '6': /* pgup/pgdown */
534
535 case '7':
536 /* rxvt home */
537 case '1': 567 case '1':
538 /* Home (Ctrl-A) */ 568 /* Home (Ctrl-A) */
539 input_home(outputFd, &cursor); 569 input_home(outputFd, &cursor);
540 break; 570 break;
541 case '8':
542 /* rxvt END */
543 case '4': 571 case '4':
544 /* End (Ctrl-E) */ 572 /* End (Ctrl-E) */
545 input_end(outputFd, &cursor, len); 573 input_end(outputFd, &cursor, len);
546 break; 574 break;
575 default:
576 xwrite(outputFd, "\007", 1);
547 } 577 }
548 if (c == '1' || c == '3' || c == '4') 578 if (c == '1' || c == '3' || c == '4')
549 if ((ret = read(inputFd, &c, 1)) < 1) 579 if ((ret = read(inputFd, &c, 1)) < 1)
550 return ret; /* read 126 (~) */ 580 return; /* read 126 (~) */
551 } 581 }
552 if (c == 'O') { 582 if (c == 'O') {
553 /* 79 */ 583 /* 79 */
554 if ((ret = read(inputFd, &c, 1)) < 1) 584 if ((ret = read(inputFd, &c, 1)) < 1)
555 return ret; 585 return;
556 switch (c) { 586 switch (c) {
557 case 'H': 587 case 'H':
558 /* Home (xterm) */ 588 /* Home (xterm) */
@@ -562,6 +592,8 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
562 /* End (xterm) */ 592 /* End (xterm) */
563 input_end(outputFd, &cursor, len); 593 input_end(outputFd, &cursor, len);
564 break; 594 break;
595 default:
596 xwrite(outputFd, "\007", 1);
565 } 597 }
566 } 598 }
567 c = 0; 599 c = 0;
@@ -570,8 +602,9 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
570 602
571 default: /* If it's regular input, do the normal thing */ 603 default: /* If it's regular input, do the normal thing */
572 604
573 if (!isprint(c)) /* Skip non-printable characters */ 605 if (!isprint(c)) { /* Skip non-printable characters */
574 break; 606 break;
607 }
575 608
576 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ 609 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
577 break; 610 break;
@@ -579,15 +612,15 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
579 len++; 612 len++;
580 613
581 if (cursor == (len - 1)) { /* Append if at the end of the line */ 614 if (cursor == (len - 1)) { /* Append if at the end of the line */
582 *(parsenextc + cursor) = c; 615 *(command + cursor) = c;
583 } else { /* Insert otherwise */ 616 } else { /* Insert otherwise */
584 memmove(parsenextc + cursor + 1, parsenextc + cursor, 617 memmove(command + cursor + 1, command + cursor,
585 len - cursor - 1); 618 len - cursor - 1);
586 619
587 *(parsenextc + cursor) = c; 620 *(command + cursor) = c;
588 621
589 for (j = cursor; j < len; j++) 622 for (j = cursor; j < len; j++)
590 xwrite(outputFd, parsenextc + j, 1); 623 xwrite(outputFd, command + j, 1);
591 for (; j > cursor; j--) 624 for (; j > cursor; j--)
592 xwrite(outputFd, "\033[D", 3); 625 xwrite(outputFd, "\033[D", 3);
593 } 626 }
@@ -606,12 +639,12 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
606 } 639 }
607 640
608 nr = len + 1; 641 nr = len + 1;
609 xioctl(inputFd, TCSETA, (void *) &old_term); 642 ioctl(inputFd, TCSETA, (void *) &old_term);
610 reset_term = 0; 643 reset_term = 0;
611 644
612 645
613 /* Handle command history log */ 646 /* Handle command history log */
614 if (*(parsenextc)) { 647 if (*(command)) {
615 648
616 struct history *h = his_end; 649 struct history *h = his_end;
617 650
@@ -621,7 +654,7 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
621 h->n = malloc(sizeof(struct history)); 654 h->n = malloc(sizeof(struct history));
622 655
623 h->p = NULL; 656 h->p = NULL;
624 h->s = strdup(parsenextc); 657 h->s = strdup(command);
625 h->n->p = h; 658 h->n->p = h;
626 h->n->n = NULL; 659 h->n->n = NULL;
627 h->n->s = NULL; 660 h->n->s = NULL;
@@ -634,7 +667,7 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
634 h->n->p = h; 667 h->n->p = h;
635 h->n->n = NULL; 668 h->n->n = NULL;
636 h->n->s = NULL; 669 h->n->s = NULL;
637 h->s = strdup(parsenextc); 670 h->s = strdup(command);
638 his_end = h->n; 671 his_end = h->n;
639 672
640 /* After max history, remove the oldest command */ 673 /* After max history, remove the oldest command */
@@ -652,14 +685,14 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
652 } 685 }
653 } 686 }
654 687
655 return nr; 688 return;
656} 689}
657 690
658extern void cmdedit_init(void) 691extern void cmdedit_init(void)
659{ 692{
660 atexit(cmdedit_reset_term); 693 atexit(cmdedit_reset_term);
661 signal(SIGINT, prepareToDie); 694 signal(SIGINT, clean_up_and_die);
662 signal(SIGQUIT, prepareToDie); 695 signal(SIGQUIT, clean_up_and_die);
663 signal(SIGTERM, prepareToDie); 696 signal(SIGTERM, clean_up_and_die);
664} 697}
665#endif /* BB_FEATURE_SH_COMMAND_EDITING */ 698#endif /* BB_FEATURE_SH_COMMAND_EDITING */
diff --git a/shell/cmdedit.h b/shell/cmdedit.h
index 843a74070..0e465e50e 100644
--- a/shell/cmdedit.h
+++ b/shell/cmdedit.h
@@ -1,17 +1,35 @@
1/* 1#ifndef GETLINE_H
2 * Termios command line History and Editting for NetBSD sh (ash) 2#define GETLINE_H
3 * Copyright (c) 1999 3
4 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu> 4/* unix systems can #define POSIX to use termios, otherwise
5 * Etc: Dave Cinege <dcinege@psychosis.com> 5 * the bsd or sysv interface will be used
6 * Adjusted for busybox: Erik Andersen <andersee@debian.org>
7 *
8 * You may use this code as you wish, so long as the original author(s)
9 * are attributed in any redistributions of the source code.
10 * This code is 'as is' with no warranty.
11 * This code may safely be consumed by a BSD or GPL license.
12 *
13 */ 6 */
14 7
15extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]); 8#ifdef __STDC__
16extern void cmdedit_init(void); 9#include <stddef.h>
10
11typedef size_t (*cmdedit_strwidth_proc)(char *);
12
13void cmdedit_read_input(char* promptStr, char* command); /* read a line of input */
14void cmdedit_setwidth(int); /* specify width of screen */
15void cmdedit_histadd(char *); /* adds entries to hist */
16void cmdedit_strwidth(cmdedit_strwidth_proc); /* to bind cmdedit_strlen */
17
18extern int (*cmdedit_in_hook)(char *);
19extern int (*cmdedit_out_hook)(char *);
20extern int (*cmdedit_tab_hook)(char *, int, int *);
21
22#else /* not __STDC__ */
23
24void cmdedit_read_input(char* promptStr, char* command);
25void cmdedit_setwidth();
26void cmdedit_histadd();
27void cmdedit_strwidth();
28
29extern int (*cmdedit_in_hook)();
30extern int (*cmdedit_out_hook)();
31extern int (*cmdedit_tab_hook)();
32
33#endif /* __STDC__ */
17 34
35#endif /* GETLINE_H */
diff --git a/shell/lash.c b/shell/lash.c
index f17097c64..44ffe968f 100644
--- a/shell/lash.c
+++ b/shell/lash.c
@@ -37,6 +37,9 @@
37#include <sys/ioctl.h> 37#include <sys/ioctl.h>
38#include <sys/wait.h> 38#include <sys/wait.h>
39#include <unistd.h> 39#include <unistd.h>
40#ifdef BB_FEATURE_SH_COMMAND_EDITING
41#include "cmdedit.h"
42#endif
40 43
41 44
42#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" 45#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
@@ -132,6 +135,16 @@ static const char shell_usage[] =
132static char cwd[1024]; 135static char cwd[1024];
133static char *prompt = "# "; 136static char *prompt = "# ";
134 137
138#ifdef BB_FEATURE_SH_COMMAND_EDITING
139void win_changed(int sig)
140{
141 struct winsize win = { 0, 0 };
142 ioctl(0, TIOCGWINSZ, &win);
143 if (win.ws_col > 0) {
144 cmdedit_setwidth( win.ws_col - 1);
145 }
146}
147#endif
135 148
136 149
137/* built-in 'cd <path>' handler */ 150/* built-in 'cd <path>' handler */
@@ -398,7 +411,7 @@ static int getCommand(FILE * source, char *command)
398 fflush(stdout); 411 fflush(stdout);
399 promptStr=(char*)malloc(sizeof(char)*(len+1)); 412 promptStr=(char*)malloc(sizeof(char)*(len+1));
400 sprintf(promptStr, "%s %s", cwd, prompt); 413 sprintf(promptStr, "%s %s", cwd, prompt);
401 cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); 414 cmdedit_read_input(promptStr, command);
402 free( promptStr); 415 free( promptStr);
403 return 0; 416 return 0;
404#else 417#else
@@ -696,7 +709,6 @@ static int parseCommand(char **commandPtr, struct job *job, int *isBg)
696 strcpy(job->text, *commandPtr); 709 strcpy(job->text, *commandPtr);
697 } else { 710 } else {
698 /* This leaves any trailing spaces, which is a bit sloppy */ 711 /* This leaves any trailing spaces, which is a bit sloppy */
699
700 count = returnCommand - *commandPtr; 712 count = returnCommand - *commandPtr;
701 job->text = malloc(count + 1); 713 job->text = malloc(count + 1);
702 strncpy(job->text, *commandPtr, count); 714 strncpy(job->text, *commandPtr, count);
@@ -793,14 +805,12 @@ static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
793 if (inBg) { 805 if (inBg) {
794 /* we don't wait for background jobs to return -- append it 806 /* we don't wait for background jobs to return -- append it
795 to the list of backgrounded jobs and leave it alone */ 807 to the list of backgrounded jobs and leave it alone */
796
797 printf("[%d] %d\n", job->jobId, 808 printf("[%d] %d\n", job->jobId,
798 newJob.progs[newJob.numProgs - 1].pid); 809 newJob.progs[newJob.numProgs - 1].pid);
799 } else { 810 } else {
800 jobList->fg = job; 811 jobList->fg = job;
801 812
802 /* move the new process group into the foreground */ 813 /* move the new process group into the foreground */
803
804 if (tcsetpgrp(0, newJob.pgrp)) 814 if (tcsetpgrp(0, newJob.pgrp))
805 perror("tcsetpgrp"); 815 perror("tcsetpgrp");
806 } 816 }
@@ -938,29 +948,24 @@ int shell_main(int argc, char **argv)
938 /* initialize the cwd */ 948 /* initialize the cwd */
939 getcwd(cwd, sizeof(cwd)); 949 getcwd(cwd, sizeof(cwd));
940 950
951#ifdef BB_FEATURE_SH_COMMAND_EDITING
952 signal(SIGWINCH, win_changed);
953 win_changed(0);
954#endif
941 955
942 //if (argv[0] && argv[0][0] == '-') { 956 //if (argv[0] && argv[0][0] == '-') {
943 // shell_source("/etc/profile"); 957 // shell_source("/etc/profile");
944 //} 958 //}
945 959
946 if (argc < 2) { 960 if (argc < 2) {
947 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, 961 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
948 BB_BT); 962 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
949 fprintf(stdout,
950 "Enter 'help' for a list of built-in commands.\n\n");
951 } else { 963 } else {
952 input = fopen(argv[1], "r"); 964 input = fopen(argv[1], "r");
953 if (!input) 965 if (!input) {
954 fatalError("A: Couldn't open file '%s': %s\n", argv[1], 966 fatalError("A: Couldn't open file '%s': %s\n", argv[1],
955 strerror(errno)); 967 strerror(errno));
956// else 968 }
957// fatalError("Got it.\n");
958 //exit(shell_source(argv[1]));
959
960 /* Set terminal IO to canonical mode, and save old term settings. */
961#ifdef BB_FEATURE_SH_COMMAND_EDITING
962 cmdedit_init();
963#endif
964 } 969 }
965 970
966 return (busy_loop(input)); 971 return (busy_loop(input));