blob: b3e7fd58c9f1a227dd7f2b923aafc8711c85e6da [file] [log] [blame]
Erik Andersenc7c634b2000-03-19 05:28:55 +00001/* vi: set sw=4 ts=4: */
Erik Andersen13456d12000-03-16 08:09:57 +00002/*
3 * Termios command line History and Editting for NetBSD sh (ash)
4 * Copyright (c) 1999
5 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu>
6 * Etc: Dave Cinege <dcinege@psychosis.com>
7 * Adjusted for busybox: Erik Andersen <andersee@debian.org>
8 *
9 * You may use this code as you wish, so long as the original author(s)
10 * are attributed in any redistributions of the source code.
11 * This code is 'as is' with no warranty.
12 * This code may safely be consumed by a BSD or GPL license.
13 *
14 * v 0.5 19990328 Initial release
15 *
16 * Future plans: Simple file and path name completion. (like BASH)
17 *
18 */
19
20/*
21 Usage and Known bugs:
22 Terminal key codes are not extensive, and more will probably
23 need to be added. This version was created on Debian GNU/Linux 2.x.
24 Delete, Backspace, Home, End, and the arrow keys were tested
25 to work in an Xterm and console. Ctrl-A also works as Home.
26 Ctrl-E also works as End. The binary size increase is <3K.
27
28 Editting will not display correctly for lines greater then the
29 terminal width. (more then one line.) However, history will.
30 */
31
32#include "internal.h"
33#ifdef BB_FEATURE_SH_COMMAND_EDITING
34
35#include <stdio.h>
36#include <errno.h>
37#include <unistd.h>
38#include <stdlib.h>
39#include <string.h>
40#include <termio.h>
41#include <ctype.h>
42#include <signal.h>
43
Erik Andersen13456d12000-03-16 08:09:57 +000044
Erik Andersenc7c634b2000-03-19 05:28:55 +000045#define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */
Erik Andersen13456d12000-03-16 08:09:57 +000046
47#define ESC 27
48#define DEL 127
Erik Andersen6273f652000-03-17 01:12:41 +000049#define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0)
50#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
Erik Andersen13456d12000-03-16 08:09:57 +000051
52static struct history *his_front = NULL; /* First 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 */
55
56static int history_counter = 0; /* Number of commands in history list */
Erik Andersenc7c634b2000-03-19 05:28:55 +000057static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */
58char *parsenextc; /* copy of parsefile->nextc */
Erik Andersen13456d12000-03-16 08:09:57 +000059
60struct history {
Erik Andersenc7c634b2000-03-19 05:28:55 +000061 char *s;
62 struct history *p;
63 struct history *n;
Erik Andersen13456d12000-03-16 08:09:57 +000064};
65
66
67/* Version of write which resumes after a signal is caught. */
68int xwrite(int fd, char *buf, int nbytes)
69{
Erik Andersenc7c634b2000-03-19 05:28:55 +000070 int ntry;
71 int i;
72 int n;
Erik Andersen13456d12000-03-16 08:09:57 +000073
Erik Andersenc7c634b2000-03-19 05:28:55 +000074 n = nbytes;
75 ntry = 0;
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 }
Erik Andersen13456d12000-03-16 08:09:57 +000089 }
Erik Andersen13456d12000-03-16 08:09:57 +000090}
91
92
93/* Version of ioctl that retries after a signal is caught. */
94int xioctl(int fd, unsigned long request, char *arg)
95{
Erik Andersenc7c634b2000-03-19 05:28:55 +000096 int i;
97
98 while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
99 return i;
Erik Andersen13456d12000-03-16 08:09:57 +0000100}
101
102
103void cmdedit_reset_term(void)
104{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000105 if (reset_term)
106 xioctl(fileno(stdin), TCSETA, (void *) &old_term);
Erik Andersen13456d12000-03-16 08:09:57 +0000107}
108
Erik Andersen6273f652000-03-17 01:12:41 +0000109void prepareToDie(int sig)
Erik Andersen13456d12000-03-16 08:09:57 +0000110{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000111 cmdedit_reset_term();
112 fprintf(stdout, "\n");
113 exit(TRUE);
Erik Andersen13456d12000-03-16 08:09:57 +0000114}
115
116void input_home(int outputFd, int *cursor)
Erik Andersenc7c634b2000-03-19 05:28:55 +0000117{ /* Command line input routines */
118 while (*cursor > 0) {
119 xwrite(outputFd, "\b", 1);
120 --*cursor;
121 }
Erik Andersen13456d12000-03-16 08:09:57 +0000122}
123
124
125void input_delete(int outputFd, int cursor)
126{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000127 int j = 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000128
Erik Andersenc7c634b2000-03-19 05:28:55 +0000129 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 }
Erik Andersen13456d12000-03-16 08:09:57 +0000137
Erik Andersenc7c634b2000-03-19 05:28:55 +0000138 xwrite(outputFd, " \b", 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000139
Erik Andersenc7c634b2000-03-19 05:28:55 +0000140 while (j-- > cursor)
141 xwrite(outputFd, "\b", 1);
Erik Andersen13456d12000-03-16 08:09:57 +0000142}
143
144
145void input_end(int outputFd, int *cursor, int len)
146{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000147 while (*cursor < len) {
148 xwrite(outputFd, "\033[C", 3);
149 ++*cursor;
150 }
Erik Andersen13456d12000-03-16 08:09:57 +0000151}
152
153
154void input_backspace(int outputFd, int *cursor, int *len)
155{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000156 int j = 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000157
Erik Andersenc7c634b2000-03-19 05:28:55 +0000158 if (*cursor > 0) {
159 xwrite(outputFd, "\b \b", 3);
160 --*cursor;
161 memmove(parsenextc + *cursor, parsenextc + *cursor + 1,
162 BUFSIZ - *cursor + 1);
Erik Andersen13456d12000-03-16 08:09:57 +0000163
Erik Andersenc7c634b2000-03-19 05:28:55 +0000164 for (j = *cursor; j < (BUFSIZ - 1); j++) {
165 if (!*(parsenextc + j))
166 break;
167 else
168 xwrite(outputFd, (parsenextc + j), 1);
169 }
170
171 xwrite(outputFd, " \b", 2);
172
173 while (j-- > *cursor)
174 xwrite(outputFd, "\b", 1);
175
176 --*len;
Erik Andersen13456d12000-03-16 08:09:57 +0000177 }
Erik Andersen13456d12000-03-16 08:09:57 +0000178}
179
Erik Andersenc7c634b2000-03-19 05:28:55 +0000180char** username_completion_matches(char* command, int *num_matches)
Erik Andersen6273f652000-03-17 01:12:41 +0000181{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000182 char **matches = (char **) NULL;
183 *num_matches=0;
184 fprintf(stderr, "\nin username_completion_matches\n");
185 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000186}
Erik Andersen1dbe3402000-03-19 10:46:06 +0000187
188#include <dirent.h>
Erik Andersenc7c634b2000-03-19 05:28:55 +0000189char** find_path_executable_n_cwd_matches(char* command, int *num_matches)
Erik Andersen6273f652000-03-17 01:12:41 +0000190{
Erik Andersen1dbe3402000-03-19 10:46:06 +0000191 char *dirName;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000192 char **matches = (char **) NULL;
Erik Andersen1dbe3402000-03-19 10:46:06 +0000193 DIR *dir;
194 struct dirent *next;
195
196 matches = malloc( sizeof(char*)*50);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000197
Erik Andersen1dbe3402000-03-19 10:46:06 +0000198 /* Stick a wildcard onto the command, for later use */
199 strcat( command, "*");
Erik Andersenc7c634b2000-03-19 05:28:55 +0000200
Erik Andersen1dbe3402000-03-19 10:46:06 +0000201 /* Now wall the current directory */
202 dirName = get_current_dir_name();
203 dir = opendir(dirName);
204 if (!dir) {
205 /* Don't print an error, just shut up and return */
206 *num_matches=0;
207 return (matches);
208 }
209 while ((next = readdir(dir)) != NULL) {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000210
Erik Andersen1dbe3402000-03-19 10:46:06 +0000211 /* Some quick sanity checks */
212 if ((strcmp(next->d_name, "..") == 0)
213 || (strcmp(next->d_name, ".") == 0)) {
214 continue;
215 }
216 /* See if this matches */
217 if (check_wildcard_match(next->d_name, command) == TRUE) {
218 /* Cool, found a match. Add it to the list */
219 matches[*num_matches] = malloc(strlen(next->d_name)+1);
220 strcpy( matches[*num_matches], next->d_name);
221 ++*num_matches;
222 //matches = realloc( matches, sizeof(char*)*(*num_matches));
223 }
224 }
225
Erik Andersenc7c634b2000-03-19 05:28:55 +0000226 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000227}
228
229/*
230 * This function is used to grab a character buffer
231 * from the input file descriptor and allows you to
232 * a string with full command editing (sortof like
233 * a mini readline).
234 *
235 * The following standard commands are not implemented:
236 * ESC-b -- Move back one word
237 * ESC-f -- Move forward one word
238 * ESC-d -- Delete back one word
239 * ESC-h -- Delete forward one word
240 * CTL-t -- Transpose two characters
241 *
242 * Furthermore, the "vi" command editing keys are not implemented.
243 *
244 * TODO: implement TAB command completion. :)
245 *
246 */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000247extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
248 char command[BUFSIZ])
Erik Andersen13456d12000-03-16 08:09:57 +0000249{
250
Erik Andersenc7c634b2000-03-19 05:28:55 +0000251 int nr = 0;
252 int len = 0;
253 int j = 0;
254 int cursor = 0;
255 int break_out = 0;
256 int ret = 0;
257 int lastWasTab = FALSE;
258 char c = 0;
259 struct history *hp = his_end;
Erik Andersen13456d12000-03-16 08:09:57 +0000260
Erik Andersenc7c634b2000-03-19 05:28:55 +0000261 memset(command, 0, sizeof(command));
262 parsenextc = command;
263 if (!reset_term) {
264 xioctl(inputFd, TCGETA, (void *) &old_term);
265 memcpy(&new_term, &old_term, sizeof(struct termio));
Erik Andersen13456d12000-03-16 08:09:57 +0000266
Erik Andersenc7c634b2000-03-19 05:28:55 +0000267 new_term.c_cc[VMIN] = 1;
268 new_term.c_cc[VTIME] = 0;
269 new_term.c_lflag &= ~ICANON; /* unbuffered input */
270 new_term.c_lflag &= ~ECHO;
271 xioctl(inputFd, TCSETA, (void *) &new_term);
272 reset_term = 1;
273 } else {
274 xioctl(inputFd, TCSETA, (void *) &new_term);
275 }
Erik Andersen13456d12000-03-16 08:09:57 +0000276
Erik Andersenc7c634b2000-03-19 05:28:55 +0000277 memset(parsenextc, 0, BUFSIZ);
Erik Andersen13456d12000-03-16 08:09:57 +0000278
Erik Andersenc7c634b2000-03-19 05:28:55 +0000279 while (1) {
Erik Andersen6273f652000-03-17 01:12:41 +0000280
Erik Andersen13456d12000-03-16 08:09:57 +0000281 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenc7c634b2000-03-19 05:28:55 +0000282 return ret;
283
Erik Andersen13456d12000-03-16 08:09:57 +0000284 switch (c) {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000285 case 1:
286 /* Control-a -- Beginning of line */
287 input_home(outputFd, &cursor);
288 case 5:
289 /* Control-e -- End of line */
290 input_end(outputFd, &cursor, len);
291 break;
292 case 2:
293 /* Control-b -- Move back one character */
294 if (cursor > 0) {
295 xwrite(outputFd, "\033[D", 3);
296 cursor--;
297 }
298 break;
299 case 6:
300 /* Control-f -- Move forward one character */
301 if (cursor < len) {
302 xwrite(outputFd, "\033[C", 3);
303 cursor++;
304 }
305 break;
306 case 4:
307 /* Control-d -- Delete one character */
308 if (cursor != len) {
309 input_delete(outputFd, cursor);
310 len--;
311 } else if (len == 0) {
312 prepareToDie(0);
313 exit(0);
314 }
315 break;
316 case 14:
317 /* Control-n -- Get next command */
318 if (hp && hp->n && hp->n->s) {
319 free(hp->s);
320 hp->s = strdup(parsenextc);
321 hp = hp->n;
322 goto hop;
323 }
324 break;
325 case 16:
326 /* Control-p -- Get previous command */
327 if (hp && hp->p) {
328 free(hp->s);
329 hp->s = strdup(parsenextc);
330 hp = hp->p;
331 goto hop;
332 }
333 break;
334 case '\t':
335 {
336 /* Do TAB completion */
337 static int num_matches=0;
338 static char **matches = (char **) NULL;
339 int pos = cursor;
340
Erik Andersen13456d12000-03-16 08:09:57 +0000341
Erik Andersenc7c634b2000-03-19 05:28:55 +0000342 if (lastWasTab == FALSE) {
343 char *tmp, *tmp1, *matchBuf;
Erik Andersen13456d12000-03-16 08:09:57 +0000344
Erik Andersenc7c634b2000-03-19 05:28:55 +0000345 /* For now, we will not bother with trying to distinguish
346 * whether the cursor is in/at a command extression -- we
347 * will always try all possable matches. If you don't like
Erik Andersen1dbe3402000-03-19 10:46:06 +0000348 * that then feel free to fix it.
Erik Andersenc7c634b2000-03-19 05:28:55 +0000349 */
350
351 /* Make a local copy of the string -- up
Erik Andersen1dbe3402000-03-19 10:46:06 +0000352 * to the position of the cursor */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000353 matchBuf = (char *) calloc(BUFSIZ, sizeof(char));
354 strncpy(matchBuf, parsenextc, cursor);
355 tmp=matchBuf;
Erik Andersen13456d12000-03-16 08:09:57 +0000356
Erik Andersenc7c634b2000-03-19 05:28:55 +0000357 /* skip past any command seperator tokens */
358 while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
359 tmp=++tmp1;
360 /* skip any leading white space */
361 while (*tmp && isspace(*tmp))
362 ++tmp;
363 }
Erik Andersen13456d12000-03-16 08:09:57 +0000364
Erik Andersenc7c634b2000-03-19 05:28:55 +0000365 /* skip any leading white space */
366 while (*tmp && isspace(*tmp))
367 ++tmp;
Erik Andersen13456d12000-03-16 08:09:57 +0000368
Erik Andersenc7c634b2000-03-19 05:28:55 +0000369 /* Free up any memory already allocated */
370 if (matches) {
371 free(matches);
372 matches = (char **) NULL;
373 }
374
Erik Andersen1dbe3402000-03-19 10:46:06 +0000375 /* If the word starts with `~' and there is no slash in the word,
Erik Andersenc7c634b2000-03-19 05:28:55 +0000376 * then try completing this word as a username. */
Erik Andersen1dbe3402000-03-19 10:46:06 +0000377
378 /* FIXME -- this check is broken! */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000379 if (*tmp == '~' && !strchr(tmp, '/'))
380 matches = username_completion_matches(tmp, &num_matches);
381
Erik Andersen1dbe3402000-03-19 10:46:06 +0000382 /* Try to match any executable in our path and everything
383 * in the current working directory that matches. */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000384 if (!matches)
385 matches = find_path_executable_n_cwd_matches(tmp, &num_matches);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000386
387 /* Don't leak memory */
388 free( matchBuf);
389
390 /* Did we find exactly one match? */
391 if (matches && num_matches==1) {
392 /* write out the matched command */
393 strncpy(parsenextc+pos, matches[0]+pos, strlen(matches[0])-pos);
394 len=strlen(parsenextc);
395 cursor=len;
396 xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos);
397 break;
398 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000399 } else {
Erik Andersen1dbe3402000-03-19 10:46:06 +0000400 /* Ok -- the last char was a TAB. Since they
401 * just hit TAB again, print a list of all the
402 * available choices... */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000403 if ( matches && num_matches>0 ) {
404 int i, col;
405
Erik Andersen1dbe3402000-03-19 10:46:06 +0000406 /* Go to the next line */
407 xwrite(outputFd, "\n", 1);
408 /* Print the list of matches */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000409 for (i=0,col=0; i<num_matches; i++) {
Erik Andersen1dbe3402000-03-19 10:46:06 +0000410 char foo[17];
411 sprintf(foo, "%-14s ", matches[i]);
412 col += xwrite(outputFd, foo, strlen(foo));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000413 if (col > 60 && matches[i+1] != NULL) {
414 xwrite(outputFd, "\n", 1);
415 col = 0;
416 }
417 }
Erik Andersen1dbe3402000-03-19 10:46:06 +0000418 /* Go to the next line */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000419 xwrite(outputFd, "\n", 1);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000420 /* Rewrite the prompt */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000421 xwrite(outputFd, prompt, strlen(prompt));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000422 /* Rewrite the command */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000423 xwrite(outputFd, parsenextc, len);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000424 /* Put the cursor back to where it used to be */
425 for (cursor=len; cursor > pos; cursor--)
Erik Andersenc7c634b2000-03-19 05:28:55 +0000426 xwrite(outputFd, "\b", 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000427 }
428 }
429 break;
430 }
431 case '\b':
432 case DEL:
433 /* Backspace */
434 input_backspace(outputFd, &cursor, &len);
435 break;
436 case '\n':
437 /* Enter */
438 *(parsenextc + len++ + 1) = c;
439 xwrite(outputFd, &c, 1);
440 break_out = 1;
441 break;
442 case ESC:{
443 /* escape sequence follows */
444 if ((ret = read(inputFd, &c, 1)) < 1)
445 return ret;
446
447 if (c == '[') { /* 91 */
448 if ((ret = read(inputFd, &c, 1)) < 1)
449 return ret;
450
451 switch (c) {
452 case 'A':
453 /* Up Arrow -- Get previous command */
454 if (hp && hp->p) {
455 free(hp->s);
456 hp->s = strdup(parsenextc);
457 hp = hp->p;
458 goto hop;
459 }
460 break;
461 case 'B':
462 /* Down Arrow -- Get next command */
463 if (hp && hp->n && hp->n->s) {
464 free(hp->s);
465 hp->s = strdup(parsenextc);
466 hp = hp->n;
467 goto hop;
468 }
469 break;
470
471 /* This is where we rewrite the line
472 * using the selected history item */
473 hop:
474 len = strlen(parsenextc);
475
476 /* return to begining of line */
477 for (; cursor > 0; cursor--)
478 xwrite(outputFd, "\b", 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000479
480 /* erase old command */
481 for (j = 0; j < len; j++)
482 xwrite(outputFd, " ", 1);
483
484 /* return to begining of line */
485 for (j = len; j > 0; j--)
486 xwrite(outputFd, "\b", 1);
487
488 memset(parsenextc, 0, BUFSIZ);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000489 len = strlen(parsenextc);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000490 /* write new command */
491 strcpy(parsenextc, hp->s);
492 len = strlen(hp->s);
493 xwrite(outputFd, parsenextc, len);
494 cursor = len;
495 break;
496 case 'C':
497 /* Right Arrow -- Move forward one character */
498 if (cursor < len) {
499 xwrite(outputFd, "\033[C", 3);
500 cursor++;
501 }
502 break;
503 case 'D':
504 /* Left Arrow -- Move back one character */
505 if (cursor > 0) {
506 xwrite(outputFd, "\033[D", 3);
507 cursor--;
508 }
509 break;
510 case '3':
511 /* Delete */
512 if (cursor != len) {
513 input_delete(outputFd, cursor);
514 len--;
515 }
516 break;
517 case '1':
518 /* Home (Ctrl-A) */
519 input_home(outputFd, &cursor);
520 break;
521 case '4':
522 /* End (Ctrl-E) */
523 input_end(outputFd, &cursor, len);
524 break;
525 }
526 if (c == '1' || c == '3' || c == '4')
527 if ((ret = read(inputFd, &c, 1)) < 1)
528 return ret; /* read 126 (~) */
529 }
530 if (c == 'O') {
531 /* 79 */
532 if ((ret = read(inputFd, &c, 1)) < 1)
533 return ret;
534 switch (c) {
535 case 'H':
536 /* Home (xterm) */
537 input_home(outputFd, &cursor);
538 break;
539 case 'F':
540 /* End (xterm) */
541 input_end(outputFd, &cursor, len);
542 break;
543 }
544 }
545 c = 0;
546 break;
547 }
548
549 default: /* If it's regular input, do the normal thing */
550
551 if (!isprint(c)) /* Skip non-printable characters */
552 break;
553
554 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
555 break;
556
557 len++;
558
559 if (cursor == (len - 1)) { /* Append if at the end of the line */
560 *(parsenextc + cursor) = c;
561 } else { /* Insert otherwise */
562 memmove(parsenextc + cursor + 1, parsenextc + cursor,
563 len - cursor - 1);
564
565 *(parsenextc + cursor) = c;
566
567 for (j = cursor; j < len; j++)
568 xwrite(outputFd, parsenextc + j, 1);
569 for (; j > cursor; j--)
570 xwrite(outputFd, "\033[D", 3);
571 }
572
Erik Andersen13456d12000-03-16 08:09:57 +0000573 cursor++;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000574 xwrite(outputFd, &c, 1);
575 break;
Erik Andersen13456d12000-03-16 08:09:57 +0000576 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000577 if (c == '\t')
578 lastWasTab = TRUE;
579 else
580 lastWasTab = FALSE;
581
582 if (break_out) /* Enter is the command terminator, no more input. */
583 break;
584 }
585
586 nr = len + 1;
587 xioctl(inputFd, TCSETA, (void *) &old_term);
588 reset_term = 0;
589
590
591 /* Handle command history log */
592 if (*(parsenextc)) {
593
594 struct history *h = his_end;
595
596 if (!h) {
597 /* No previous history */
598 h = his_front = malloc(sizeof(struct history));
599 h->n = malloc(sizeof(struct history));
600
601 h->p = NULL;
602 h->s = strdup(parsenextc);
603 h->n->p = h;
604 h->n->n = NULL;
605 h->n->s = NULL;
606 his_end = h->n;
607 history_counter++;
608 } else {
609 /* Add a new history command */
610 h->n = malloc(sizeof(struct history));
611
612 h->n->p = h;
613 h->n->n = NULL;
614 h->n->s = NULL;
615 h->s = strdup(parsenextc);
616 his_end = h->n;
617
618 /* After max history, remove the oldest command */
619 if (history_counter >= MAX_HISTORY) {
620
621 struct history *p = his_front->n;
622
623 p->p = NULL;
624 free(his_front->s);
625 free(his_front);
626 his_front = p;
627 } else {
628 history_counter++;
629 }
Erik Andersen13456d12000-03-16 08:09:57 +0000630 }
Erik Andersen6273f652000-03-17 01:12:41 +0000631 }
Erik Andersen13456d12000-03-16 08:09:57 +0000632
Erik Andersenc7c634b2000-03-19 05:28:55 +0000633 return nr;
Erik Andersen13456d12000-03-16 08:09:57 +0000634}
635
636extern void cmdedit_init(void)
637{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000638 atexit(cmdedit_reset_term);
639 signal(SIGINT, prepareToDie);
640 signal(SIGQUIT, prepareToDie);
641 signal(SIGTERM, prepareToDie);
Erik Andersen13456d12000-03-16 08:09:57 +0000642}
Erik Andersenc7c634b2000-03-19 05:28:55 +0000643#endif /* BB_FEATURE_SH_COMMAND_EDITING */