blob: 2e57b9a349e1be06446848f9af10bb5257b048c6 [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/*
Erik Andersen61677fe2000-04-13 01:18:56 +00003 * Termios command line History and Editting, originally
4 * intended for NetBSD sh (ash)
Erik Andersen13456d12000-03-16 08:09:57 +00005 * Copyright (c) 1999
6 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu>
7 * Etc: Dave Cinege <dcinege@psychosis.com>
Erik Andersen61677fe2000-04-13 01:18:56 +00008 * Majorly adjusted/re-written for busybox:
9 * Erik Andersen <andersee@debian.org>
Erik Andersen13456d12000-03-16 08:09:57 +000010 *
11 * You may use this code as you wish, so long as the original author(s)
12 * are attributed in any redistributions of the source code.
13 * This code is 'as is' with no warranty.
14 * This code may safely be consumed by a BSD or GPL license.
15 *
16 * v 0.5 19990328 Initial release
17 *
18 * Future plans: Simple file and path name completion. (like BASH)
19 *
20 */
21
22/*
23 Usage and Known bugs:
24 Terminal key codes are not extensive, and more will probably
25 need to be added. This version was created on Debian GNU/Linux 2.x.
26 Delete, Backspace, Home, End, and the arrow keys were tested
27 to work in an Xterm and console. Ctrl-A also works as Home.
Mark Whitley4e338752001-01-26 20:42:23 +000028 Ctrl-E also works as End.
Erik Andersen13456d12000-03-16 08:09:57 +000029
Mark Whitley4e338752001-01-26 20:42:23 +000030
31 Editor with vertical scrolling and completion by
32 Vladimir Oleynik. vodz@usa.net (c) 2001
33
34 Small bug: not true work if terminal size (x*y symbols) less
35 size (prompt + editor`s line + 2 symbols)
Erik Andersen13456d12000-03-16 08:09:57 +000036 */
37
Mark Whitley4e338752001-01-26 20:42:23 +000038
39
Eric Andersen3570a342000-09-25 21:45:58 +000040#include "busybox.h"
Mark Whitley4e338752001-01-26 20:42:23 +000041
Erik Andersen13456d12000-03-16 08:09:57 +000042#ifdef BB_FEATURE_SH_COMMAND_EDITING
43
44#include <stdio.h>
45#include <errno.h>
46#include <unistd.h>
47#include <stdlib.h>
48#include <string.h>
Erik Andersen1d1d9502000-04-21 01:26:49 +000049#include <sys/ioctl.h>
Erik Andersen13456d12000-03-16 08:09:57 +000050#include <ctype.h>
51#include <signal.h>
52
Mark Whitley4e338752001-01-26 20:42:23 +000053#ifdef BB_FEATURE_SH_TAB_COMPLETION
54#include <sys/stat.h>
55#endif
56
Eric Andersenaf4ac772001-02-01 22:43:49 +000057#include "pwd_grp/pwd.h"
58
59
Mark Whitley59ab0252001-01-23 22:30:04 +000060static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */
Erik Andersen13456d12000-03-16 08:09:57 +000061
Mark Whitley59ab0252001-01-23 22:30:04 +000062enum {
63 ESC = 27,
64 DEL = 127,
65};
66
Erik Andersen6273f652000-03-17 01:12:41 +000067#define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0)
68#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
Erik Andersen13456d12000-03-16 08:09:57 +000069
70static struct history *his_front = NULL; /* First element in command line list */
71static struct history *his_end = NULL; /* Last element in command line list */
Erik Andersen1d1d9502000-04-21 01:26:49 +000072
73/* ED: sparc termios is broken: revert back to old termio handling. */
Erik Andersen1d1d9502000-04-21 01:26:49 +000074
75#if #cpu(sparc)
76# include <termio.h>
77# define termios termio
78# define setTermSettings(fd,argp) ioctl(fd,TCSETAF,argp)
79# define getTermSettings(fd,argp) ioctl(fd,TCGETA,argp)
80#else
81# include <termios.h>
82# define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
83# define getTermSettings(fd,argp) tcgetattr(fd, argp);
84#endif
85
86/* Current termio and the previous termio before starting sh */
Eric Andersen63a86222000-11-07 06:52:13 +000087static struct termios initial_settings, new_settings;
Erik Andersen8ea7d8c2000-05-20 00:40:08 +000088
89
90#ifndef _POSIX_VDISABLE
91#define _POSIX_VDISABLE '\0'
92#endif
93
Erik Andersen1d1d9502000-04-21 01:26:49 +000094
Mark Whitley4e338752001-01-26 20:42:23 +000095static
96volatile int cmdedit_termw; /* actual terminal width */
97static int history_counter = 0; /* Number of commands in history list */
Erik Andersen1d1d9502000-04-21 01:26:49 +000098
Mark Whitley4e338752001-01-26 20:42:23 +000099static
100volatile int handlers_sets = 0; /* Set next bites
101 when atexit() has been called
102 and set many "terminates" signal handlers
103 and winchg signal handler
104 and if the terminal needs to be reset upon exit
105 */
106enum {
107 SET_ATEXIT = 1,
108 SET_TERM_HANDLERS = 2,
109 SET_WCHG_HANDLERS = 4,
110 SET_RESET_TERM = 8,
111};
Erik Andersen13456d12000-03-16 08:09:57 +0000112
Eric Andersen86349772000-12-18 20:25:50 +0000113
Mark Whitley4e338752001-01-26 20:42:23 +0000114static int cmdedit_x; /* real x terminal position,
115 require put prompt in start x position */
116static int cmdedit_y; /* pseudoreal y terminal position */
117static int cmdedit_prmt_len; /* for fast running, without duplicate calculate */
118
119static int cursor; /* required global for signal handler */
120static int len; /* --- "" - - "" - -"- --""-- --""--- */
121static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */
122static const char *cmdedit_prompt;/* --- "" - - "" - -"- --""-- --""--- */
Eric Andersen86349772000-12-18 20:25:50 +0000123
124/* Link into lash to reset context to 0
125 * on ^C and such */
126extern unsigned int shell_context;
127
Erik Andersen13456d12000-03-16 08:09:57 +0000128
129struct history {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000130 char *s;
131 struct history *p;
132 struct history *n;
Erik Andersen13456d12000-03-16 08:09:57 +0000133};
134
Mark Whitley4e338752001-01-26 20:42:23 +0000135static void cmdedit_setwidth(int w, int redraw_flg);
Erik Andersen13456d12000-03-16 08:09:57 +0000136
Mark Whitley4e338752001-01-26 20:42:23 +0000137static void win_changed(int nsig)
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000138{
139 struct winsize win = { 0, 0, 0, 0 };
Mark Whitley4e338752001-01-26 20:42:23 +0000140 static __sighandler_t previous_SIGWINCH_handler; /* for reset */
Erik Andersen61677fe2000-04-13 01:18:56 +0000141
Eric Andersen306f4fe2001-01-31 00:30:45 +0000142 /* emulate signal call if not called as a sig handler */
Mark Whitley4e338752001-01-26 20:42:23 +0000143 if(nsig == -SIGWINCH || nsig == SIGWINCH) {
144 ioctl(0, TIOCGWINSZ, &win);
145 if (win.ws_col > 0) {
146 cmdedit_setwidth( win.ws_col, nsig == SIGWINCH );
Eric Andersen306f4fe2001-01-31 00:30:45 +0000147 } else {
148 /* Default to 79 if their console doesn't want to share */
149 cmdedit_setwidth( 79, nsig == SIGWINCH );
Eric Andersen4bbdd782001-01-30 22:23:17 +0000150 }
Mark Whitley4e338752001-01-26 20:42:23 +0000151 }
Eric Andersen4bbdd782001-01-30 22:23:17 +0000152
153 /* Unix not all standart in recall signal */
Mark Whitley4e338752001-01-26 20:42:23 +0000154
Eric Andersen4bbdd782001-01-30 22:23:17 +0000155 if(nsig == -SIGWINCH) /* save previous handler */
Mark Whitley4e338752001-01-26 20:42:23 +0000156 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
157 else if(nsig == SIGWINCH) /* signaled called handler */
Eric Andersen4bbdd782001-01-30 22:23:17 +0000158 signal(SIGWINCH, win_changed); /* set for next call */
Mark Whitley4e338752001-01-26 20:42:23 +0000159 else /* set previous handler */
160 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
161}
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000162
163static void cmdedit_reset_term(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000164{
Mark Whitley4e338752001-01-26 20:42:23 +0000165 if((handlers_sets & SET_RESET_TERM)!=0) {
Erik Andersena6c75222000-04-18 00:00:52 +0000166 /* sparc and other have broken termios support: use old termio handling. */
Erik Andersen1d1d9502000-04-21 01:26:49 +0000167 setTermSettings(fileno(stdin), (void*) &initial_settings);
Mark Whitley4e338752001-01-26 20:42:23 +0000168 handlers_sets &= ~SET_RESET_TERM;
169 }
170 if((handlers_sets & SET_WCHG_HANDLERS)!=0) {
171 /* reset SIGWINCH handler to previous (default) */
172 win_changed(0);
173 handlers_sets &= ~SET_WCHG_HANDLERS;
174 }
175 fflush(stdout);
Eric Andersenb040d4f2000-07-25 18:01:20 +0000176#ifdef BB_FEATURE_CLEAN_UP
177 if (his_front) {
178 struct history *n;
179 //while(his_front!=his_end) {
180 while(his_front!=his_end) {
181 n = his_front->n;
182 free(his_front->s);
183 free(his_front);
184 his_front=n;
185 }
186 }
187#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000188}
189
Mark Whitley4e338752001-01-26 20:42:23 +0000190
191
192/* special for recount position for scroll and remove terminal margin effect */
193static void cmdedit_set_out_char(int c, int next_char) {
194 putchar(c);
195 if(++cmdedit_x>=cmdedit_termw) {
196 /* terminal is scrolled down */
197 cmdedit_y++;
198 cmdedit_x=0;
199
200 if(!next_char)
201 next_char = ' ';
202 /* destroy "(auto)margin" */
203 putchar(next_char);
204 putchar('\b');
205 }
206 cursor++;
Erik Andersen13456d12000-03-16 08:09:57 +0000207}
208
Mark Whitley4e338752001-01-26 20:42:23 +0000209/* Move to end line. Bonus: rewrite line from cursor without use
210 special control terminal strings, also saved size and speed! */
211static void input_end (void) {
212 while(cursor < len)
213 cmdedit_set_out_char(command_ps[cursor], 0);
214}
215
216/* Go to the next line */
217static void goto_new_line(void) {
218 input_end();
219 cmdedit_set_out_char('\n', 0);
220}
221
222
223static inline void out1str(const char *s) { fputs (s, stdout); }
224static inline void beep (void) { putchar('\007'); }
225
Erik Andersenf0657d32000-04-12 17:49:52 +0000226/* Go to HOME position */
Mark Whitley4e338752001-01-26 20:42:23 +0000227static void input_home(void)
Erik Andersenf0657d32000-04-12 17:49:52 +0000228{
Mark Whitley4e338752001-01-26 20:42:23 +0000229 while(cmdedit_y>0) { /* up to start y */
230 out1str("\033[A");
231 cmdedit_y--;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000232 }
Mark Whitley4e338752001-01-26 20:42:23 +0000233 putchar('\r');
234 cursor = 0;
235 out1str(cmdedit_prompt);
236 cmdedit_x = cmdedit_prmt_len;
237
Erik Andersen13456d12000-03-16 08:09:57 +0000238}
239
Mark Whitley4e338752001-01-26 20:42:23 +0000240/* Move back one charactor */
241static void input_backward(void) {
242 if (cursor > 0) {
243 cursor--;
244 if(cmdedit_x!=0) { /* no first position in terminal line */
245 putchar('\b');
246 cmdedit_x--;
247 }
248 else {
249 out1str("\033[A"); /* up */
250 cmdedit_y--;
Erik Andersen13456d12000-03-16 08:09:57 +0000251
Mark Whitley4e338752001-01-26 20:42:23 +0000252 /* to end in current terminal line */
253 while(cmdedit_x<(cmdedit_termw-1)) {
254 out1str("\033[C");
255 cmdedit_x++;
256 }
257 }
Erik Andersen13456d12000-03-16 08:09:57 +0000258 }
Erik Andersen13456d12000-03-16 08:09:57 +0000259}
260
Erik Andersenf0657d32000-04-12 17:49:52 +0000261/* Delete the char in front of the cursor */
Mark Whitley4e338752001-01-26 20:42:23 +0000262static void input_delete(void)
Erik Andersenf0657d32000-04-12 17:49:52 +0000263{
Mark Whitley4e338752001-01-26 20:42:23 +0000264 int j = cursor;
Erik Andersena2685732000-04-09 18:27:46 +0000265
Mark Whitley4e338752001-01-26 20:42:23 +0000266 if (j == len)
Erik Andersenf0657d32000-04-12 17:49:52 +0000267 return;
268
Mark Whitley4e338752001-01-26 20:42:23 +0000269 memmove (command_ps + j, command_ps + j + 1, BUFSIZ - j - 1);
270 len--;
271 input_end(); /* rewtite new line */
272 cmdedit_set_out_char(' ', 0); /* destroy end char */
273 while (j < cursor)
274 input_backward(); /* back to old pos cursor */
Erik Andersenf0657d32000-04-12 17:49:52 +0000275}
276
Mark Whitley4e338752001-01-26 20:42:23 +0000277/* Delete the char in back of the cursor */
278static void input_backspace(void)
279{
280 if (cursor > 0) {
281 input_backward();
282 input_delete ();
283 }
284}
285
286
Erik Andersenf0657d32000-04-12 17:49:52 +0000287/* Move forward one charactor */
Mark Whitley4e338752001-01-26 20:42:23 +0000288static void input_forward(void)
Erik Andersenf0657d32000-04-12 17:49:52 +0000289{
Mark Whitley4e338752001-01-26 20:42:23 +0000290 if (cursor < len)
291 cmdedit_set_out_char(command_ps[cursor], command_ps[cursor + 1]);
Erik Andersenf0657d32000-04-12 17:49:52 +0000292}
293
294
Mark Whitley4e338752001-01-26 20:42:23 +0000295static void clean_up_and_die(int sig)
296{
297 goto_new_line();
298 if (sig!=SIGINT)
299 exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */
300 cmdedit_reset_term();
301}
302
303static void cmdedit_setwidth(int w, int redraw_flg)
304{
305 cmdedit_termw = cmdedit_prmt_len+2;
306 if (w > cmdedit_termw) {
307
308 cmdedit_termw = w;
309
310 if(redraw_flg) {
311 int sav_cursor = cursor;
312
313 /* set variables for new terminal size */
314 cmdedit_y = sav_cursor/w;
315 cmdedit_x = sav_cursor-cmdedit_y*w;
316
317 /* redraw */
318 input_home();
319 input_end();
320 while(sav_cursor<cursor)
321 input_backward();
322 }
323 } else {
Matt Kraaidd19c692001-01-31 19:00:21 +0000324 error_msg("\n*** Error: minimum screen width is %d", cmdedit_termw);
Mark Whitley4e338752001-01-26 20:42:23 +0000325 }
326}
327
328extern void cmdedit_init(void)
329{
330 if((handlers_sets & SET_WCHG_HANDLERS)==0) {
Eric Andersen4bbdd782001-01-30 22:23:17 +0000331 /* pretend we received a signal in order to set term size and sig handling */
Mark Whitley4e338752001-01-26 20:42:23 +0000332 win_changed(-SIGWINCH);
333 handlers_sets |= SET_WCHG_HANDLERS;
334 }
335
336 if((handlers_sets & SET_ATEXIT)==0) {
337 atexit(cmdedit_reset_term); /* be sure to do this only once */
338 handlers_sets |= SET_ATEXIT;
339 }
340 if((handlers_sets & SET_TERM_HANDLERS)==0) {
341 signal(SIGKILL, clean_up_and_die);
342 signal(SIGINT, clean_up_and_die);
343 signal(SIGQUIT, clean_up_and_die);
344 signal(SIGTERM, clean_up_and_die);
345 handlers_sets |= SET_TERM_HANDLERS;
346 }
347}
Erik Andersenf0657d32000-04-12 17:49:52 +0000348
349#ifdef BB_FEATURE_SH_TAB_COMPLETION
Mark Whitley4e338752001-01-26 20:42:23 +0000350
351#ifdef BB_FEATURE_USERNAME_COMPLETION
352static char** username_tab_completion(char *ud, int *num_matches)
Erik Andersen6273f652000-03-17 01:12:41 +0000353{
Mark Whitley4e338752001-01-26 20:42:23 +0000354 static struct passwd *entry;
355 int userlen;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000356 char **matches = (char **) NULL;
Mark Whitley4e338752001-01-26 20:42:23 +0000357 char *temp;
358 int nm = 0;
359
Eric Andersen4bbdd782001-01-30 22:23:17 +0000360 setpwent ();
Mark Whitley4e338752001-01-26 20:42:23 +0000361 userlen = strlen (ud + 1);
362
Eric Andersen4bbdd782001-01-30 22:23:17 +0000363 while ((entry = getpwent ()) != NULL) {
Mark Whitley4e338752001-01-26 20:42:23 +0000364 /* Null usernames should result in all users as possible completions. */
365 if (!userlen || !strncmp (ud + 1, entry->pw_name, userlen)) {
366
367 temp = xmalloc (3 + strlen (entry->pw_name));
368 sprintf(temp, "~%s/", entry->pw_name);
369
370 matches = xrealloc(matches, (nm+1)*sizeof(char *));
371 matches[nm++] = temp;
372 }
373 }
374
Eric Andersen4bbdd782001-01-30 22:23:17 +0000375 endpwent ();
Mark Whitley4e338752001-01-26 20:42:23 +0000376 (*num_matches) = nm;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000377 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000378}
Mark Whitley4e338752001-01-26 20:42:23 +0000379#endif
380
381enum {
382 FIND_EXE_ONLY = 0,
383 FIND_DIR_ONLY = 1,
384 FIND_FILE_ONLY = 2,
385};
Erik Andersen1dbe3402000-03-19 10:46:06 +0000386
387#include <dirent.h>
Mark Whitley4e338752001-01-26 20:42:23 +0000388
389static int path_parse(char ***p, int flags)
390{
391 int npth;
392 char *tmp;
393 char *pth;
394
395 if(flags!=FIND_EXE_ONLY || (pth=getenv("PATH"))==0) {
396 /* if not setenv PATH variable, to search cur dir "." */
397 (*p) = xmalloc(sizeof(char *));
398 (*p)[0] = xstrdup(".");
399 return 1;
400 }
401
402 tmp = pth;
403 npth=0;
404
405 for(;;) {
406 npth++; /* count words is + 1 count ':' */
407 tmp = strchr(tmp, ':');
408 if(tmp)
409 tmp++;
410 else
411 break;
412 }
413
414 *p = xmalloc(npth*sizeof(char *));
415
416 tmp = pth;
417 (*p)[0] = xstrdup(tmp);
418 npth=1; /* count words is + 1 count ':' */
419
420 for(;;) {
421 tmp = strchr(tmp, ':');
422 if(tmp) {
423 (*p)[0][(tmp-pth)]=0; /* ':' -> '\0'*/
424 tmp++;
425 } else
426 break;
427 (*p)[npth++] = &(*p)[0][(tmp-pth)]; /* p[next]=p[0][&'\0'+1] */
428 }
429
430 return npth;
431}
432
433static char** exe_n_cwd_tab_completion(char* command, int *num_matches, int type)
Erik Andersen6273f652000-03-17 01:12:41 +0000434{
Erik Andersen1dbe3402000-03-19 10:46:06 +0000435 char *dirName;
Mark Whitley4e338752001-01-26 20:42:23 +0000436 char **matches = 0;
Erik Andersen1dbe3402000-03-19 10:46:06 +0000437 DIR *dir;
438 struct dirent *next;
Mark Whitley4e338752001-01-26 20:42:23 +0000439 char cmd [BUFSIZ+4];
440 char *dirbuf;
441 char found [BUFSIZ+4];
442 int nm = *num_matches;
443 struct stat st;
444 char **paths;
445 int npaths;
446 int i;
447 char full_pth[BUFSIZ+4+PATH_MAX];
448
449
450 strcpy(cmd, command); /* save for change (last '/' to '\0') */
451
452 dirName = strrchr(cmd, '/');
453 if(dirName==NULL) {
454 /* no dir, if flags==EXE_ONLY - get paths, else "." */
455 npaths = path_parse(&paths, type);
456 if(npaths==0)
457 return 0;
458 } else {
459 /* with dir */
460
461 /* save dir */
462 dirbuf = xstrdup(cmd);
463 /* set only dirname */
464 dirbuf[(dirName-cmd)+1]=0;
465
466 /* strip dirname in cmd */
467 strcpy(cmd, dirName+1);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000468
Mark Whitley4e338752001-01-26 20:42:23 +0000469 paths = xmalloc(sizeof(char*));
470 paths[0] = dirbuf;
471 npaths = 1; /* only 1 dir */
472 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000473
Mark Whitley4e338752001-01-26 20:42:23 +0000474 for(i=0; i < npaths; i++) {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000475
Mark Whitley4e338752001-01-26 20:42:23 +0000476 dir = opendir(paths[i]);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000477 if (!dir) {
478 /* Don't print an error, just shut up and return */
Erik Andersen1dbe3402000-03-19 10:46:06 +0000479 return (matches);
480 }
481 while ((next = readdir(dir)) != NULL) {
Mark Whitley4e338752001-01-26 20:42:23 +0000482 /* matched ? */
483 if(strncmp(next->d_name, cmd, strlen(cmd)))
484 continue;
485 /* not see .name without .match */
486 if(*next->d_name == '.' && *cmd != '.')
487 continue;
488 sprintf(full_pth, "%s/%s", paths[i], next->d_name);
489 /* hmm, remover in progress? */
490 if(stat(full_pth, &st)<0)
491 continue;
492 /* Cool, found a match. */
493 if (S_ISDIR(st.st_mode)) {
494 /* name is directory */
495 strcpy(found, next->d_name);
496 strcat(found, "/");
497 if(type==FIND_DIR_ONLY)
498 strcat(found, " ");
499 } else {
500 /* not put found file if search only dirs for cd */
501 if(type==FIND_DIR_ONLY)
Erik Andersen1dbe3402000-03-19 10:46:06 +0000502 continue;
Mark Whitley4e338752001-01-26 20:42:23 +0000503 strcpy(found, next->d_name);
504 strcat(found, " ");
Erik Andersen1dbe3402000-03-19 10:46:06 +0000505 }
Mark Whitley4e338752001-01-26 20:42:23 +0000506 /* Add it to the list */
507 matches = xrealloc(matches, (nm+1)*sizeof(char *));
508 matches[nm++] = xstrdup(found);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000509 }
510 }
Mark Whitley4e338752001-01-26 20:42:23 +0000511 free(paths[0]); /* allocate memory only in first member */
512 free(paths);
513 *num_matches = nm;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000514 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000515}
Erik Andersenf0657d32000-04-12 17:49:52 +0000516
Mark Whitley4e338752001-01-26 20:42:23 +0000517static void input_tab(int lastWasTab)
Erik Andersenf0657d32000-04-12 17:49:52 +0000518{
519 /* Do TAB completion */
Mark Whitley4e338752001-01-26 20:42:23 +0000520 static int num_matches;
521 static char **matches;
522
523 char matchBuf[BUFSIZ];
524
525 int pos = cursor;
526 int find_type=FIND_FILE_ONLY;
Erik Andersenf0657d32000-04-12 17:49:52 +0000527
528
529 if (lastWasTab == FALSE) {
Mark Whitley4e338752001-01-26 20:42:23 +0000530 char *tmp, *tmp1;
531 int len_found;
Erik Andersenf0657d32000-04-12 17:49:52 +0000532
533 /* For now, we will not bother with trying to distinguish
534 * whether the cursor is in/at a command extression -- we
Eric Andersen74c66ad2000-06-16 19:57:44 +0000535 * will always try all possible matches. If you don't like
Erik Andersenf0657d32000-04-12 17:49:52 +0000536 * that then feel free to fix it.
537 */
538
539 /* Make a local copy of the string -- up
540 * to the position of the cursor */
Mark Whitley4e338752001-01-26 20:42:23 +0000541 memset(matchBuf, 0, BUFSIZ);
542 tmp = strncpy(matchBuf, command_ps, cursor);
Erik Andersenf0657d32000-04-12 17:49:52 +0000543
544 /* skip past any command seperator tokens */
Mark Whitley4e338752001-01-26 20:42:23 +0000545 while ( (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
546 tmp = ++tmp1;
Erik Andersenf0657d32000-04-12 17:49:52 +0000547 }
548
549 /* skip any leading white space */
Mark Whitley4e338752001-01-26 20:42:23 +0000550 while (*tmp == ' ')
551 tmp++;
552
553 if(strncmp(tmp, "cd ", 3)==0)
554 find_type = FIND_DIR_ONLY;
555 else if(strchr(tmp, ' ')==NULL)
556 find_type = FIND_EXE_ONLY;
557
558 /* find begin curent word */
559 if( (tmp1=strrchr(tmp, ' ')) != NULL) {
560 tmp = ++tmp1;
561 }
562 strcpy(matchBuf, tmp);
Erik Andersenf0657d32000-04-12 17:49:52 +0000563
564 /* Free up any memory already allocated */
565 if (matches) {
Mark Whitley4e338752001-01-26 20:42:23 +0000566 while(num_matches>0)
567 free(matches[--num_matches]);
Erik Andersenf0657d32000-04-12 17:49:52 +0000568 free(matches);
569 matches = (char **) NULL;
570 }
571
Mark Whitley4e338752001-01-26 20:42:23 +0000572#ifdef BB_FEATURE_USERNAME_COMPLETION
Erik Andersenf0657d32000-04-12 17:49:52 +0000573 /* If the word starts with `~' and there is no slash in the word,
574 * then try completing this word as a username. */
575
Mark Whitley4e338752001-01-26 20:42:23 +0000576 if (matchBuf[0]=='~' && strchr(matchBuf, '/')==0) {
577 matches = username_tab_completion(matchBuf, &num_matches);
578 }
579#endif
Erik Andersenf0657d32000-04-12 17:49:52 +0000580 /* Try to match any executable in our path and everything
581 * in the current working directory that matches. */
582 if (!matches)
Mark Whitley4e338752001-01-26 20:42:23 +0000583 matches = exe_n_cwd_tab_completion(matchBuf, &num_matches, find_type);
Erik Andersenf0657d32000-04-12 17:49:52 +0000584
585 /* Did we find exactly one match? */
Mark Whitley4e338752001-01-26 20:42:23 +0000586 if(!matches || num_matches>1) {
587 beep();
588 return;
589 }
590
591 len_found = strlen(matches[0]);
592
593 /* have space to placed match? */
594 if ( (len_found-strlen(matchBuf)+len) < BUFSIZ ) {
595
596 int recalc_pos = len;
597
598 /* before word for match */
599 command_ps[pos-strlen(matchBuf)]=0;
600
601 /* tail line */
602 strcpy(matchBuf, command_ps+pos);
603
604 /* add match */
605 strcat(command_ps, matches[0]);
606 /* add tail */
607 strcat(command_ps, matchBuf);
608
Erik Andersenf0657d32000-04-12 17:49:52 +0000609 /* write out the matched command */
Mark Whitley4e338752001-01-26 20:42:23 +0000610 len=strlen(command_ps);
611 recalc_pos = len-recalc_pos+pos;
612 input_end(); /* write */
613 while(recalc_pos<cursor)
614 input_backward();
Eric Andersena75466e2000-11-02 17:02:26 +0000615 return;
Erik Andersenf0657d32000-04-12 17:49:52 +0000616 }
617 } else {
618 /* Ok -- the last char was a TAB. Since they
619 * just hit TAB again, print a list of all the
620 * available choices... */
621 if ( matches && num_matches>0 ) {
622 int i, col;
Mark Whitley4e338752001-01-26 20:42:23 +0000623 int sav_cursor = cursor;
Erik Andersenf0657d32000-04-12 17:49:52 +0000624
625 /* Go to the next line */
Mark Whitley4e338752001-01-26 20:42:23 +0000626 goto_new_line();
Erik Andersenf0657d32000-04-12 17:49:52 +0000627 for (i=0,col=0; i<num_matches; i++) {
Mark Whitley4e338752001-01-26 20:42:23 +0000628 printf("%s ", matches[i]);
629 col += strlen(matches[i])+2;
630 col -= (col/cmdedit_termw)*cmdedit_termw;
Erik Andersenf0657d32000-04-12 17:49:52 +0000631 if (col > 60 && matches[i+1] != NULL) {
Mark Whitley4e338752001-01-26 20:42:23 +0000632 putchar('\n');
Erik Andersenf0657d32000-04-12 17:49:52 +0000633 col = 0;
634 }
635 }
Mark Whitley4e338752001-01-26 20:42:23 +0000636 /* Go to the next line and rewrite the prompt */
637 printf("\n%s", cmdedit_prompt);
638 cmdedit_x = cmdedit_prmt_len;
639 cmdedit_y = 0;
640 cursor = 0;
641 input_end(); /* Rewrite the command */
Erik Andersenf0657d32000-04-12 17:49:52 +0000642 /* Put the cursor back to where it used to be */
Mark Whitley4e338752001-01-26 20:42:23 +0000643 while (sav_cursor < cursor)
644 input_backward();
Erik Andersenf0657d32000-04-12 17:49:52 +0000645 }
646 }
647}
648#endif
649
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000650static void get_previous_history(struct history **hp, char* command)
Erik Andersenf0657d32000-04-12 17:49:52 +0000651{
Eric Andersen91a44002000-07-19 17:37:57 +0000652 if ((*hp)->s)
653 free((*hp)->s);
Erik Andersenf0657d32000-04-12 17:49:52 +0000654 (*hp)->s = strdup(command);
655 *hp = (*hp)->p;
656}
657
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000658static void get_next_history(struct history **hp, char* command)
Erik Andersenf0657d32000-04-12 17:49:52 +0000659{
Eric Andersen91a44002000-07-19 17:37:57 +0000660 if ((*hp)->s)
661 free((*hp)->s);
Erik Andersenf0657d32000-04-12 17:49:52 +0000662 (*hp)->s = strdup(command);
663 *hp = (*hp)->n;
Erik Andersenf0657d32000-04-12 17:49:52 +0000664}
665
Erik Andersen6273f652000-03-17 01:12:41 +0000666/*
667 * This function is used to grab a character buffer
668 * from the input file descriptor and allows you to
669 * a string with full command editing (sortof like
670 * a mini readline).
671 *
672 * The following standard commands are not implemented:
673 * ESC-b -- Move back one word
674 * ESC-f -- Move forward one word
675 * ESC-d -- Delete back one word
676 * ESC-h -- Delete forward one word
677 * CTL-t -- Transpose two characters
678 *
679 * Furthermore, the "vi" command editing keys are not implemented.
680 *
681 * TODO: implement TAB command completion. :)
Erik Andersen6273f652000-03-17 01:12:41 +0000682 */
Erik Andersenf0657d32000-04-12 17:49:52 +0000683extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
Erik Andersen13456d12000-03-16 08:09:57 +0000684{
685
Erik Andersenf0657d32000-04-12 17:49:52 +0000686 int inputFd=fileno(stdin);
Mark Whitley4e338752001-01-26 20:42:23 +0000687
Erik Andersenc7c634b2000-03-19 05:28:55 +0000688 int j = 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000689 int break_out = 0;
690 int ret = 0;
691 int lastWasTab = FALSE;
692 char c = 0;
693 struct history *hp = his_end;
Erik Andersen13456d12000-03-16 08:09:57 +0000694
Mark Whitley4e338752001-01-26 20:42:23 +0000695 len = 0;
696 cursor = 0;
697 command_ps = command;
698
699 if (new_settings.c_cc[VMIN]==0) {
Erik Andersen1d1d9502000-04-21 01:26:49 +0000700
701 getTermSettings(inputFd, (void*) &initial_settings);
702 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
703 new_settings.c_cc[VMIN] = 1;
704 new_settings.c_cc[VTIME] = 0;
705 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */
706 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
707 new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000708 }
Erik Andersen1d1d9502000-04-21 01:26:49 +0000709 setTermSettings(inputFd, (void*) &new_settings);
Mark Whitley4e338752001-01-26 20:42:23 +0000710 handlers_sets |= SET_RESET_TERM;
Erik Andersen13456d12000-03-16 08:09:57 +0000711
Erik Andersenf0657d32000-04-12 17:49:52 +0000712 memset(command, 0, BUFSIZ);
Erik Andersen13456d12000-03-16 08:09:57 +0000713
Mark Whitley4e338752001-01-26 20:42:23 +0000714 cmdedit_init();
715
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000716 /* Print out the command prompt */
Mark Whitley4e338752001-01-26 20:42:23 +0000717 cmdedit_prompt = prompt;
718 cmdedit_prmt_len = strlen(prompt);
719 printf("%s", prompt);
720 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
721 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
722
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000723
Erik Andersenc7c634b2000-03-19 05:28:55 +0000724 while (1) {
Erik Andersen6273f652000-03-17 01:12:41 +0000725
Mark Whitley4e338752001-01-26 20:42:23 +0000726 fflush(stdout); /* buffered out to fast */
727
Erik Andersen13456d12000-03-16 08:09:57 +0000728 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000729 return;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000730 //fprintf(stderr, "got a '%c' (%d)\n", c, c);
Erik Andersenf3b3d172000-04-09 18:24:05 +0000731
Erik Andersen13456d12000-03-16 08:09:57 +0000732 switch (c) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000733 case '\n':
734 case '\r':
735 /* Enter */
Mark Whitley4e338752001-01-26 20:42:23 +0000736 *(command + len) = c;
737 len++;
738 input_end ();
Erik Andersenf0657d32000-04-12 17:49:52 +0000739 break_out = 1;
740 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000741 case 1:
742 /* Control-a -- Beginning of line */
Mark Whitley4e338752001-01-26 20:42:23 +0000743 input_home();
744 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000745 case 2:
746 /* Control-b -- Move back one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000747 input_backward();
Erik Andersenf0657d32000-04-12 17:49:52 +0000748 break;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000749 case 3:
Eric Andersen86349772000-12-18 20:25:50 +0000750 /* Control-c -- stop gathering input */
751
752 /* Link into lash to reset context to 0 on ^C and such */
753 shell_context = 0;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000754
755 /* Go to the next line */
Mark Whitley4e338752001-01-26 20:42:23 +0000756 goto_new_line();
Erik Andersen1d1d9502000-04-21 01:26:49 +0000757
Eric Andersen86349772000-12-18 20:25:50 +0000758#if 0
Erik Andersen1d1d9502000-04-21 01:26:49 +0000759 /* Rewrite the prompt */
Mark Whitley4e338752001-01-26 20:42:23 +0000760 printf("%s", prompt);
Erik Andersen1d1d9502000-04-21 01:26:49 +0000761
762 /* Reset the command string */
Eric Andersen4ac6cb52000-07-14 01:13:37 +0000763 memset(command, 0, BUFSIZ);
Erik Andersen1d1d9502000-04-21 01:26:49 +0000764 len = cursor = 0;
Eric Andersen86349772000-12-18 20:25:50 +0000765#endif
766 return;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000767
Erik Andersenf0657d32000-04-12 17:49:52 +0000768 case 4:
769 /* Control-d -- Delete one character, or exit
770 * if the len=0 and no chars to delete */
771 if (len == 0) {
Mark Whitley4e338752001-01-26 20:42:23 +0000772 printf("exit");
Erik Andersenf0657d32000-04-12 17:49:52 +0000773 clean_up_and_die(0);
774 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000775 input_delete();
Erik Andersenf0657d32000-04-12 17:49:52 +0000776 }
777 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000778 case 5:
779 /* Control-e -- End of line */
Mark Whitley4e338752001-01-26 20:42:23 +0000780 input_end();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000781 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000782 case 6:
783 /* Control-f -- Move forward one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000784 input_forward();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000785 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000786 case '\b':
787 case DEL:
Erik Andersen1d1d9502000-04-21 01:26:49 +0000788 /* Control-h and DEL */
Mark Whitley4e338752001-01-26 20:42:23 +0000789 input_backspace();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000790 break;
791 case '\t':
Erik Andersena2685732000-04-09 18:27:46 +0000792#ifdef BB_FEATURE_SH_TAB_COMPLETION
Mark Whitley4e338752001-01-26 20:42:23 +0000793 input_tab(lastWasTab);
Erik Andersena2685732000-04-09 18:27:46 +0000794#endif
Erik Andersenc7c634b2000-03-19 05:28:55 +0000795 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000796 case 14:
797 /* Control-n -- Get next command in history */
798 if (hp && hp->n && hp->n->s) {
799 get_next_history(&hp, command);
800 goto rewrite_line;
801 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000802 beep();
Erik Andersenf0657d32000-04-12 17:49:52 +0000803 }
804 break;
805 case 16:
806 /* Control-p -- Get previous command from history */
807 if (hp && hp->p) {
808 get_previous_history(&hp, command);
809 goto rewrite_line;
810 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000811 beep();
Erik Andersenf0657d32000-04-12 17:49:52 +0000812 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000813 break;
814 case ESC:{
815 /* escape sequence follows */
816 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000817 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000818
819 if (c == '[') { /* 91 */
820 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000821 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000822
823 switch (c) {
824 case 'A':
Erik Andersenf0657d32000-04-12 17:49:52 +0000825 /* Up Arrow -- Get previous command from history */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000826 if (hp && hp->p) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000827 get_previous_history(&hp, command);
828 goto rewrite_line;
829 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000830 beep();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000831 }
832 break;
833 case 'B':
Erik Andersenf0657d32000-04-12 17:49:52 +0000834 /* Down Arrow -- Get next command in history */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000835 if (hp && hp->n && hp->n->s) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000836 get_next_history(&hp, command);
837 goto rewrite_line;
838 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000839 beep();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000840 }
841 break;
842
Erik Andersenf0657d32000-04-12 17:49:52 +0000843 /* Rewrite the line with the selected history item */
844 rewrite_line:
Mark Whitley4e338752001-01-26 20:42:23 +0000845 /* return to begin of line */
846 input_home ();
847 /* for next memmoves without set '\0' */
848 memset (command, 0, BUFSIZ);
849 /* change command */
850 strcpy (command, hp->s);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000851 /* write new command */
Mark Whitley4e338752001-01-26 20:42:23 +0000852 for (j=0; command[j]; j++)
853 cmdedit_set_out_char(command[j], 0);
854 ret = cursor;
855 /* erase tail if required */
856 for (j = ret; j < len; j++)
857 cmdedit_set_out_char(' ', 0);
858 /* and backward cursor */
859 for (j = ret; j < len; j++)
860 input_backward();
861 len = cursor; /* set new len */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000862 break;
863 case 'C':
864 /* Right Arrow -- Move forward one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000865 input_forward();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000866 break;
867 case 'D':
868 /* Left Arrow -- Move back one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000869 input_backward();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000870 break;
871 case '3':
872 /* Delete */
Mark Whitley4e338752001-01-26 20:42:23 +0000873 input_delete();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000874 break;
875 case '1':
876 /* Home (Ctrl-A) */
Mark Whitley4e338752001-01-26 20:42:23 +0000877 input_home();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000878 break;
879 case '4':
880 /* End (Ctrl-E) */
Mark Whitley4e338752001-01-26 20:42:23 +0000881 input_end();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000882 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000883 default:
Mark Whitley4e338752001-01-26 20:42:23 +0000884 beep();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000885 }
886 if (c == '1' || c == '3' || c == '4')
887 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000888 return; /* read 126 (~) */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000889 }
890 if (c == 'O') {
891 /* 79 */
892 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000893 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000894 switch (c) {
895 case 'H':
896 /* Home (xterm) */
Mark Whitley4e338752001-01-26 20:42:23 +0000897 input_home();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000898 break;
899 case 'F':
900 /* End (xterm) */
Mark Whitley4e338752001-01-26 20:42:23 +0000901 input_end();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000902 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000903 default:
Mark Whitley4e338752001-01-26 20:42:23 +0000904 beep();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000905 }
906 }
907 c = 0;
908 break;
909 }
910
911 default: /* If it's regular input, do the normal thing */
912
Erik Andersenf0657d32000-04-12 17:49:52 +0000913 if (!isprint(c)) { /* Skip non-printable characters */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000914 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000915 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000916
917 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
918 break;
919
920 len++;
921
922 if (cursor == (len - 1)) { /* Append if at the end of the line */
Erik Andersenf0657d32000-04-12 17:49:52 +0000923 *(command + cursor) = c;
Mark Whitley4e338752001-01-26 20:42:23 +0000924 cmdedit_set_out_char(c, command[cursor+1]);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000925 } else { /* Insert otherwise */
Erik Andersenf0657d32000-04-12 17:49:52 +0000926 memmove(command + cursor + 1, command + cursor,
Erik Andersenc7c634b2000-03-19 05:28:55 +0000927 len - cursor - 1);
928
Erik Andersenf0657d32000-04-12 17:49:52 +0000929 *(command + cursor) = c;
Mark Whitley4e338752001-01-26 20:42:23 +0000930 j = cursor+1;
931 /* rewrite from cursor */
932 input_end ();
933 /* to prev x pos + 1 */
934 while(cursor > j)
935 input_backward();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000936 }
937
Erik Andersenc7c634b2000-03-19 05:28:55 +0000938 break;
Erik Andersen13456d12000-03-16 08:09:57 +0000939 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000940 if (c == '\t')
941 lastWasTab = TRUE;
942 else
943 lastWasTab = FALSE;
944
945 if (break_out) /* Enter is the command terminator, no more input. */
946 break;
947 }
948
Mark Whitley4e338752001-01-26 20:42:23 +0000949 setTermSettings (inputFd, (void *) &initial_settings);
950 handlers_sets &= ~SET_RESET_TERM;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000951
952 /* Handle command history log */
Mark Whitley4e338752001-01-26 20:42:23 +0000953 if (len>1) { /* no put empty line (only '\n') */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000954
955 struct history *h = his_end;
Mark Whitley4e338752001-01-26 20:42:23 +0000956 char *ss;
957
958 command[len-1] = 0; /* destroy end '\n' */
959 ss = strdup(command); /* duplicate without '\n' */
960 command[len-1] = '\n'; /* restore '\n' */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000961
962 if (!h) {
Eric Andersen91a44002000-07-19 17:37:57 +0000963 /* No previous history -- this memory is never freed */
Matt Kraai322ae932000-09-13 02:46:14 +0000964 h = his_front = xmalloc(sizeof(struct history));
965 h->n = xmalloc(sizeof(struct history));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000966
967 h->p = NULL;
Mark Whitley4e338752001-01-26 20:42:23 +0000968 h->s = ss;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000969 h->n->p = h;
970 h->n->n = NULL;
971 h->n->s = NULL;
972 his_end = h->n;
973 history_counter++;
974 } else {
Eric Andersen91a44002000-07-19 17:37:57 +0000975 /* Add a new history command -- this memory is never freed */
Matt Kraai322ae932000-09-13 02:46:14 +0000976 h->n = xmalloc(sizeof(struct history));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000977
978 h->n->p = h;
979 h->n->n = NULL;
980 h->n->s = NULL;
Mark Whitley4e338752001-01-26 20:42:23 +0000981 h->s = ss;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000982 his_end = h->n;
983
984 /* After max history, remove the oldest command */
985 if (history_counter >= MAX_HISTORY) {
986
987 struct history *p = his_front->n;
988
989 p->p = NULL;
990 free(his_front->s);
991 free(his_front);
992 his_front = p;
993 } else {
994 history_counter++;
995 }
Erik Andersen13456d12000-03-16 08:09:57 +0000996 }
Erik Andersen6273f652000-03-17 01:12:41 +0000997 }
Erik Andersen13456d12000-03-16 08:09:57 +0000998
Erik Andersenf0657d32000-04-12 17:49:52 +0000999 return;
Erik Andersen13456d12000-03-16 08:09:57 +00001000}
1001
Eric Andersenb3dc3b82001-01-04 11:08:45 +00001002
Mark Whitley4e338752001-01-26 20:42:23 +00001003/* Undo the effects of cmdedit_init(). */
Eric Andersen501c88b2000-07-28 15:14:45 +00001004extern void cmdedit_terminate(void)
1005{
1006 cmdedit_reset_term();
Mark Whitley4e338752001-01-26 20:42:23 +00001007 if((handlers_sets & SET_TERM_HANDLERS)!=0) {
1008 signal(SIGKILL, SIG_DFL);
1009 signal(SIGINT, SIG_DFL);
1010 signal(SIGQUIT, SIG_DFL);
1011 signal(SIGTERM, SIG_DFL);
1012 signal(SIGWINCH, SIG_DFL);
1013 handlers_sets &= ~SET_TERM_HANDLERS;
1014 }
Eric Andersen501c88b2000-07-28 15:14:45 +00001015}
1016
1017
1018
Mark Whitley4e338752001-01-26 20:42:23 +00001019#endif /* BB_FEATURE_SH_COMMAND_EDITING */