blob: d15c694977ff28f746cf6723b6aae36b9ac04e1f [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 Andersena2685732000-04-09 18:27:46 +0000180#ifdef BB_FEATURE_SH_TAB_COMPLETION
181
Erik Andersenc7c634b2000-03-19 05:28:55 +0000182char** username_completion_matches(char* command, int *num_matches)
Erik Andersen6273f652000-03-17 01:12:41 +0000183{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000184 char **matches = (char **) NULL;
185 *num_matches=0;
186 fprintf(stderr, "\nin username_completion_matches\n");
187 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000188}
Erik Andersen1dbe3402000-03-19 10:46:06 +0000189
190#include <dirent.h>
Erik Andersenc7c634b2000-03-19 05:28:55 +0000191char** find_path_executable_n_cwd_matches(char* command, int *num_matches)
Erik Andersen6273f652000-03-17 01:12:41 +0000192{
Erik Andersen1dbe3402000-03-19 10:46:06 +0000193 char *dirName;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000194 char **matches = (char **) NULL;
Erik Andersen1dbe3402000-03-19 10:46:06 +0000195 DIR *dir;
196 struct dirent *next;
197
198 matches = malloc( sizeof(char*)*50);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000199
Erik Andersen1dbe3402000-03-19 10:46:06 +0000200 /* Stick a wildcard onto the command, for later use */
201 strcat( command, "*");
Erik Andersenc7c634b2000-03-19 05:28:55 +0000202
Erik Andersen1dbe3402000-03-19 10:46:06 +0000203 /* Now wall the current directory */
204 dirName = get_current_dir_name();
205 dir = opendir(dirName);
206 if (!dir) {
207 /* Don't print an error, just shut up and return */
208 *num_matches=0;
209 return (matches);
210 }
211 while ((next = readdir(dir)) != NULL) {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000212
Erik Andersen1dbe3402000-03-19 10:46:06 +0000213 /* Some quick sanity checks */
214 if ((strcmp(next->d_name, "..") == 0)
215 || (strcmp(next->d_name, ".") == 0)) {
216 continue;
217 }
218 /* See if this matches */
219 if (check_wildcard_match(next->d_name, command) == TRUE) {
220 /* Cool, found a match. Add it to the list */
221 matches[*num_matches] = malloc(strlen(next->d_name)+1);
222 strcpy( matches[*num_matches], next->d_name);
223 ++*num_matches;
224 //matches = realloc( matches, sizeof(char*)*(*num_matches));
225 }
226 }
227
Erik Andersenc7c634b2000-03-19 05:28:55 +0000228 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000229}
Erik Andersena2685732000-04-09 18:27:46 +0000230#endif
Erik Andersen6273f652000-03-17 01:12:41 +0000231
232/*
233 * This function is used to grab a character buffer
234 * from the input file descriptor and allows you to
235 * a string with full command editing (sortof like
236 * a mini readline).
237 *
238 * The following standard commands are not implemented:
239 * ESC-b -- Move back one word
240 * ESC-f -- Move forward one word
241 * ESC-d -- Delete back one word
242 * ESC-h -- Delete forward one word
243 * CTL-t -- Transpose two characters
244 *
245 * Furthermore, the "vi" command editing keys are not implemented.
246 *
247 * TODO: implement TAB command completion. :)
248 *
249 */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000250extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd,
251 char command[BUFSIZ])
Erik Andersen13456d12000-03-16 08:09:57 +0000252{
253
Erik Andersenc7c634b2000-03-19 05:28:55 +0000254 int nr = 0;
255 int len = 0;
256 int j = 0;
257 int cursor = 0;
258 int break_out = 0;
259 int ret = 0;
260 int lastWasTab = FALSE;
261 char c = 0;
262 struct history *hp = his_end;
Erik Andersen13456d12000-03-16 08:09:57 +0000263
Erik Andersenc7c634b2000-03-19 05:28:55 +0000264 memset(command, 0, sizeof(command));
265 parsenextc = command;
266 if (!reset_term) {
267 xioctl(inputFd, TCGETA, (void *) &old_term);
268 memcpy(&new_term, &old_term, sizeof(struct termio));
Erik Andersen13456d12000-03-16 08:09:57 +0000269
Erik Andersenc7c634b2000-03-19 05:28:55 +0000270 new_term.c_cc[VMIN] = 1;
271 new_term.c_cc[VTIME] = 0;
272 new_term.c_lflag &= ~ICANON; /* unbuffered input */
273 new_term.c_lflag &= ~ECHO;
274 xioctl(inputFd, TCSETA, (void *) &new_term);
275 reset_term = 1;
276 } else {
277 xioctl(inputFd, TCSETA, (void *) &new_term);
278 }
Erik Andersen13456d12000-03-16 08:09:57 +0000279
Erik Andersenc7c634b2000-03-19 05:28:55 +0000280 memset(parsenextc, 0, BUFSIZ);
Erik Andersen13456d12000-03-16 08:09:57 +0000281
Erik Andersenc7c634b2000-03-19 05:28:55 +0000282 while (1) {
Erik Andersen6273f652000-03-17 01:12:41 +0000283
Erik Andersen13456d12000-03-16 08:09:57 +0000284 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenc7c634b2000-03-19 05:28:55 +0000285 return ret;
286
Erik Andersenf3b3d172000-04-09 18:24:05 +0000287 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
Erik Andersen13456d12000-03-16 08:09:57 +0000295 switch (c) {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000296 case 1:
297 /* Control-a -- Beginning of line */
298 input_home(outputFd, &cursor);
299 case 5:
300 /* Control-e -- End of line */
301 input_end(outputFd, &cursor, len);
302 break;
303 case 2:
304 /* Control-b -- Move back one character */
305 if (cursor > 0) {
306 xwrite(outputFd, "\033[D", 3);
307 cursor--;
308 }
309 break;
310 case 6:
311 /* Control-f -- Move forward one character */
312 if (cursor < len) {
313 xwrite(outputFd, "\033[C", 3);
314 cursor++;
315 }
316 break;
317 case 4:
318 /* Control-d -- Delete one character */
319 if (cursor != len) {
320 input_delete(outputFd, cursor);
321 len--;
322 } else if (len == 0) {
323 prepareToDie(0);
324 exit(0);
325 }
326 break;
327 case 14:
328 /* Control-n -- Get next command */
329 if (hp && hp->n && hp->n->s) {
330 free(hp->s);
331 hp->s = strdup(parsenextc);
332 hp = hp->n;
333 goto hop;
334 }
335 break;
336 case 16:
337 /* Control-p -- Get previous command */
338 if (hp && hp->p) {
339 free(hp->s);
340 hp->s = strdup(parsenextc);
341 hp = hp->p;
342 goto hop;
343 }
344 break;
345 case '\t':
Erik Andersena2685732000-04-09 18:27:46 +0000346#ifdef BB_FEATURE_SH_TAB_COMPLETION
Erik Andersenc7c634b2000-03-19 05:28:55 +0000347 {
348 /* Do TAB completion */
349 static int num_matches=0;
350 static char **matches = (char **) NULL;
351 int pos = cursor;
352
Erik Andersen13456d12000-03-16 08:09:57 +0000353
Erik Andersenc7c634b2000-03-19 05:28:55 +0000354 if (lastWasTab == FALSE) {
355 char *tmp, *tmp1, *matchBuf;
Erik Andersen13456d12000-03-16 08:09:57 +0000356
Erik Andersenc7c634b2000-03-19 05:28:55 +0000357 /* 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
Erik Andersen1dbe3402000-03-19 10:46:06 +0000360 * that then feel free to fix it.
Erik Andersenc7c634b2000-03-19 05:28:55 +0000361 */
362
363 /* Make a local copy of the string -- up
Erik Andersen1dbe3402000-03-19 10:46:06 +0000364 * to the position of the cursor */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000365 matchBuf = (char *) calloc(BUFSIZ, sizeof(char));
366 strncpy(matchBuf, parsenextc, cursor);
367 tmp=matchBuf;
Erik Andersen13456d12000-03-16 08:09:57 +0000368
Erik Andersenc7c634b2000-03-19 05:28:55 +0000369 /* 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 }
Erik Andersen13456d12000-03-16 08:09:57 +0000376
Erik Andersenc7c634b2000-03-19 05:28:55 +0000377 /* skip any leading white space */
378 while (*tmp && isspace(*tmp))
379 ++tmp;
Erik Andersen13456d12000-03-16 08:09:57 +0000380
Erik Andersenc7c634b2000-03-19 05:28:55 +0000381 /* Free up any memory already allocated */
382 if (matches) {
383 free(matches);
384 matches = (char **) NULL;
385 }
386
Erik Andersen1dbe3402000-03-19 10:46:06 +0000387 /* If the word starts with `~' and there is no slash in the word,
Erik Andersenc7c634b2000-03-19 05:28:55 +0000388 * then try completing this word as a username. */
Erik Andersen1dbe3402000-03-19 10:46:06 +0000389
390 /* FIXME -- this check is broken! */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000391 if (*tmp == '~' && !strchr(tmp, '/'))
392 matches = username_completion_matches(tmp, &num_matches);
393
Erik Andersen1dbe3402000-03-19 10:46:06 +0000394 /* Try to match any executable in our path and everything
395 * in the current working directory that matches. */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000396 if (!matches)
397 matches = find_path_executable_n_cwd_matches(tmp, &num_matches);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000398
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 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000411 } else {
Erik Andersen1dbe3402000-03-19 10:46:06 +0000412 /* Ok -- the last char was a TAB. Since they
413 * just hit TAB again, print a list of all the
414 * available choices... */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000415 if ( matches && num_matches>0 ) {
416 int i, col;
417
Erik Andersen1dbe3402000-03-19 10:46:06 +0000418 /* Go to the next line */
419 xwrite(outputFd, "\n", 1);
420 /* Print the list of matches */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000421 for (i=0,col=0; i<num_matches; i++) {
Erik Andersen1dbe3402000-03-19 10:46:06 +0000422 char foo[17];
423 sprintf(foo, "%-14s ", matches[i]);
424 col += xwrite(outputFd, foo, strlen(foo));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000425 if (col > 60 && matches[i+1] != NULL) {
426 xwrite(outputFd, "\n", 1);
427 col = 0;
428 }
429 }
Erik Andersen1dbe3402000-03-19 10:46:06 +0000430 /* Go to the next line */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000431 xwrite(outputFd, "\n", 1);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000432 /* Rewrite the prompt */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000433 xwrite(outputFd, prompt, strlen(prompt));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000434 /* Rewrite the command */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000435 xwrite(outputFd, parsenextc, len);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000436 /* Put the cursor back to where it used to be */
437 for (cursor=len; cursor > pos; cursor--)
Erik Andersenc7c634b2000-03-19 05:28:55 +0000438 xwrite(outputFd, "\b", 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000439 }
440 }
441 break;
442 }
Erik Andersena2685732000-04-09 18:27:46 +0000443#else
444 break;
445#endif
Erik Andersenc7c634b2000-03-19 05:28:55 +0000446 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;
457 case ESC:{
458 /* escape sequence follows */
459 if ((ret = read(inputFd, &c, 1)) < 1)
460 return ret;
461
462 if (c == '[') { /* 91 */
463 if ((ret = read(inputFd, &c, 1)) < 1)
464 return ret;
465
466 switch (c) {
467 case 'A':
468 /* Up Arrow -- Get previous command */
469 if (hp && hp->p) {
470 free(hp->s);
471 hp->s = strdup(parsenextc);
472 hp = hp->p;
473 goto hop;
474 }
475 break;
476 case 'B':
477 /* Down Arrow -- Get next command */
478 if (hp && hp->n && hp->n->s) {
479 free(hp->s);
480 hp->s = strdup(parsenextc);
481 hp = hp->n;
482 goto hop;
483 }
484 break;
485
486 /* This is where we rewrite the line
487 * using the selected history item */
488 hop:
489 len = strlen(parsenextc);
490
491 /* return to begining of line */
492 for (; cursor > 0; cursor--)
493 xwrite(outputFd, "\b", 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000494
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);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000504 len = strlen(parsenextc);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000505 /* write new command */
506 strcpy(parsenextc, hp->s);
507 len = strlen(hp->s);
508 xwrite(outputFd, parsenextc, len);
509 cursor = len;
510 break;
511 case 'C':
512 /* Right Arrow -- Move forward one character */
513 if (cursor < len) {
514 xwrite(outputFd, "\033[C", 3);
515 cursor++;
516 }
517 break;
518 case 'D':
519 /* Left Arrow -- Move back one character */
520 if (cursor > 0) {
521 xwrite(outputFd, "\033[D", 3);
522 cursor--;
523 }
524 break;
525 case '3':
526 /* Delete */
527 if (cursor != len) {
528 input_delete(outputFd, cursor);
529 len--;
530 }
531 break;
Erik Andersenf3b3d172000-04-09 18:24:05 +0000532
533 //case '5': case '6': /* pgup/pgdown */
534
535 case '7':
536 /* rxvt home */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000537 case '1':
538 /* Home (Ctrl-A) */
539 input_home(outputFd, &cursor);
540 break;
Erik Andersenf3b3d172000-04-09 18:24:05 +0000541 case '8':
542 /* rxvt END */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000543 case '4':
544 /* End (Ctrl-E) */
545 input_end(outputFd, &cursor, len);
546 break;
547 }
548 if (c == '1' || c == '3' || c == '4')
549 if ((ret = read(inputFd, &c, 1)) < 1)
550 return ret; /* read 126 (~) */
551 }
552 if (c == 'O') {
553 /* 79 */
554 if ((ret = read(inputFd, &c, 1)) < 1)
555 return ret;
556 switch (c) {
557 case 'H':
558 /* Home (xterm) */
559 input_home(outputFd, &cursor);
560 break;
561 case 'F':
562 /* End (xterm) */
563 input_end(outputFd, &cursor, len);
564 break;
565 }
566 }
567 c = 0;
568 break;
569 }
570
571 default: /* If it's regular input, do the normal thing */
572
573 if (!isprint(c)) /* Skip non-printable characters */
574 break;
575
576 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
577 break;
578
579 len++;
580
581 if (cursor == (len - 1)) { /* Append if at the end of the line */
582 *(parsenextc + cursor) = c;
583 } else { /* Insert otherwise */
584 memmove(parsenextc + cursor + 1, parsenextc + cursor,
585 len - cursor - 1);
586
587 *(parsenextc + cursor) = c;
588
589 for (j = cursor; j < len; j++)
590 xwrite(outputFd, parsenextc + j, 1);
591 for (; j > cursor; j--)
592 xwrite(outputFd, "\033[D", 3);
593 }
594
Erik Andersen13456d12000-03-16 08:09:57 +0000595 cursor++;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000596 xwrite(outputFd, &c, 1);
597 break;
Erik Andersen13456d12000-03-16 08:09:57 +0000598 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000599 if (c == '\t')
600 lastWasTab = TRUE;
601 else
602 lastWasTab = FALSE;
603
604 if (break_out) /* Enter is the command terminator, no more input. */
605 break;
606 }
607
608 nr = len + 1;
609 xioctl(inputFd, TCSETA, (void *) &old_term);
610 reset_term = 0;
611
612
613 /* Handle command history log */
614 if (*(parsenextc)) {
615
616 struct history *h = his_end;
617
618 if (!h) {
619 /* No previous history */
620 h = his_front = malloc(sizeof(struct history));
621 h->n = malloc(sizeof(struct history));
622
623 h->p = NULL;
624 h->s = strdup(parsenextc);
625 h->n->p = h;
626 h->n->n = NULL;
627 h->n->s = NULL;
628 his_end = h->n;
629 history_counter++;
630 } else {
631 /* Add a new history command */
632 h->n = malloc(sizeof(struct history));
633
634 h->n->p = h;
635 h->n->n = NULL;
636 h->n->s = NULL;
637 h->s = strdup(parsenextc);
638 his_end = h->n;
639
640 /* After max history, remove the oldest command */
641 if (history_counter >= MAX_HISTORY) {
642
643 struct history *p = his_front->n;
644
645 p->p = NULL;
646 free(his_front->s);
647 free(his_front);
648 his_front = p;
649 } else {
650 history_counter++;
651 }
Erik Andersen13456d12000-03-16 08:09:57 +0000652 }
Erik Andersen6273f652000-03-17 01:12:41 +0000653 }
Erik Andersen13456d12000-03-16 08:09:57 +0000654
Erik Andersenc7c634b2000-03-19 05:28:55 +0000655 return nr;
Erik Andersen13456d12000-03-16 08:09:57 +0000656}
657
658extern void cmdedit_init(void)
659{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000660 atexit(cmdedit_reset_term);
661 signal(SIGINT, prepareToDie);
662 signal(SIGQUIT, prepareToDie);
663 signal(SIGTERM, prepareToDie);
Erik Andersen13456d12000-03-16 08:09:57 +0000664}
Erik Andersenc7c634b2000-03-19 05:28:55 +0000665#endif /* BB_FEATURE_SH_COMMAND_EDITING */