blob: ed441d39406b790f488dd8f3d49982ceac168f42 [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 Andersen306f4fe2001-01-31 00:30:45 +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 Andersen306f4fe2001-01-31 00:30:45 +0000144 } else {
145 /* Default to 79 if their console doesn't want to share */
146 cmdedit_setwidth( 79, nsig == SIGWINCH );
Eric Andersen4bbdd782001-01-30 22:23:17 +0000147 }
Mark Whitley4e338752001-01-26 20:42:23 +0000148 }
Eric Andersen4bbdd782001-01-30 22:23:17 +0000149
150 /* Unix not all standart in recall signal */
Mark Whitley4e338752001-01-26 20:42:23 +0000151
Eric Andersen4bbdd782001-01-30 22:23:17 +0000152 if(nsig == -SIGWINCH) /* save previous handler */
Mark Whitley4e338752001-01-26 20:42:23 +0000153 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
154 else if(nsig == SIGWINCH) /* signaled called handler */
Eric Andersen4bbdd782001-01-30 22:23:17 +0000155 signal(SIGWINCH, win_changed); /* set for next call */
Mark Whitley4e338752001-01-26 20:42:23 +0000156 else /* set previous handler */
157 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
158}
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000159
160static void cmdedit_reset_term(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000161{
Mark Whitley4e338752001-01-26 20:42:23 +0000162 if((handlers_sets & SET_RESET_TERM)!=0) {
Erik Andersena6c75222000-04-18 00:00:52 +0000163 /* sparc and other have broken termios support: use old termio handling. */
Erik Andersen1d1d9502000-04-21 01:26:49 +0000164 setTermSettings(fileno(stdin), (void*) &initial_settings);
Mark Whitley4e338752001-01-26 20:42:23 +0000165 handlers_sets &= ~SET_RESET_TERM;
166 }
167 if((handlers_sets & SET_WCHG_HANDLERS)!=0) {
168 /* reset SIGWINCH handler to previous (default) */
169 win_changed(0);
170 handlers_sets &= ~SET_WCHG_HANDLERS;
171 }
172 fflush(stdout);
Eric Andersenb040d4f2000-07-25 18:01:20 +0000173#ifdef BB_FEATURE_CLEAN_UP
174 if (his_front) {
175 struct history *n;
176 //while(his_front!=his_end) {
177 while(his_front!=his_end) {
178 n = his_front->n;
179 free(his_front->s);
180 free(his_front);
181 his_front=n;
182 }
183 }
184#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000185}
186
Mark Whitley4e338752001-01-26 20:42:23 +0000187
188
189/* special for recount position for scroll and remove terminal margin effect */
190static void cmdedit_set_out_char(int c, int next_char) {
191 putchar(c);
192 if(++cmdedit_x>=cmdedit_termw) {
193 /* terminal is scrolled down */
194 cmdedit_y++;
195 cmdedit_x=0;
196
197 if(!next_char)
198 next_char = ' ';
199 /* destroy "(auto)margin" */
200 putchar(next_char);
201 putchar('\b');
202 }
203 cursor++;
Erik Andersen13456d12000-03-16 08:09:57 +0000204}
205
Mark Whitley4e338752001-01-26 20:42:23 +0000206/* Move to end line. Bonus: rewrite line from cursor without use
207 special control terminal strings, also saved size and speed! */
208static void input_end (void) {
209 while(cursor < len)
210 cmdedit_set_out_char(command_ps[cursor], 0);
211}
212
213/* Go to the next line */
214static void goto_new_line(void) {
215 input_end();
216 cmdedit_set_out_char('\n', 0);
217}
218
219
220static inline void out1str(const char *s) { fputs (s, stdout); }
221static inline void beep (void) { putchar('\007'); }
222
Erik Andersenf0657d32000-04-12 17:49:52 +0000223/* Go to HOME position */
Mark Whitley4e338752001-01-26 20:42:23 +0000224static void input_home(void)
Erik Andersenf0657d32000-04-12 17:49:52 +0000225{
Mark Whitley4e338752001-01-26 20:42:23 +0000226 while(cmdedit_y>0) { /* up to start y */
227 out1str("\033[A");
228 cmdedit_y--;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000229 }
Mark Whitley4e338752001-01-26 20:42:23 +0000230 putchar('\r');
231 cursor = 0;
232 out1str(cmdedit_prompt);
233 cmdedit_x = cmdedit_prmt_len;
234
Erik Andersen13456d12000-03-16 08:09:57 +0000235}
236
Mark Whitley4e338752001-01-26 20:42:23 +0000237/* Move back one charactor */
238static void input_backward(void) {
239 if (cursor > 0) {
240 cursor--;
241 if(cmdedit_x!=0) { /* no first position in terminal line */
242 putchar('\b');
243 cmdedit_x--;
244 }
245 else {
246 out1str("\033[A"); /* up */
247 cmdedit_y--;
Erik Andersen13456d12000-03-16 08:09:57 +0000248
Mark Whitley4e338752001-01-26 20:42:23 +0000249 /* to end in current terminal line */
250 while(cmdedit_x<(cmdedit_termw-1)) {
251 out1str("\033[C");
252 cmdedit_x++;
253 }
254 }
Erik Andersen13456d12000-03-16 08:09:57 +0000255 }
Erik Andersen13456d12000-03-16 08:09:57 +0000256}
257
Erik Andersenf0657d32000-04-12 17:49:52 +0000258/* Delete the char in front of the cursor */
Mark Whitley4e338752001-01-26 20:42:23 +0000259static void input_delete(void)
Erik Andersenf0657d32000-04-12 17:49:52 +0000260{
Mark Whitley4e338752001-01-26 20:42:23 +0000261 int j = cursor;
Erik Andersena2685732000-04-09 18:27:46 +0000262
Mark Whitley4e338752001-01-26 20:42:23 +0000263 if (j == len)
Erik Andersenf0657d32000-04-12 17:49:52 +0000264 return;
265
Mark Whitley4e338752001-01-26 20:42:23 +0000266 memmove (command_ps + j, command_ps + j + 1, BUFSIZ - j - 1);
267 len--;
268 input_end(); /* rewtite new line */
269 cmdedit_set_out_char(' ', 0); /* destroy end char */
270 while (j < cursor)
271 input_backward(); /* back to old pos cursor */
Erik Andersenf0657d32000-04-12 17:49:52 +0000272}
273
Mark Whitley4e338752001-01-26 20:42:23 +0000274/* Delete the char in back of the cursor */
275static void input_backspace(void)
276{
277 if (cursor > 0) {
278 input_backward();
279 input_delete ();
280 }
281}
282
283
Erik Andersenf0657d32000-04-12 17:49:52 +0000284/* Move forward one charactor */
Mark Whitley4e338752001-01-26 20:42:23 +0000285static void input_forward(void)
Erik Andersenf0657d32000-04-12 17:49:52 +0000286{
Mark Whitley4e338752001-01-26 20:42:23 +0000287 if (cursor < len)
288 cmdedit_set_out_char(command_ps[cursor], command_ps[cursor + 1]);
Erik Andersenf0657d32000-04-12 17:49:52 +0000289}
290
291
Mark Whitley4e338752001-01-26 20:42:23 +0000292static void clean_up_and_die(int sig)
293{
294 goto_new_line();
295 if (sig!=SIGINT)
296 exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */
297 cmdedit_reset_term();
298}
299
300static void cmdedit_setwidth(int w, int redraw_flg)
301{
302 cmdedit_termw = cmdedit_prmt_len+2;
303 if (w > cmdedit_termw) {
304
305 cmdedit_termw = w;
306
307 if(redraw_flg) {
308 int sav_cursor = cursor;
309
310 /* set variables for new terminal size */
311 cmdedit_y = sav_cursor/w;
312 cmdedit_x = sav_cursor-cmdedit_y*w;
313
314 /* redraw */
315 input_home();
316 input_end();
317 while(sav_cursor<cursor)
318 input_backward();
319 }
320 } else {
Matt Kraaidd19c692001-01-31 19:00:21 +0000321 error_msg("\n*** Error: minimum screen width is %d", cmdedit_termw);
Mark Whitley4e338752001-01-26 20:42:23 +0000322 }
323}
324
325extern void cmdedit_init(void)
326{
327 if((handlers_sets & SET_WCHG_HANDLERS)==0) {
Eric Andersen4bbdd782001-01-30 22:23:17 +0000328 /* pretend we received a signal in order to set term size and sig handling */
Mark Whitley4e338752001-01-26 20:42:23 +0000329 win_changed(-SIGWINCH);
330 handlers_sets |= SET_WCHG_HANDLERS;
331 }
332
333 if((handlers_sets & SET_ATEXIT)==0) {
334 atexit(cmdedit_reset_term); /* be sure to do this only once */
335 handlers_sets |= SET_ATEXIT;
336 }
337 if((handlers_sets & SET_TERM_HANDLERS)==0) {
338 signal(SIGKILL, clean_up_and_die);
339 signal(SIGINT, clean_up_and_die);
340 signal(SIGQUIT, clean_up_and_die);
341 signal(SIGTERM, clean_up_and_die);
342 handlers_sets |= SET_TERM_HANDLERS;
343 }
344}
Erik Andersenf0657d32000-04-12 17:49:52 +0000345
346#ifdef BB_FEATURE_SH_TAB_COMPLETION
Mark Whitley4e338752001-01-26 20:42:23 +0000347
348#ifdef BB_FEATURE_USERNAME_COMPLETION
349static char** username_tab_completion(char *ud, int *num_matches)
Erik Andersen6273f652000-03-17 01:12:41 +0000350{
Mark Whitley4e338752001-01-26 20:42:23 +0000351 static struct passwd *entry;
352 int userlen;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000353 char **matches = (char **) NULL;
Mark Whitley4e338752001-01-26 20:42:23 +0000354 char *temp;
355 int nm = 0;
356
Eric Andersen4bbdd782001-01-30 22:23:17 +0000357 setpwent ();
Mark Whitley4e338752001-01-26 20:42:23 +0000358 userlen = strlen (ud + 1);
359
Eric Andersen4bbdd782001-01-30 22:23:17 +0000360 while ((entry = getpwent ()) != NULL) {
Mark Whitley4e338752001-01-26 20:42:23 +0000361 /* Null usernames should result in all users as possible completions. */
362 if (!userlen || !strncmp (ud + 1, entry->pw_name, userlen)) {
363
364 temp = xmalloc (3 + strlen (entry->pw_name));
365 sprintf(temp, "~%s/", entry->pw_name);
366
367 matches = xrealloc(matches, (nm+1)*sizeof(char *));
368 matches[nm++] = temp;
369 }
370 }
371
Eric Andersen4bbdd782001-01-30 22:23:17 +0000372 endpwent ();
Mark Whitley4e338752001-01-26 20:42:23 +0000373 (*num_matches) = nm;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000374 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000375}
Mark Whitley4e338752001-01-26 20:42:23 +0000376#endif
377
378enum {
379 FIND_EXE_ONLY = 0,
380 FIND_DIR_ONLY = 1,
381 FIND_FILE_ONLY = 2,
382};
Erik Andersen1dbe3402000-03-19 10:46:06 +0000383
384#include <dirent.h>
Mark Whitley4e338752001-01-26 20:42:23 +0000385
386static int path_parse(char ***p, int flags)
387{
388 int npth;
389 char *tmp;
390 char *pth;
391
392 if(flags!=FIND_EXE_ONLY || (pth=getenv("PATH"))==0) {
393 /* if not setenv PATH variable, to search cur dir "." */
394 (*p) = xmalloc(sizeof(char *));
395 (*p)[0] = xstrdup(".");
396 return 1;
397 }
398
399 tmp = pth;
400 npth=0;
401
402 for(;;) {
403 npth++; /* count words is + 1 count ':' */
404 tmp = strchr(tmp, ':');
405 if(tmp)
406 tmp++;
407 else
408 break;
409 }
410
411 *p = xmalloc(npth*sizeof(char *));
412
413 tmp = pth;
414 (*p)[0] = xstrdup(tmp);
415 npth=1; /* count words is + 1 count ':' */
416
417 for(;;) {
418 tmp = strchr(tmp, ':');
419 if(tmp) {
420 (*p)[0][(tmp-pth)]=0; /* ':' -> '\0'*/
421 tmp++;
422 } else
423 break;
424 (*p)[npth++] = &(*p)[0][(tmp-pth)]; /* p[next]=p[0][&'\0'+1] */
425 }
426
427 return npth;
428}
429
430static char** exe_n_cwd_tab_completion(char* command, int *num_matches, int type)
Erik Andersen6273f652000-03-17 01:12:41 +0000431{
Erik Andersen1dbe3402000-03-19 10:46:06 +0000432 char *dirName;
Mark Whitley4e338752001-01-26 20:42:23 +0000433 char **matches = 0;
Erik Andersen1dbe3402000-03-19 10:46:06 +0000434 DIR *dir;
435 struct dirent *next;
Mark Whitley4e338752001-01-26 20:42:23 +0000436 char cmd [BUFSIZ+4];
437 char *dirbuf;
438 char found [BUFSIZ+4];
439 int nm = *num_matches;
440 struct stat st;
441 char **paths;
442 int npaths;
443 int i;
444 char full_pth[BUFSIZ+4+PATH_MAX];
445
446
447 strcpy(cmd, command); /* save for change (last '/' to '\0') */
448
449 dirName = strrchr(cmd, '/');
450 if(dirName==NULL) {
451 /* no dir, if flags==EXE_ONLY - get paths, else "." */
452 npaths = path_parse(&paths, type);
453 if(npaths==0)
454 return 0;
455 } else {
456 /* with dir */
457
458 /* save dir */
459 dirbuf = xstrdup(cmd);
460 /* set only dirname */
461 dirbuf[(dirName-cmd)+1]=0;
462
463 /* strip dirname in cmd */
464 strcpy(cmd, dirName+1);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000465
Mark Whitley4e338752001-01-26 20:42:23 +0000466 paths = xmalloc(sizeof(char*));
467 paths[0] = dirbuf;
468 npaths = 1; /* only 1 dir */
469 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000470
Mark Whitley4e338752001-01-26 20:42:23 +0000471 for(i=0; i < npaths; i++) {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000472
Mark Whitley4e338752001-01-26 20:42:23 +0000473 dir = opendir(paths[i]);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000474 if (!dir) {
475 /* Don't print an error, just shut up and return */
Erik Andersen1dbe3402000-03-19 10:46:06 +0000476 return (matches);
477 }
478 while ((next = readdir(dir)) != NULL) {
Mark Whitley4e338752001-01-26 20:42:23 +0000479 /* matched ? */
480 if(strncmp(next->d_name, cmd, strlen(cmd)))
481 continue;
482 /* not see .name without .match */
483 if(*next->d_name == '.' && *cmd != '.')
484 continue;
485 sprintf(full_pth, "%s/%s", paths[i], next->d_name);
486 /* hmm, remover in progress? */
487 if(stat(full_pth, &st)<0)
488 continue;
489 /* Cool, found a match. */
490 if (S_ISDIR(st.st_mode)) {
491 /* name is directory */
492 strcpy(found, next->d_name);
493 strcat(found, "/");
494 if(type==FIND_DIR_ONLY)
495 strcat(found, " ");
496 } else {
497 /* not put found file if search only dirs for cd */
498 if(type==FIND_DIR_ONLY)
Erik Andersen1dbe3402000-03-19 10:46:06 +0000499 continue;
Mark Whitley4e338752001-01-26 20:42:23 +0000500 strcpy(found, next->d_name);
501 strcat(found, " ");
Erik Andersen1dbe3402000-03-19 10:46:06 +0000502 }
Mark Whitley4e338752001-01-26 20:42:23 +0000503 /* Add it to the list */
504 matches = xrealloc(matches, (nm+1)*sizeof(char *));
505 matches[nm++] = xstrdup(found);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000506 }
507 }
Mark Whitley4e338752001-01-26 20:42:23 +0000508 free(paths[0]); /* allocate memory only in first member */
509 free(paths);
510 *num_matches = nm;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000511 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000512}
Erik Andersenf0657d32000-04-12 17:49:52 +0000513
Mark Whitley4e338752001-01-26 20:42:23 +0000514static void input_tab(int lastWasTab)
Erik Andersenf0657d32000-04-12 17:49:52 +0000515{
516 /* Do TAB completion */
Mark Whitley4e338752001-01-26 20:42:23 +0000517 static int num_matches;
518 static char **matches;
519
520 char matchBuf[BUFSIZ];
521
522 int pos = cursor;
523 int find_type=FIND_FILE_ONLY;
Erik Andersenf0657d32000-04-12 17:49:52 +0000524
525
526 if (lastWasTab == FALSE) {
Mark Whitley4e338752001-01-26 20:42:23 +0000527 char *tmp, *tmp1;
528 int len_found;
Erik Andersenf0657d32000-04-12 17:49:52 +0000529
530 /* For now, we will not bother with trying to distinguish
531 * whether the cursor is in/at a command extression -- we
Eric Andersen74c66ad2000-06-16 19:57:44 +0000532 * will always try all possible matches. If you don't like
Erik Andersenf0657d32000-04-12 17:49:52 +0000533 * that then feel free to fix it.
534 */
535
536 /* Make a local copy of the string -- up
537 * to the position of the cursor */
Mark Whitley4e338752001-01-26 20:42:23 +0000538 memset(matchBuf, 0, BUFSIZ);
539 tmp = strncpy(matchBuf, command_ps, cursor);
Erik Andersenf0657d32000-04-12 17:49:52 +0000540
541 /* skip past any command seperator tokens */
Mark Whitley4e338752001-01-26 20:42:23 +0000542 while ( (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
543 tmp = ++tmp1;
Erik Andersenf0657d32000-04-12 17:49:52 +0000544 }
545
546 /* skip any leading white space */
Mark Whitley4e338752001-01-26 20:42:23 +0000547 while (*tmp == ' ')
548 tmp++;
549
550 if(strncmp(tmp, "cd ", 3)==0)
551 find_type = FIND_DIR_ONLY;
552 else if(strchr(tmp, ' ')==NULL)
553 find_type = FIND_EXE_ONLY;
554
555 /* find begin curent word */
556 if( (tmp1=strrchr(tmp, ' ')) != NULL) {
557 tmp = ++tmp1;
558 }
559 strcpy(matchBuf, tmp);
Erik Andersenf0657d32000-04-12 17:49:52 +0000560
561 /* Free up any memory already allocated */
562 if (matches) {
Mark Whitley4e338752001-01-26 20:42:23 +0000563 while(num_matches>0)
564 free(matches[--num_matches]);
Erik Andersenf0657d32000-04-12 17:49:52 +0000565 free(matches);
566 matches = (char **) NULL;
567 }
568
Mark Whitley4e338752001-01-26 20:42:23 +0000569#ifdef BB_FEATURE_USERNAME_COMPLETION
Erik Andersenf0657d32000-04-12 17:49:52 +0000570 /* If the word starts with `~' and there is no slash in the word,
571 * then try completing this word as a username. */
572
Mark Whitley4e338752001-01-26 20:42:23 +0000573 if (matchBuf[0]=='~' && strchr(matchBuf, '/')==0) {
574 matches = username_tab_completion(matchBuf, &num_matches);
575 }
576#endif
Erik Andersenf0657d32000-04-12 17:49:52 +0000577 /* Try to match any executable in our path and everything
578 * in the current working directory that matches. */
579 if (!matches)
Mark Whitley4e338752001-01-26 20:42:23 +0000580 matches = exe_n_cwd_tab_completion(matchBuf, &num_matches, find_type);
Erik Andersenf0657d32000-04-12 17:49:52 +0000581
582 /* Did we find exactly one match? */
Mark Whitley4e338752001-01-26 20:42:23 +0000583 if(!matches || num_matches>1) {
584 beep();
585 return;
586 }
587
588 len_found = strlen(matches[0]);
589
590 /* have space to placed match? */
591 if ( (len_found-strlen(matchBuf)+len) < BUFSIZ ) {
592
593 int recalc_pos = len;
594
595 /* before word for match */
596 command_ps[pos-strlen(matchBuf)]=0;
597
598 /* tail line */
599 strcpy(matchBuf, command_ps+pos);
600
601 /* add match */
602 strcat(command_ps, matches[0]);
603 /* add tail */
604 strcat(command_ps, matchBuf);
605
Erik Andersenf0657d32000-04-12 17:49:52 +0000606 /* write out the matched command */
Mark Whitley4e338752001-01-26 20:42:23 +0000607 len=strlen(command_ps);
608 recalc_pos = len-recalc_pos+pos;
609 input_end(); /* write */
610 while(recalc_pos<cursor)
611 input_backward();
Eric Andersena75466e2000-11-02 17:02:26 +0000612 return;
Erik Andersenf0657d32000-04-12 17:49:52 +0000613 }
614 } else {
615 /* Ok -- the last char was a TAB. Since they
616 * just hit TAB again, print a list of all the
617 * available choices... */
618 if ( matches && num_matches>0 ) {
619 int i, col;
Mark Whitley4e338752001-01-26 20:42:23 +0000620 int sav_cursor = cursor;
Erik Andersenf0657d32000-04-12 17:49:52 +0000621
622 /* Go to the next line */
Mark Whitley4e338752001-01-26 20:42:23 +0000623 goto_new_line();
Erik Andersenf0657d32000-04-12 17:49:52 +0000624 for (i=0,col=0; i<num_matches; i++) {
Mark Whitley4e338752001-01-26 20:42:23 +0000625 printf("%s ", matches[i]);
626 col += strlen(matches[i])+2;
627 col -= (col/cmdedit_termw)*cmdedit_termw;
Erik Andersenf0657d32000-04-12 17:49:52 +0000628 if (col > 60 && matches[i+1] != NULL) {
Mark Whitley4e338752001-01-26 20:42:23 +0000629 putchar('\n');
Erik Andersenf0657d32000-04-12 17:49:52 +0000630 col = 0;
631 }
632 }
Mark Whitley4e338752001-01-26 20:42:23 +0000633 /* Go to the next line and rewrite the prompt */
634 printf("\n%s", cmdedit_prompt);
635 cmdedit_x = cmdedit_prmt_len;
636 cmdedit_y = 0;
637 cursor = 0;
638 input_end(); /* Rewrite the command */
Erik Andersenf0657d32000-04-12 17:49:52 +0000639 /* Put the cursor back to where it used to be */
Mark Whitley4e338752001-01-26 20:42:23 +0000640 while (sav_cursor < cursor)
641 input_backward();
Erik Andersenf0657d32000-04-12 17:49:52 +0000642 }
643 }
644}
645#endif
646
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000647static void get_previous_history(struct history **hp, char* command)
Erik Andersenf0657d32000-04-12 17:49:52 +0000648{
Eric Andersen91a44002000-07-19 17:37:57 +0000649 if ((*hp)->s)
650 free((*hp)->s);
Erik Andersenf0657d32000-04-12 17:49:52 +0000651 (*hp)->s = strdup(command);
652 *hp = (*hp)->p;
653}
654
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000655static void get_next_history(struct history **hp, char* command)
Erik Andersenf0657d32000-04-12 17:49:52 +0000656{
Eric Andersen91a44002000-07-19 17:37:57 +0000657 if ((*hp)->s)
658 free((*hp)->s);
Erik Andersenf0657d32000-04-12 17:49:52 +0000659 (*hp)->s = strdup(command);
660 *hp = (*hp)->n;
Erik Andersenf0657d32000-04-12 17:49:52 +0000661}
662
Erik Andersen6273f652000-03-17 01:12:41 +0000663/*
664 * This function is used to grab a character buffer
665 * from the input file descriptor and allows you to
666 * a string with full command editing (sortof like
667 * a mini readline).
668 *
669 * The following standard commands are not implemented:
670 * ESC-b -- Move back one word
671 * ESC-f -- Move forward one word
672 * ESC-d -- Delete back one word
673 * ESC-h -- Delete forward one word
674 * CTL-t -- Transpose two characters
675 *
676 * Furthermore, the "vi" command editing keys are not implemented.
677 *
678 * TODO: implement TAB command completion. :)
Erik Andersen6273f652000-03-17 01:12:41 +0000679 */
Erik Andersenf0657d32000-04-12 17:49:52 +0000680extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
Erik Andersen13456d12000-03-16 08:09:57 +0000681{
682
Erik Andersenf0657d32000-04-12 17:49:52 +0000683 int inputFd=fileno(stdin);
Mark Whitley4e338752001-01-26 20:42:23 +0000684
Erik Andersenc7c634b2000-03-19 05:28:55 +0000685 int j = 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000686 int break_out = 0;
687 int ret = 0;
688 int lastWasTab = FALSE;
689 char c = 0;
690 struct history *hp = his_end;
Erik Andersen13456d12000-03-16 08:09:57 +0000691
Mark Whitley4e338752001-01-26 20:42:23 +0000692 len = 0;
693 cursor = 0;
694 command_ps = command;
695
696 if (new_settings.c_cc[VMIN]==0) {
Erik Andersen1d1d9502000-04-21 01:26:49 +0000697
698 getTermSettings(inputFd, (void*) &initial_settings);
699 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
700 new_settings.c_cc[VMIN] = 1;
701 new_settings.c_cc[VTIME] = 0;
702 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */
703 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
704 new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000705 }
Erik Andersen1d1d9502000-04-21 01:26:49 +0000706 setTermSettings(inputFd, (void*) &new_settings);
Mark Whitley4e338752001-01-26 20:42:23 +0000707 handlers_sets |= SET_RESET_TERM;
Erik Andersen13456d12000-03-16 08:09:57 +0000708
Erik Andersenf0657d32000-04-12 17:49:52 +0000709 memset(command, 0, BUFSIZ);
Erik Andersen13456d12000-03-16 08:09:57 +0000710
Mark Whitley4e338752001-01-26 20:42:23 +0000711 cmdedit_init();
712
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000713 /* Print out the command prompt */
Mark Whitley4e338752001-01-26 20:42:23 +0000714 cmdedit_prompt = prompt;
715 cmdedit_prmt_len = strlen(prompt);
716 printf("%s", prompt);
717 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
718 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
719
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000720
Erik Andersenc7c634b2000-03-19 05:28:55 +0000721 while (1) {
Erik Andersen6273f652000-03-17 01:12:41 +0000722
Mark Whitley4e338752001-01-26 20:42:23 +0000723 fflush(stdout); /* buffered out to fast */
724
Erik Andersen13456d12000-03-16 08:09:57 +0000725 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000726 return;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000727 //fprintf(stderr, "got a '%c' (%d)\n", c, c);
Erik Andersenf3b3d172000-04-09 18:24:05 +0000728
Erik Andersen13456d12000-03-16 08:09:57 +0000729 switch (c) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000730 case '\n':
731 case '\r':
732 /* Enter */
Mark Whitley4e338752001-01-26 20:42:23 +0000733 *(command + len) = c;
734 len++;
735 input_end ();
Erik Andersenf0657d32000-04-12 17:49:52 +0000736 break_out = 1;
737 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000738 case 1:
739 /* Control-a -- Beginning of line */
Mark Whitley4e338752001-01-26 20:42:23 +0000740 input_home();
741 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000742 case 2:
743 /* Control-b -- Move back one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000744 input_backward();
Erik Andersenf0657d32000-04-12 17:49:52 +0000745 break;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000746 case 3:
Eric Andersen86349772000-12-18 20:25:50 +0000747 /* Control-c -- stop gathering input */
748
749 /* Link into lash to reset context to 0 on ^C and such */
750 shell_context = 0;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000751
752 /* Go to the next line */
Mark Whitley4e338752001-01-26 20:42:23 +0000753 goto_new_line();
Erik Andersen1d1d9502000-04-21 01:26:49 +0000754
Eric Andersen86349772000-12-18 20:25:50 +0000755#if 0
Erik Andersen1d1d9502000-04-21 01:26:49 +0000756 /* Rewrite the prompt */
Mark Whitley4e338752001-01-26 20:42:23 +0000757 printf("%s", prompt);
Erik Andersen1d1d9502000-04-21 01:26:49 +0000758
759 /* Reset the command string */
Eric Andersen4ac6cb52000-07-14 01:13:37 +0000760 memset(command, 0, BUFSIZ);
Erik Andersen1d1d9502000-04-21 01:26:49 +0000761 len = cursor = 0;
Eric Andersen86349772000-12-18 20:25:50 +0000762#endif
763 return;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000764
Erik Andersenf0657d32000-04-12 17:49:52 +0000765 case 4:
766 /* Control-d -- Delete one character, or exit
767 * if the len=0 and no chars to delete */
768 if (len == 0) {
Mark Whitley4e338752001-01-26 20:42:23 +0000769 printf("exit");
Erik Andersenf0657d32000-04-12 17:49:52 +0000770 clean_up_and_die(0);
771 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000772 input_delete();
Erik Andersenf0657d32000-04-12 17:49:52 +0000773 }
774 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000775 case 5:
776 /* Control-e -- End of line */
Mark Whitley4e338752001-01-26 20:42:23 +0000777 input_end();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000778 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000779 case 6:
780 /* Control-f -- Move forward one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000781 input_forward();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000782 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000783 case '\b':
784 case DEL:
Erik Andersen1d1d9502000-04-21 01:26:49 +0000785 /* Control-h and DEL */
Mark Whitley4e338752001-01-26 20:42:23 +0000786 input_backspace();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000787 break;
788 case '\t':
Erik Andersena2685732000-04-09 18:27:46 +0000789#ifdef BB_FEATURE_SH_TAB_COMPLETION
Mark Whitley4e338752001-01-26 20:42:23 +0000790 input_tab(lastWasTab);
Erik Andersena2685732000-04-09 18:27:46 +0000791#endif
Erik Andersenc7c634b2000-03-19 05:28:55 +0000792 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000793 case 14:
794 /* Control-n -- Get next command in history */
795 if (hp && hp->n && hp->n->s) {
796 get_next_history(&hp, command);
797 goto rewrite_line;
798 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000799 beep();
Erik Andersenf0657d32000-04-12 17:49:52 +0000800 }
801 break;
802 case 16:
803 /* Control-p -- Get previous command from history */
804 if (hp && hp->p) {
805 get_previous_history(&hp, command);
806 goto rewrite_line;
807 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000808 beep();
Erik Andersenf0657d32000-04-12 17:49:52 +0000809 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000810 break;
811 case ESC:{
812 /* escape sequence follows */
813 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000814 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000815
816 if (c == '[') { /* 91 */
817 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000818 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000819
820 switch (c) {
821 case 'A':
Erik Andersenf0657d32000-04-12 17:49:52 +0000822 /* Up Arrow -- Get previous command from history */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000823 if (hp && hp->p) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000824 get_previous_history(&hp, command);
825 goto rewrite_line;
826 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000827 beep();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000828 }
829 break;
830 case 'B':
Erik Andersenf0657d32000-04-12 17:49:52 +0000831 /* Down Arrow -- Get next command in history */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000832 if (hp && hp->n && hp->n->s) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000833 get_next_history(&hp, command);
834 goto rewrite_line;
835 } else {
Mark Whitley4e338752001-01-26 20:42:23 +0000836 beep();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000837 }
838 break;
839
Erik Andersenf0657d32000-04-12 17:49:52 +0000840 /* Rewrite the line with the selected history item */
841 rewrite_line:
Mark Whitley4e338752001-01-26 20:42:23 +0000842 /* return to begin of line */
843 input_home ();
844 /* for next memmoves without set '\0' */
845 memset (command, 0, BUFSIZ);
846 /* change command */
847 strcpy (command, hp->s);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000848 /* write new command */
Mark Whitley4e338752001-01-26 20:42:23 +0000849 for (j=0; command[j]; j++)
850 cmdedit_set_out_char(command[j], 0);
851 ret = cursor;
852 /* erase tail if required */
853 for (j = ret; j < len; j++)
854 cmdedit_set_out_char(' ', 0);
855 /* and backward cursor */
856 for (j = ret; j < len; j++)
857 input_backward();
858 len = cursor; /* set new len */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000859 break;
860 case 'C':
861 /* Right Arrow -- Move forward one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000862 input_forward();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000863 break;
864 case 'D':
865 /* Left Arrow -- Move back one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000866 input_backward();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000867 break;
868 case '3':
869 /* Delete */
Mark Whitley4e338752001-01-26 20:42:23 +0000870 input_delete();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000871 break;
872 case '1':
873 /* Home (Ctrl-A) */
Mark Whitley4e338752001-01-26 20:42:23 +0000874 input_home();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000875 break;
876 case '4':
877 /* End (Ctrl-E) */
Mark Whitley4e338752001-01-26 20:42:23 +0000878 input_end();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000879 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000880 default:
Mark Whitley4e338752001-01-26 20:42:23 +0000881 beep();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000882 }
883 if (c == '1' || c == '3' || c == '4')
884 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000885 return; /* read 126 (~) */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000886 }
887 if (c == 'O') {
888 /* 79 */
889 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000890 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000891 switch (c) {
892 case 'H':
893 /* Home (xterm) */
Mark Whitley4e338752001-01-26 20:42:23 +0000894 input_home();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000895 break;
896 case 'F':
897 /* End (xterm) */
Mark Whitley4e338752001-01-26 20:42:23 +0000898 input_end();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000899 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000900 default:
Mark Whitley4e338752001-01-26 20:42:23 +0000901 beep();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000902 }
903 }
904 c = 0;
905 break;
906 }
907
908 default: /* If it's regular input, do the normal thing */
909
Erik Andersenf0657d32000-04-12 17:49:52 +0000910 if (!isprint(c)) { /* Skip non-printable characters */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000911 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000912 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000913
914 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
915 break;
916
917 len++;
918
919 if (cursor == (len - 1)) { /* Append if at the end of the line */
Erik Andersenf0657d32000-04-12 17:49:52 +0000920 *(command + cursor) = c;
Mark Whitley4e338752001-01-26 20:42:23 +0000921 cmdedit_set_out_char(c, command[cursor+1]);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000922 } else { /* Insert otherwise */
Erik Andersenf0657d32000-04-12 17:49:52 +0000923 memmove(command + cursor + 1, command + cursor,
Erik Andersenc7c634b2000-03-19 05:28:55 +0000924 len - cursor - 1);
925
Erik Andersenf0657d32000-04-12 17:49:52 +0000926 *(command + cursor) = c;
Mark Whitley4e338752001-01-26 20:42:23 +0000927 j = cursor+1;
928 /* rewrite from cursor */
929 input_end ();
930 /* to prev x pos + 1 */
931 while(cursor > j)
932 input_backward();
Erik Andersenc7c634b2000-03-19 05:28:55 +0000933 }
934
Erik Andersenc7c634b2000-03-19 05:28:55 +0000935 break;
Erik Andersen13456d12000-03-16 08:09:57 +0000936 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000937 if (c == '\t')
938 lastWasTab = TRUE;
939 else
940 lastWasTab = FALSE;
941
942 if (break_out) /* Enter is the command terminator, no more input. */
943 break;
944 }
945
Mark Whitley4e338752001-01-26 20:42:23 +0000946 setTermSettings (inputFd, (void *) &initial_settings);
947 handlers_sets &= ~SET_RESET_TERM;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000948
949 /* Handle command history log */
Mark Whitley4e338752001-01-26 20:42:23 +0000950 if (len>1) { /* no put empty line (only '\n') */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000951
952 struct history *h = his_end;
Mark Whitley4e338752001-01-26 20:42:23 +0000953 char *ss;
954
955 command[len-1] = 0; /* destroy end '\n' */
956 ss = strdup(command); /* duplicate without '\n' */
957 command[len-1] = '\n'; /* restore '\n' */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000958
959 if (!h) {
Eric Andersen91a44002000-07-19 17:37:57 +0000960 /* No previous history -- this memory is never freed */
Matt Kraai322ae932000-09-13 02:46:14 +0000961 h = his_front = xmalloc(sizeof(struct history));
962 h->n = xmalloc(sizeof(struct history));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000963
964 h->p = NULL;
Mark Whitley4e338752001-01-26 20:42:23 +0000965 h->s = ss;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000966 h->n->p = h;
967 h->n->n = NULL;
968 h->n->s = NULL;
969 his_end = h->n;
970 history_counter++;
971 } else {
Eric Andersen91a44002000-07-19 17:37:57 +0000972 /* Add a new history command -- this memory is never freed */
Matt Kraai322ae932000-09-13 02:46:14 +0000973 h->n = xmalloc(sizeof(struct history));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000974
975 h->n->p = h;
976 h->n->n = NULL;
977 h->n->s = NULL;
Mark Whitley4e338752001-01-26 20:42:23 +0000978 h->s = ss;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000979 his_end = h->n;
980
981 /* After max history, remove the oldest command */
982 if (history_counter >= MAX_HISTORY) {
983
984 struct history *p = his_front->n;
985
986 p->p = NULL;
987 free(his_front->s);
988 free(his_front);
989 his_front = p;
990 } else {
991 history_counter++;
992 }
Erik Andersen13456d12000-03-16 08:09:57 +0000993 }
Erik Andersen6273f652000-03-17 01:12:41 +0000994 }
Erik Andersen13456d12000-03-16 08:09:57 +0000995
Erik Andersenf0657d32000-04-12 17:49:52 +0000996 return;
Erik Andersen13456d12000-03-16 08:09:57 +0000997}
998
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000999
Mark Whitley4e338752001-01-26 20:42:23 +00001000/* Undo the effects of cmdedit_init(). */
Eric Andersen501c88b2000-07-28 15:14:45 +00001001extern void cmdedit_terminate(void)
1002{
1003 cmdedit_reset_term();
Mark Whitley4e338752001-01-26 20:42:23 +00001004 if((handlers_sets & SET_TERM_HANDLERS)!=0) {
1005 signal(SIGKILL, SIG_DFL);
1006 signal(SIGINT, SIG_DFL);
1007 signal(SIGQUIT, SIG_DFL);
1008 signal(SIGTERM, SIG_DFL);
1009 signal(SIGWINCH, SIG_DFL);
1010 handlers_sets &= ~SET_TERM_HANDLERS;
1011 }
Eric Andersen501c88b2000-07-28 15:14:45 +00001012}
1013
1014
1015
Mark Whitley4e338752001-01-26 20:42:23 +00001016#endif /* BB_FEATURE_SH_COMMAND_EDITING */