blob: 9c2facc7b0ad410a061bba24836c39e86fe0eedf [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
Mark Whitley59ab0252001-01-23 22:30:04 +000057static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */
Erik Andersen13456d12000-03-16 08:09:57 +000058
Mark Whitley59ab0252001-01-23 22:30:04 +000059enum {
60 ESC = 27,
61 DEL = 127,
62};
63
Erik Andersen6273f652000-03-17 01:12:41 +000064#define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0)
65#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
Erik Andersen13456d12000-03-16 08:09:57 +000066
67static struct history *his_front = NULL; /* First element in command line list */
68static struct history *his_end = NULL; /* Last element in command line list */
Erik Andersen1d1d9502000-04-21 01:26:49 +000069
70/* ED: sparc termios is broken: revert back to old termio handling. */
Erik Andersen1d1d9502000-04-21 01:26:49 +000071
72#if #cpu(sparc)
73# include <termio.h>
74# define termios termio
75# define setTermSettings(fd,argp) ioctl(fd,TCSETAF,argp)
76# define getTermSettings(fd,argp) ioctl(fd,TCGETA,argp)
77#else
78# include <termios.h>
79# define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
80# define getTermSettings(fd,argp) tcgetattr(fd, argp);
81#endif
82
83/* Current termio and the previous termio before starting sh */
Eric Andersen63a86222000-11-07 06:52:13 +000084static struct termios initial_settings, new_settings;
Erik Andersen8ea7d8c2000-05-20 00:40:08 +000085
86
87#ifndef _POSIX_VDISABLE
88#define _POSIX_VDISABLE '\0'
89#endif
90
Erik Andersen1d1d9502000-04-21 01:26:49 +000091
Mark Whitley4e338752001-01-26 20:42:23 +000092static
93volatile int cmdedit_termw; /* actual terminal width */
94static int history_counter = 0; /* Number of commands in history list */
Erik Andersen1d1d9502000-04-21 01:26:49 +000095
Mark Whitley4e338752001-01-26 20:42:23 +000096static
97volatile int handlers_sets = 0; /* Set next bites
98 when atexit() has been called
99 and set many "terminates" signal handlers
100 and winchg signal handler
101 and if the terminal needs to be reset upon exit
102 */
103enum {
104 SET_ATEXIT = 1,
105 SET_TERM_HANDLERS = 2,
106 SET_WCHG_HANDLERS = 4,
107 SET_RESET_TERM = 8,
108};
Erik Andersen13456d12000-03-16 08:09:57 +0000109
Eric Andersen86349772000-12-18 20:25:50 +0000110
Mark Whitley4e338752001-01-26 20:42:23 +0000111static int cmdedit_x; /* real x terminal position,
112 require put prompt in start x position */
113static int cmdedit_y; /* pseudoreal y terminal position */
114static int cmdedit_prmt_len; /* for fast running, without duplicate calculate */
115
116static int cursor; /* required global for signal handler */
117static int len; /* --- "" - - "" - -"- --""-- --""--- */
118static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */
119static const char *cmdedit_prompt;/* --- "" - - "" - -"- --""-- --""--- */
Eric Andersen86349772000-12-18 20:25:50 +0000120
121/* Link into lash to reset context to 0
122 * on ^C and such */
123extern unsigned int shell_context;
124
Erik Andersen13456d12000-03-16 08:09:57 +0000125
126struct history {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000127 char *s;
128 struct history *p;
129 struct history *n;
Erik Andersen13456d12000-03-16 08:09:57 +0000130};
131
Mark Whitley4e338752001-01-26 20:42:23 +0000132static void cmdedit_setwidth(int w, int redraw_flg);
Erik Andersen13456d12000-03-16 08:09:57 +0000133
Mark Whitley4e338752001-01-26 20:42:23 +0000134static void win_changed(int nsig)
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000135{
136 struct winsize win = { 0, 0, 0, 0 };
Mark Whitley4e338752001-01-26 20:42:23 +0000137 static __sighandler_t previous_SIGWINCH_handler; /* for reset */
Erik Andersen61677fe2000-04-13 01:18:56 +0000138
Eric Andersen4bbdd782001-01-30 22:23:17 +0000139 /* emulate signal call if not called as a sig handler */
Mark Whitley4e338752001-01-26 20:42:23 +0000140 if(nsig == -SIGWINCH || nsig == SIGWINCH) {
141 ioctl(0, TIOCGWINSZ, &win);
142 if (win.ws_col > 0) {
143 cmdedit_setwidth( win.ws_col, nsig == SIGWINCH );
Eric Andersen4bbdd782001-01-30 22:23:17 +0000144 }
Mark Whitley4e338752001-01-26 20:42:23 +0000145 }
Eric Andersen4bbdd782001-01-30 22:23:17 +0000146
147 /* Unix not all standart in recall signal */
Mark Whitley4e338752001-01-26 20:42:23 +0000148
Eric Andersen4bbdd782001-01-30 22:23:17 +0000149 if(nsig == -SIGWINCH) /* save previous handler */
Mark Whitley4e338752001-01-26 20:42:23 +0000150 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
151 else if(nsig == SIGWINCH) /* signaled called handler */
Eric Andersen4bbdd782001-01-30 22:23:17 +0000152 signal(SIGWINCH, win_changed); /* set for next call */
Mark Whitley4e338752001-01-26 20:42:23 +0000153 else /* set previous handler */
154 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
155}
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000156
157static void cmdedit_reset_term(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000158{
Mark Whitley4e338752001-01-26 20:42:23 +0000159 if((handlers_sets & SET_RESET_TERM)!=0) {
Erik Andersena6c75222000-04-18 00:00:52 +0000160 /* sparc and other have broken termios support: use old termio handling. */
Erik Andersen1d1d9502000-04-21 01:26:49 +0000161 setTermSettings(fileno(stdin), (void*) &initial_settings);
Mark Whitley4e338752001-01-26 20:42:23 +0000162 handlers_sets &= ~SET_RESET_TERM;
163 }
164 if((handlers_sets & SET_WCHG_HANDLERS)!=0) {
165 /* reset SIGWINCH handler to previous (default) */
166 win_changed(0);
167 handlers_sets &= ~SET_WCHG_HANDLERS;
168 }
169 fflush(stdout);
Eric Andersenb040d4f2000-07-25 18:01:20 +0000170#ifdef BB_FEATURE_CLEAN_UP
171 if (his_front) {
172 struct history *n;
173 //while(his_front!=his_end) {
174 while(his_front!=his_end) {
175 n = his_front->n;
176 free(his_front->s);
177 free(his_front);
178 his_front=n;
179 }
180 }
181#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000182}
183
Mark Whitley4e338752001-01-26 20:42:23 +0000184
185
186/* special for recount position for scroll and remove terminal margin effect */
187static void cmdedit_set_out_char(int c, int next_char) {
188 putchar(c);
189 if(++cmdedit_x>=cmdedit_termw) {
190 /* terminal is scrolled down */
191 cmdedit_y++;
192 cmdedit_x=0;
193
194 if(!next_char)
195 next_char = ' ';
196 /* destroy "(auto)margin" */
197 putchar(next_char);
198 putchar('\b');
199 }
200 cursor++;
Erik Andersen13456d12000-03-16 08:09:57 +0000201}
202
Mark Whitley4e338752001-01-26 20:42:23 +0000203/* Move to end line. Bonus: rewrite line from cursor without use
204 special control terminal strings, also saved size and speed! */
205static void input_end (void) {
206 while(cursor < len)
207 cmdedit_set_out_char(command_ps[cursor], 0);
208}
209
210/* Go to the next line */
211static void goto_new_line(void) {
212 input_end();
213 cmdedit_set_out_char('\n', 0);
214}
215
216
217static inline void out1str(const char *s) { fputs (s, stdout); }
218static inline void beep (void) { putchar('\007'); }
219
Erik Andersenf0657d32000-04-12 17:49:52 +0000220/* Go to HOME position */
Mark Whitley4e338752001-01-26 20:42:23 +0000221static void input_home(void)
Erik Andersenf0657d32000-04-12 17:49:52 +0000222{
Mark Whitley4e338752001-01-26 20:42:23 +0000223 while(cmdedit_y>0) { /* up to start y */
224 out1str("\033[A");
225 cmdedit_y--;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000226 }
Mark Whitley4e338752001-01-26 20:42:23 +0000227 putchar('\r');
228 cursor = 0;
229 out1str(cmdedit_prompt);
230 cmdedit_x = cmdedit_prmt_len;
231
Erik Andersen13456d12000-03-16 08:09:57 +0000232}
233
Mark Whitley4e338752001-01-26 20:42:23 +0000234/* Move back one charactor */
235static void input_backward(void) {
236 if (cursor > 0) {
237 cursor--;
238 if(cmdedit_x!=0) { /* no first position in terminal line */
239 putchar('\b');
240 cmdedit_x--;
241 }
242 else {
243 out1str("\033[A"); /* up */
244 cmdedit_y--;
Erik Andersen13456d12000-03-16 08:09:57 +0000245
Mark Whitley4e338752001-01-26 20:42:23 +0000246 /* to end in current terminal line */
247 while(cmdedit_x<(cmdedit_termw-1)) {
248 out1str("\033[C");
249 cmdedit_x++;
250 }
251 }
Erik Andersen13456d12000-03-16 08:09:57 +0000252 }
Erik Andersen13456d12000-03-16 08:09:57 +0000253}
254
Erik Andersenf0657d32000-04-12 17:49:52 +0000255/* Delete the char in front of the cursor */
Mark Whitley4e338752001-01-26 20:42:23 +0000256static void input_delete(void)
Erik Andersenf0657d32000-04-12 17:49:52 +0000257{
Mark Whitley4e338752001-01-26 20:42:23 +0000258 int j = cursor;
Erik Andersena2685732000-04-09 18:27:46 +0000259
Mark Whitley4e338752001-01-26 20:42:23 +0000260 if (j == len)
Erik Andersenf0657d32000-04-12 17:49:52 +0000261 return;
262
Mark Whitley4e338752001-01-26 20:42:23 +0000263 memmove (command_ps + j, command_ps + j + 1, BUFSIZ - j - 1);
264 len--;
265 input_end(); /* rewtite new line */
266 cmdedit_set_out_char(' ', 0); /* destroy end char */
267 while (j < cursor)
268 input_backward(); /* back to old pos cursor */
Erik Andersenf0657d32000-04-12 17:49:52 +0000269}
270
Mark Whitley4e338752001-01-26 20:42:23 +0000271/* Delete the char in back of the cursor */
272static void input_backspace(void)
273{
274 if (cursor > 0) {
275 input_backward();
276 input_delete ();
277 }
278}
279
280
Erik Andersenf0657d32000-04-12 17:49:52 +0000281/* Move forward one charactor */
Mark Whitley4e338752001-01-26 20:42:23 +0000282static void input_forward(void)
Erik Andersenf0657d32000-04-12 17:49:52 +0000283{
Mark Whitley4e338752001-01-26 20:42:23 +0000284 if (cursor < len)
285 cmdedit_set_out_char(command_ps[cursor], command_ps[cursor + 1]);
Erik Andersenf0657d32000-04-12 17:49:52 +0000286}
287
288
Mark Whitley4e338752001-01-26 20:42:23 +0000289static void clean_up_and_die(int sig)
290{
291 goto_new_line();
292 if (sig!=SIGINT)
293 exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */
294 cmdedit_reset_term();
295}
296
297static void cmdedit_setwidth(int w, int redraw_flg)
298{
299 cmdedit_termw = cmdedit_prmt_len+2;
300 if (w > cmdedit_termw) {
301
302 cmdedit_termw = w;
303
304 if(redraw_flg) {
305 int sav_cursor = cursor;
306
307 /* set variables for new terminal size */
308 cmdedit_y = sav_cursor/w;
309 cmdedit_x = sav_cursor-cmdedit_y*w;
310
311 /* redraw */
312 input_home();
313 input_end();
314 while(sav_cursor<cursor)
315 input_backward();
316 }
317 } else {
318 error_msg("\n*** Error: minimum screen width is %d\n", cmdedit_termw);
319 }
320}
321
322extern void cmdedit_init(void)
323{
324 if((handlers_sets & SET_WCHG_HANDLERS)==0) {
Eric Andersen4bbdd782001-01-30 22:23:17 +0000325 /* pretend we received a signal in order to set term size and sig handling */
Mark Whitley4e338752001-01-26 20:42:23 +0000326 win_changed(-SIGWINCH);
327 handlers_sets |= SET_WCHG_HANDLERS;
328 }
329
330 if((handlers_sets & SET_ATEXIT)==0) {
331 atexit(cmdedit_reset_term); /* be sure to do this only once */
332 handlers_sets |= SET_ATEXIT;
333 }
334 if((handlers_sets & SET_TERM_HANDLERS)==0) {
335 signal(SIGKILL, clean_up_and_die);
336 signal(SIGINT, clean_up_and_die);
337 signal(SIGQUIT, clean_up_and_die);
338 signal(SIGTERM, clean_up_and_die);
339 handlers_sets |= SET_TERM_HANDLERS;
340 }
341}
Erik Andersenf0657d32000-04-12 17:49:52 +0000342
343#ifdef BB_FEATURE_SH_TAB_COMPLETION
Mark Whitley4e338752001-01-26 20:42:23 +0000344
345#ifdef BB_FEATURE_USERNAME_COMPLETION
346static char** username_tab_completion(char *ud, int *num_matches)
Erik Andersen6273f652000-03-17 01:12:41 +0000347{
Mark Whitley4e338752001-01-26 20:42:23 +0000348 static struct passwd *entry;
349 int userlen;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000350 char **matches = (char **) NULL;
Mark Whitley4e338752001-01-26 20:42:23 +0000351 char *temp;
352 int nm = 0;
353
Eric Andersen4bbdd782001-01-30 22:23:17 +0000354 setpwent ();
Mark Whitley4e338752001-01-26 20:42:23 +0000355 userlen = strlen (ud + 1);
356
Eric Andersen4bbdd782001-01-30 22:23:17 +0000357 while ((entry = getpwent ()) != NULL) {
Mark Whitley4e338752001-01-26 20:42:23 +0000358 /* Null usernames should result in all users as possible completions. */
359 if (!userlen || !strncmp (ud + 1, entry->pw_name, userlen)) {
360
361 temp = xmalloc (3 + strlen (entry->pw_name));
362 sprintf(temp, "~%s/", entry->pw_name);
363
364 matches = xrealloc(matches, (nm+1)*sizeof(char *));
365 matches[nm++] = temp;
366 }
367 }
368
Eric Andersen4bbdd782001-01-30 22:23:17 +0000369 endpwent ();
Mark Whitley4e338752001-01-26 20:42:23 +0000370 (*num_matches) = nm;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000371 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000372}
Mark Whitley4e338752001-01-26 20:42:23 +0000373#endif
374
375enum {
376 FIND_EXE_ONLY = 0,
377 FIND_DIR_ONLY = 1,
378 FIND_FILE_ONLY = 2,
379};
Erik Andersen1dbe3402000-03-19 10:46:06 +0000380
381#include <dirent.h>
Mark Whitley4e338752001-01-26 20:42:23 +0000382
383static int path_parse(char ***p, int flags)
384{
385 int npth;
386 char *tmp;
387 char *pth;
388
389 if(flags!=FIND_EXE_ONLY || (pth=getenv("PATH"))==0) {
390 /* if not setenv PATH variable, to search cur dir "." */
391 (*p) = xmalloc(sizeof(char *));
392 (*p)[0] = xstrdup(".");
393 return 1;
394 }
395
396 tmp = pth;
397 npth=0;
398
399 for(;;) {
400 npth++; /* count words is + 1 count ':' */
401 tmp = strchr(tmp, ':');
402 if(tmp)
403 tmp++;
404 else
405 break;
406 }
407
408 *p = xmalloc(npth*sizeof(char *));
409
410 tmp = pth;
411 (*p)[0] = xstrdup(tmp);
412 npth=1; /* count words is + 1 count ':' */
413
414 for(;;) {
415 tmp = strchr(tmp, ':');
416 if(tmp) {
417 (*p)[0][(tmp-pth)]=0; /* ':' -> '\0'*/
418 tmp++;
419 } else
420 break;
421 (*p)[npth++] = &(*p)[0][(tmp-pth)]; /* p[next]=p[0][&'\0'+1] */
422 }
423
424 return npth;
425}
426
427static char** exe_n_cwd_tab_completion(char* command, int *num_matches, int type)
Erik Andersen6273f652000-03-17 01:12:41 +0000428{
Erik Andersen1dbe3402000-03-19 10:46:06 +0000429 char *dirName;
Mark Whitley4e338752001-01-26 20:42:23 +0000430 char **matches = 0;
Erik Andersen1dbe3402000-03-19 10:46:06 +0000431 DIR *dir;
432 struct dirent *next;
Mark Whitley4e338752001-01-26 20:42:23 +0000433 char cmd [BUFSIZ+4];
434 char *dirbuf;
435 char found [BUFSIZ+4];
436 int nm = *num_matches;
437 struct stat st;
438 char **paths;
439 int npaths;
440 int i;
441 char full_pth[BUFSIZ+4+PATH_MAX];
442
443
444 strcpy(cmd, command); /* save for change (last '/' to '\0') */
445
446 dirName = strrchr(cmd, '/');
447 if(dirName==NULL) {
448 /* no dir, if flags==EXE_ONLY - get paths, else "." */
449 npaths = path_parse(&paths, type);
450 if(npaths==0)
451 return 0;
452 } else {
453 /* with dir */
454
455 /* save dir */
456 dirbuf = xstrdup(cmd);
457 /* set only dirname */
458 dirbuf[(dirName-cmd)+1]=0;
459
460 /* strip dirname in cmd */
461 strcpy(cmd, dirName+1);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000462
Mark Whitley4e338752001-01-26 20:42:23 +0000463 paths = xmalloc(sizeof(char*));
464 paths[0] = dirbuf;
465 npaths = 1; /* only 1 dir */
466 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000467
Mark Whitley4e338752001-01-26 20:42:23 +0000468 for(i=0; i < npaths; i++) {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000469
Mark Whitley4e338752001-01-26 20:42:23 +0000470 dir = opendir(paths[i]);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000471 if (!dir) {
472 /* Don't print an error, just shut up and return */
Erik Andersen1dbe3402000-03-19 10:46:06 +0000473 return (matches);
474 }
475 while ((next = readdir(dir)) != NULL) {
Mark Whitley4e338752001-01-26 20:42:23 +0000476 /* matched ? */
477 if(strncmp(next->d_name, cmd, strlen(cmd)))
478 continue;
479 /* not see .name without .match */
480 if(*next->d_name == '.' && *cmd != '.')
481 continue;
482 sprintf(full_pth, "%s/%s", paths[i], next->d_name);
483 /* hmm, remover in progress? */
484 if(stat(full_pth, &st)<0)
485 continue;
486 /* Cool, found a match. */
487 if (S_ISDIR(st.st_mode)) {
488 /* name is directory */
489 strcpy(found, next->d_name);
490 strcat(found, "/");
491 if(type==FIND_DIR_ONLY)
492 strcat(found, " ");
493 } else {
494 /* not put found file if search only dirs for cd */
495 if(type==FIND_DIR_ONLY)
Erik Andersen1dbe3402000-03-19 10:46:06 +0000496 continue;
Mark Whitley4e338752001-01-26 20:42:23 +0000497 strcpy(found, next->d_name);
498 strcat(found, " ");
Erik Andersen1dbe3402000-03-19 10:46:06 +0000499 }
Mark Whitley4e338752001-01-26 20:42:23 +0000500 /* Add it to the list */
501 matches = xrealloc(matches, (nm+1)*sizeof(char *));
502 matches[nm++] = xstrdup(found);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000503 }
504 }
Mark Whitley4e338752001-01-26 20:42:23 +0000505 free(paths[0]); /* allocate memory only in first member */
506 free(paths);
507 *num_matches = nm;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000508 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000509}
Erik Andersenf0657d32000-04-12 17:49:52 +0000510
Mark Whitley4e338752001-01-26 20:42:23 +0000511static void input_tab(int lastWasTab)
Erik Andersenf0657d32000-04-12 17:49:52 +0000512{
513 /* Do TAB completion */
Mark Whitley4e338752001-01-26 20:42:23 +0000514 static int num_matches;
515 static char **matches;
516
517 char matchBuf[BUFSIZ];
518
519 int pos = cursor;
520 int find_type=FIND_FILE_ONLY;
Erik Andersenf0657d32000-04-12 17:49:52 +0000521
522
523 if (lastWasTab == FALSE) {
Mark Whitley4e338752001-01-26 20:42:23 +0000524 char *tmp, *tmp1;
525 int len_found;
Erik Andersenf0657d32000-04-12 17:49:52 +0000526
527 /* For now, we will not bother with trying to distinguish
528 * whether the cursor is in/at a command extression -- we
Eric Andersen74c66ad2000-06-16 19:57:44 +0000529 * will always try all possible matches. If you don't like
Erik Andersenf0657d32000-04-12 17:49:52 +0000530 * that then feel free to fix it.
531 */
532
533 /* Make a local copy of the string -- up
534 * to the position of the cursor */
Mark Whitley4e338752001-01-26 20:42:23 +0000535 memset(matchBuf, 0, BUFSIZ);
536 tmp = strncpy(matchBuf, command_ps, cursor);
Erik Andersenf0657d32000-04-12 17:49:52 +0000537
538 /* skip past any command seperator tokens */
Mark Whitley4e338752001-01-26 20:42:23 +0000539 while ( (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
540 tmp = ++tmp1;
Erik Andersenf0657d32000-04-12 17:49:52 +0000541 }
542
543 /* skip any leading white space */
Mark Whitley4e338752001-01-26 20:42:23 +0000544 while (*tmp == ' ')
545 tmp++;
546
547 if(strncmp(tmp, "cd ", 3)==0)
548 find_type = FIND_DIR_ONLY;
549 else if(strchr(tmp, ' ')==NULL)
550 find_type = FIND_EXE_ONLY;
551
552 /* find begin curent word */
553 if( (tmp1=strrchr(tmp, ' ')) != NULL) {
554 tmp = ++tmp1;
555 }
556 strcpy(matchBuf, tmp);
Erik Andersenf0657d32000-04-12 17:49:52 +0000557
558 /* Free up any memory already allocated */
559 if (matches) {
Mark Whitley4e338752001-01-26 20:42:23 +0000560 while(num_matches>0)
561 free(matches[--num_matches]);
Erik Andersenf0657d32000-04-12 17:49:52 +0000562 free(matches);
563 matches = (char **) NULL;
564 }
565
Mark Whitley4e338752001-01-26 20:42:23 +0000566#ifdef BB_FEATURE_USERNAME_COMPLETION
Erik Andersenf0657d32000-04-12 17:49:52 +0000567 /* If the word starts with `~' and there is no slash in the word,
568 * then try completing this word as a username. */
569
Mark Whitley4e338752001-01-26 20:42:23 +0000570 if (matchBuf[0]=='~' && strchr(matchBuf, '/')==0) {
571 matches = username_tab_completion(matchBuf, &num_matches);
572 }
573#endif
Erik Andersenf0657d32000-04-12 17:49:52 +0000574 /* Try to match any executable in our path and everything
575 * in the current working directory that matches. */
576 if (!matches)
Mark Whitley4e338752001-01-26 20:42:23 +0000577 matches = exe_n_cwd_tab_completion(matchBuf, &num_matches, find_type);
Erik Andersenf0657d32000-04-12 17:49:52 +0000578
579 /* Did we find exactly one match? */
Mark Whitley4e338752001-01-26 20:42:23 +0000580 if(!matches || num_matches>1) {
581 beep();
582 return;
583 }
584
585 len_found = strlen(matches[0]);
586
587 /* have space to placed match? */
588 if ( (len_found-strlen(matchBuf)+len) < BUFSIZ ) {
589
590 int recalc_pos = len;
591
592 /* before word for match */
593 command_ps[pos-strlen(matchBuf)]=0;
594
595 /* tail line */
596 strcpy(matchBuf, command_ps+pos);
597
598 /* add match */
599 strcat(command_ps, matches[0]);
600 /* add tail */
601 strcat(command_ps, matchBuf);
602
Erik Andersenf0657d32000-04-12 17:49:52 +0000603 /* write out the matched command */
Mark Whitley4e338752001-01-26 20:42:23 +0000604 len=strlen(command_ps);
605 recalc_pos = len-recalc_pos+pos;
606 input_end(); /* write */
607 while(recalc_pos<cursor)
608 input_backward();
Eric Andersena75466e2000-11-02 17:02:26 +0000609 return;
Erik Andersenf0657d32000-04-12 17:49:52 +0000610 }
611 } else {
612 /* Ok -- the last char was a TAB. Since they
613 * just hit TAB again, print a list of all the
614 * available choices... */
615 if ( matches && num_matches>0 ) {
616 int i, col;
Mark Whitley4e338752001-01-26 20:42:23 +0000617 int sav_cursor = cursor;
Erik Andersenf0657d32000-04-12 17:49:52 +0000618
619 /* Go to the next line */
Mark Whitley4e338752001-01-26 20:42:23 +0000620 goto_new_line();
Erik Andersenf0657d32000-04-12 17:49:52 +0000621 for (i=0,col=0; i<num_matches; i++) {
Mark Whitley4e338752001-01-26 20:42:23 +0000622 printf("%s ", matches[i]);
623 col += strlen(matches[i])+2;
624 col -= (col/cmdedit_termw)*cmdedit_termw;
Erik Andersenf0657d32000-04-12 17:49:52 +0000625 if (col > 60 && matches[i+1] != NULL) {
Mark Whitley4e338752001-01-26 20:42:23 +0000626 putchar('\n');
Erik Andersenf0657d32000-04-12 17:49:52 +0000627 col = 0;
628 }
629 }
Mark Whitley4e338752001-01-26 20:42:23 +0000630 /* Go to the next line and rewrite the prompt */
631 printf("\n%s", cmdedit_prompt);
632 cmdedit_x = cmdedit_prmt_len;
633 cmdedit_y = 0;
634 cursor = 0;
635 input_end(); /* Rewrite the command */
Erik Andersenf0657d32000-04-12 17:49:52 +0000636 /* Put the cursor back to where it used to be */
Mark Whitley4e338752001-01-26 20:42:23 +0000637 while (sav_cursor < cursor)
638 input_backward();
Erik Andersenf0657d32000-04-12 17:49:52 +0000639 }
640 }
641}
642#endif
643
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000644static void get_previous_history(struct history **hp, char* command)
Erik Andersenf0657d32000-04-12 17:49:52 +0000645{
Eric Andersen91a44002000-07-19 17:37:57 +0000646 if ((*hp)->s)
647 free((*hp)->s);
Erik Andersenf0657d32000-04-12 17:49:52 +0000648 (*hp)->s = strdup(command);
649 *hp = (*hp)->p;
650}
651
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000652static void get_next_history(struct history **hp, char* command)
Erik Andersenf0657d32000-04-12 17:49:52 +0000653{
Eric Andersen91a44002000-07-19 17:37:57 +0000654 if ((*hp)->s)
655 free((*hp)->s);
Erik Andersenf0657d32000-04-12 17:49:52 +0000656 (*hp)->s = strdup(command);
657 *hp = (*hp)->n;
Erik Andersenf0657d32000-04-12 17:49:52 +0000658}
659
Erik Andersen6273f652000-03-17 01:12:41 +0000660/*
661 * This function is used to grab a character buffer
662 * from the input file descriptor and allows you to
663 * a string with full command editing (sortof like
664 * a mini readline).
665 *
666 * The following standard commands are not implemented:
667 * ESC-b -- Move back one word
668 * ESC-f -- Move forward one word
669 * ESC-d -- Delete back one word
670 * ESC-h -- Delete forward one word
671 * CTL-t -- Transpose two characters
672 *
673 * Furthermore, the "vi" command editing keys are not implemented.
674 *
675 * TODO: implement TAB command completion. :)
Erik Andersen6273f652000-03-17 01:12:41 +0000676 */
Erik Andersenf0657d32000-04-12 17:49:52 +0000677extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
Erik Andersen13456d12000-03-16 08:09:57 +0000678{
679
Erik Andersenf0657d32000-04-12 17:49:52 +0000680 int inputFd=fileno(stdin);
Mark Whitley4e338752001-01-26 20:42:23 +0000681
Erik Andersenc7c634b2000-03-19 05:28:55 +0000682 int j = 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000683 int break_out = 0;
684 int ret = 0;
685 int lastWasTab = FALSE;
686 char c = 0;
687 struct history *hp = his_end;
Erik Andersen13456d12000-03-16 08:09:57 +0000688
Mark Whitley4e338752001-01-26 20:42:23 +0000689 len = 0;
690 cursor = 0;
691 command_ps = command;
692
693 if (new_settings.c_cc[VMIN]==0) {
Erik Andersen1d1d9502000-04-21 01:26:49 +0000694
695 getTermSettings(inputFd, (void*) &initial_settings);
696 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
697 new_settings.c_cc[VMIN] = 1;
698 new_settings.c_cc[VTIME] = 0;
699 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */
700 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
701 new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000702 }
Erik Andersen1d1d9502000-04-21 01:26:49 +0000703 setTermSettings(inputFd, (void*) &new_settings);
Mark Whitley4e338752001-01-26 20:42:23 +0000704 handlers_sets |= SET_RESET_TERM;
Erik Andersen13456d12000-03-16 08:09:57 +0000705
Erik Andersenf0657d32000-04-12 17:49:52 +0000706 memset(command, 0, BUFSIZ);
Erik Andersen13456d12000-03-16 08:09:57 +0000707
Mark Whitley4e338752001-01-26 20:42:23 +0000708 cmdedit_init();
709
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000710 /* Print out the command prompt */
Mark Whitley4e338752001-01-26 20:42:23 +0000711 cmdedit_prompt = prompt;
712 cmdedit_prmt_len = strlen(prompt);
713 printf("%s", prompt);
714 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
715 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
716
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000717
Erik Andersenc7c634b2000-03-19 05:28:55 +0000718 while (1) {
Erik Andersen6273f652000-03-17 01:12:41 +0000719
Mark Whitley4e338752001-01-26 20:42:23 +0000720 fflush(stdout); /* buffered out to fast */
721
Erik Andersen13456d12000-03-16 08:09:57 +0000722 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000723 return;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000724 //fprintf(stderr, "got a '%c' (%d)\n", c, c);
Erik Andersenf3b3d172000-04-09 18:24:05 +0000725
Erik Andersen13456d12000-03-16 08:09:57 +0000726 switch (c) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000727 case '\n':
728 case '\r':
729 /* Enter */
Mark Whitley4e338752001-01-26 20:42:23 +0000730 *(command + len) = c;
731 len++;
732 input_end ();
Erik Andersenf0657d32000-04-12 17:49:52 +0000733 break_out = 1;
734 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000735 case 1:
736 /* Control-a -- Beginning of line */
Mark Whitley4e338752001-01-26 20:42:23 +0000737 input_home();
738 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000739 case 2:
740 /* Control-b -- Move back one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000741 input_backward();
Erik Andersenf0657d32000-04-12 17:49:52 +0000742 break;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000743 case 3:
Eric Andersen86349772000-12-18 20:25:50 +0000744 /* Control-c -- stop gathering input */
745
746 /* Link into lash to reset context to 0 on ^C and such */
747 shell_context = 0;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000748
749 /* Go to the next line */
Mark Whitley4e338752001-01-26 20:42:23 +0000750 goto_new_line();
Erik Andersen1d1d9502000-04-21 01:26:49 +0000751
Eric Andersen86349772000-12-18 20:25:50 +0000752#if 0
Erik Andersen1d1d9502000-04-21 01:26:49 +0000753 /* Rewrite the prompt */
Mark Whitley4e338752001-01-26 20:42:23 +0000754 printf("%s", prompt);
Erik Andersen1d1d9502000-04-21 01:26:49 +0000755
756 /* Reset the command string */
Eric Andersen4ac6cb52000-07-14 01:13:37 +0000757 memset(command, 0, BUFSIZ);
Erik Andersen1d1d9502000-04-21 01:26:49 +0000758 len = cursor = 0;
Eric Andersen86349772000-12-18 20:25:50 +0000759#endif
760 return;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000761
Erik Andersenf0657d32000-04-12 17:49:52 +0000762 case 4:
763 /* Control-d -- Delete one character, or exit
764 * if the len=0 and no chars to delete */
765 if (len == 0) {
Mark Whitley4e338752001-01-26 20:42:23 +0000766 printf("exit");
Erik Andersenf0657d32000-04-12 17:49:52 +0000767 clean_up_and_die(0);
768 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000769 input_delete();
Erik Andersenf0657d32000-04-12 17:49:52 +0000770 }
771 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000772 case 5:
773 /* Control-e -- End of line */
Mark Whitley4e338752001-01-26 20:42:23 +0000774 input_end();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000775 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000776 case 6:
777 /* Control-f -- Move forward one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000778 input_forward();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000779 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000780 case '\b':
781 case DEL:
Erik Andersen1d1d9502000-04-21 01:26:49 +0000782 /* Control-h and DEL */
Mark Whitley4e338752001-01-26 20:42:23 +0000783 input_backspace();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000784 break;
785 case '\t':
Erik Andersena2685732000-04-09 18:27:46 +0000786#ifdef BB_FEATURE_SH_TAB_COMPLETION
Mark Whitley4e338752001-01-26 20:42:23 +0000787 input_tab(lastWasTab);
Erik Andersena2685732000-04-09 18:27:46 +0000788#endif
Erik Andersenc7c634b2000-03-19 05:28:55 +0000789 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000790 case 14:
791 /* Control-n -- Get next command in history */
792 if (hp && hp->n && hp->n->s) {
793 get_next_history(&hp, command);
794 goto rewrite_line;
795 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000796 beep();
Erik Andersenf0657d32000-04-12 17:49:52 +0000797 }
798 break;
799 case 16:
800 /* Control-p -- Get previous command from history */
801 if (hp && hp->p) {
802 get_previous_history(&hp, command);
803 goto rewrite_line;
804 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000805 beep();
Erik Andersenf0657d32000-04-12 17:49:52 +0000806 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000807 break;
808 case ESC:{
809 /* escape sequence follows */
810 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000811 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000812
813 if (c == '[') { /* 91 */
814 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000815 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000816
817 switch (c) {
818 case 'A':
Erik Andersenf0657d32000-04-12 17:49:52 +0000819 /* Up Arrow -- Get previous command from history */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000820 if (hp && hp->p) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000821 get_previous_history(&hp, command);
822 goto rewrite_line;
823 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000824 beep();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000825 }
826 break;
827 case 'B':
Erik Andersenf0657d32000-04-12 17:49:52 +0000828 /* Down Arrow -- Get next command in history */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000829 if (hp && hp->n && hp->n->s) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000830 get_next_history(&hp, command);
831 goto rewrite_line;
832 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000833 beep();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000834 }
835 break;
836
Erik Andersenf0657d32000-04-12 17:49:52 +0000837 /* Rewrite the line with the selected history item */
838 rewrite_line:
Mark Whitley4e338752001-01-26 20:42:23 +0000839 /* return to begin of line */
840 input_home ();
841 /* for next memmoves without set '\0' */
842 memset (command, 0, BUFSIZ);
843 /* change command */
844 strcpy (command, hp->s);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000845 /* write new command */
Mark Whitley4e338752001-01-26 20:42:23 +0000846 for (j=0; command[j]; j++)
847 cmdedit_set_out_char(command[j], 0);
848 ret = cursor;
849 /* erase tail if required */
850 for (j = ret; j < len; j++)
851 cmdedit_set_out_char(' ', 0);
852 /* and backward cursor */
853 for (j = ret; j < len; j++)
854 input_backward();
855 len = cursor; /* set new len */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000856 break;
857 case 'C':
858 /* Right Arrow -- Move forward one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000859 input_forward();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000860 break;
861 case 'D':
862 /* Left Arrow -- Move back one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000863 input_backward();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000864 break;
865 case '3':
866 /* Delete */
Mark Whitley4e338752001-01-26 20:42:23 +0000867 input_delete();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000868 break;
869 case '1':
870 /* Home (Ctrl-A) */
Mark Whitley4e338752001-01-26 20:42:23 +0000871 input_home();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000872 break;
873 case '4':
874 /* End (Ctrl-E) */
Mark Whitley4e338752001-01-26 20:42:23 +0000875 input_end();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000876 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000877 default:
Mark Whitley4e338752001-01-26 20:42:23 +0000878 beep();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000879 }
880 if (c == '1' || c == '3' || c == '4')
881 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000882 return; /* read 126 (~) */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000883 }
884 if (c == 'O') {
885 /* 79 */
886 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000887 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000888 switch (c) {
889 case 'H':
890 /* Home (xterm) */
Mark Whitley4e338752001-01-26 20:42:23 +0000891 input_home();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000892 break;
893 case 'F':
894 /* End (xterm) */
Mark Whitley4e338752001-01-26 20:42:23 +0000895 input_end();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000896 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000897 default:
Mark Whitley4e338752001-01-26 20:42:23 +0000898 beep();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000899 }
900 }
901 c = 0;
902 break;
903 }
904
905 default: /* If it's regular input, do the normal thing */
906
Erik Andersenf0657d32000-04-12 17:49:52 +0000907 if (!isprint(c)) { /* Skip non-printable characters */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000908 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000909 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000910
911 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
912 break;
913
914 len++;
915
916 if (cursor == (len - 1)) { /* Append if at the end of the line */
Erik Andersenf0657d32000-04-12 17:49:52 +0000917 *(command + cursor) = c;
Mark Whitley4e338752001-01-26 20:42:23 +0000918 cmdedit_set_out_char(c, command[cursor+1]);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000919 } else { /* Insert otherwise */
Erik Andersenf0657d32000-04-12 17:49:52 +0000920 memmove(command + cursor + 1, command + cursor,
Erik Andersenc7c634b2000-03-19 05:28:55 +0000921 len - cursor - 1);
922
Erik Andersenf0657d32000-04-12 17:49:52 +0000923 *(command + cursor) = c;
Mark Whitley4e338752001-01-26 20:42:23 +0000924 j = cursor+1;
925 /* rewrite from cursor */
926 input_end ();
927 /* to prev x pos + 1 */
928 while(cursor > j)
929 input_backward();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000930 }
931
Erik Andersenc7c634b2000-03-19 05:28:55 +0000932 break;
Erik Andersen13456d12000-03-16 08:09:57 +0000933 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000934 if (c == '\t')
935 lastWasTab = TRUE;
936 else
937 lastWasTab = FALSE;
938
939 if (break_out) /* Enter is the command terminator, no more input. */
940 break;
941 }
942
Mark Whitley4e338752001-01-26 20:42:23 +0000943 setTermSettings (inputFd, (void *) &initial_settings);
944 handlers_sets &= ~SET_RESET_TERM;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000945
946 /* Handle command history log */
Mark Whitley4e338752001-01-26 20:42:23 +0000947 if (len>1) { /* no put empty line (only '\n') */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000948
949 struct history *h = his_end;
Mark Whitley4e338752001-01-26 20:42:23 +0000950 char *ss;
951
952 command[len-1] = 0; /* destroy end '\n' */
953 ss = strdup(command); /* duplicate without '\n' */
954 command[len-1] = '\n'; /* restore '\n' */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000955
956 if (!h) {
Eric Andersen91a44002000-07-19 17:37:57 +0000957 /* No previous history -- this memory is never freed */
Matt Kraai322ae932000-09-13 02:46:14 +0000958 h = his_front = xmalloc(sizeof(struct history));
959 h->n = xmalloc(sizeof(struct history));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000960
961 h->p = NULL;
Mark Whitley4e338752001-01-26 20:42:23 +0000962 h->s = ss;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000963 h->n->p = h;
964 h->n->n = NULL;
965 h->n->s = NULL;
966 his_end = h->n;
967 history_counter++;
968 } else {
Eric Andersen91a44002000-07-19 17:37:57 +0000969 /* Add a new history command -- this memory is never freed */
Matt Kraai322ae932000-09-13 02:46:14 +0000970 h->n = xmalloc(sizeof(struct history));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000971
972 h->n->p = h;
973 h->n->n = NULL;
974 h->n->s = NULL;
Mark Whitley4e338752001-01-26 20:42:23 +0000975 h->s = ss;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000976 his_end = h->n;
977
978 /* After max history, remove the oldest command */
979 if (history_counter >= MAX_HISTORY) {
980
981 struct history *p = his_front->n;
982
983 p->p = NULL;
984 free(his_front->s);
985 free(his_front);
986 his_front = p;
987 } else {
988 history_counter++;
989 }
Erik Andersen13456d12000-03-16 08:09:57 +0000990 }
Erik Andersen6273f652000-03-17 01:12:41 +0000991 }
Erik Andersen13456d12000-03-16 08:09:57 +0000992
Erik Andersenf0657d32000-04-12 17:49:52 +0000993 return;
Erik Andersen13456d12000-03-16 08:09:57 +0000994}
995
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000996
Mark Whitley4e338752001-01-26 20:42:23 +0000997/* Undo the effects of cmdedit_init(). */
Eric Andersen501c88b2000-07-28 15:14:45 +0000998extern void cmdedit_terminate(void)
999{
1000 cmdedit_reset_term();
Mark Whitley4e338752001-01-26 20:42:23 +00001001 if((handlers_sets & SET_TERM_HANDLERS)!=0) {
1002 signal(SIGKILL, SIG_DFL);
1003 signal(SIGINT, SIG_DFL);
1004 signal(SIGQUIT, SIG_DFL);
1005 signal(SIGTERM, SIG_DFL);
1006 signal(SIGWINCH, SIG_DFL);
1007 handlers_sets &= ~SET_TERM_HANDLERS;
1008 }
Eric Andersen501c88b2000-07-28 15:14:45 +00001009}
1010
1011
1012
Mark Whitley4e338752001-01-26 20:42:23 +00001013#endif /* BB_FEATURE_SH_COMMAND_EDITING */