blob: 8115e5259735e0158fcf412d5dd1c773ee5364b1 [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.
28 Ctrl-E also works as End. The binary size increase is <3K.
29
30 Editting will not display correctly for lines greater then the
31 terminal width. (more then one line.) However, history will.
32 */
33
Eric Andersen3570a342000-09-25 21:45:58 +000034#include "busybox.h"
Erik Andersen13456d12000-03-16 08:09:57 +000035#ifdef BB_FEATURE_SH_COMMAND_EDITING
36
37#include <stdio.h>
38#include <errno.h>
39#include <unistd.h>
40#include <stdlib.h>
41#include <string.h>
Erik Andersen1d1d9502000-04-21 01:26:49 +000042#include <sys/ioctl.h>
Erik Andersen13456d12000-03-16 08:09:57 +000043#include <ctype.h>
44#include <signal.h>
45
Erik Andersen13456d12000-03-16 08:09:57 +000046
Erik Andersenc7c634b2000-03-19 05:28:55 +000047#define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */
Erik Andersen13456d12000-03-16 08:09:57 +000048
49#define ESC 27
50#define DEL 127
Erik Andersen6273f652000-03-17 01:12:41 +000051#define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0)
52#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
Erik Andersen13456d12000-03-16 08:09:57 +000053
54static struct history *his_front = NULL; /* First element in command line list */
55static struct history *his_end = NULL; /* Last element in command line list */
Erik Andersen1d1d9502000-04-21 01:26:49 +000056
57/* ED: sparc termios is broken: revert back to old termio handling. */
58#ifdef BB_FEATURE_USE_TERMIOS
59
60#if #cpu(sparc)
61# include <termio.h>
62# define termios termio
63# define setTermSettings(fd,argp) ioctl(fd,TCSETAF,argp)
64# define getTermSettings(fd,argp) ioctl(fd,TCGETA,argp)
65#else
66# include <termios.h>
67# define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
68# define getTermSettings(fd,argp) tcgetattr(fd, argp);
69#endif
70
71/* Current termio and the previous termio before starting sh */
Eric Andersen63a86222000-11-07 06:52:13 +000072static struct termios initial_settings, new_settings;
Erik Andersen8ea7d8c2000-05-20 00:40:08 +000073
74
75#ifndef _POSIX_VDISABLE
76#define _POSIX_VDISABLE '\0'
77#endif
78
Erik Andersen1d1d9502000-04-21 01:26:49 +000079#endif
80
81
Erik Andersen13456d12000-03-16 08:09:57 +000082
Erik Andersenf0657d32000-04-12 17:49:52 +000083static int cmdedit_termw = 80; /* actual terminal width */
84static int cmdedit_scroll = 27; /* width of EOL scrolling region */
Erik Andersen13456d12000-03-16 08:09:57 +000085static int history_counter = 0; /* Number of commands in history list */
Erik Andersenc7c634b2000-03-19 05:28:55 +000086static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */
Eric Andersen501c88b2000-07-28 15:14:45 +000087static int exithandler_set = 0; /* Set to true when atexit() has been called */
Erik Andersen13456d12000-03-16 08:09:57 +000088
89struct history {
Erik Andersenc7c634b2000-03-19 05:28:55 +000090 char *s;
91 struct history *p;
92 struct history *n;
Erik Andersen13456d12000-03-16 08:09:57 +000093};
94
Erik Andersenf0657d32000-04-12 17:49:52 +000095#define xwrite write
Erik Andersen13456d12000-03-16 08:09:57 +000096
Mark Whitley55380702000-07-13 17:20:23 +000097/*
98 * TODO: Someday we want to implement 'horizontal scrolling' of the
99 * command-line when the user has typed more than the current width. This
100 * would allow the user to see a 'window' of what he has typed.
101 */
Erik Andersenf0657d32000-04-12 17:49:52 +0000102void
103cmdedit_setwidth(int w)
Erik Andersen13456d12000-03-16 08:09:57 +0000104{
Erik Andersen61677fe2000-04-13 01:18:56 +0000105 if (w > 20) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000106 cmdedit_termw = w;
107 cmdedit_scroll = w / 3;
Erik Andersen61677fe2000-04-13 01:18:56 +0000108 } else {
Erik Andersenf0657d32000-04-12 17:49:52 +0000109 errorMsg("\n*** Error: minimum screen width is 21\n");
Erik Andersen61677fe2000-04-13 01:18:56 +0000110 }
Erik Andersen13456d12000-03-16 08:09:57 +0000111}
112
Erik Andersen61677fe2000-04-13 01:18:56 +0000113
Erik Andersen13456d12000-03-16 08:09:57 +0000114void cmdedit_reset_term(void)
115{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000116 if (reset_term)
Erik Andersena6c75222000-04-18 00:00:52 +0000117 /* sparc and other have broken termios support: use old termio handling. */
Erik Andersen1d1d9502000-04-21 01:26:49 +0000118 setTermSettings(fileno(stdin), (void*) &initial_settings);
Eric Andersenb040d4f2000-07-25 18:01:20 +0000119#ifdef BB_FEATURE_CLEAN_UP
120 if (his_front) {
121 struct history *n;
122 //while(his_front!=his_end) {
123 while(his_front!=his_end) {
124 n = his_front->n;
125 free(his_front->s);
126 free(his_front);
127 his_front=n;
128 }
129 }
130#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000131}
132
Erik Andersenf0657d32000-04-12 17:49:52 +0000133void clean_up_and_die(int sig)
Erik Andersen13456d12000-03-16 08:09:57 +0000134{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000135 cmdedit_reset_term();
136 fprintf(stdout, "\n");
Erik Andersen1d1d9502000-04-21 01:26:49 +0000137 if (sig!=SIGINT)
138 exit(TRUE);
Erik Andersen13456d12000-03-16 08:09:57 +0000139}
140
Erik Andersenf0657d32000-04-12 17:49:52 +0000141/* Go to HOME position */
Erik Andersen13456d12000-03-16 08:09:57 +0000142void input_home(int outputFd, int *cursor)
Erik Andersenf0657d32000-04-12 17:49:52 +0000143{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000144 while (*cursor > 0) {
145 xwrite(outputFd, "\b", 1);
146 --*cursor;
147 }
Erik Andersen13456d12000-03-16 08:09:57 +0000148}
149
Erik Andersenf0657d32000-04-12 17:49:52 +0000150/* Go to END position */
Erik Andersen13456d12000-03-16 08:09:57 +0000151void input_end(int outputFd, int *cursor, int len)
152{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000153 while (*cursor < len) {
154 xwrite(outputFd, "\033[C", 3);
155 ++*cursor;
156 }
Erik Andersen13456d12000-03-16 08:09:57 +0000157}
158
Erik Andersenf0657d32000-04-12 17:49:52 +0000159/* Delete the char in back of the cursor */
160void input_backspace(char* command, int outputFd, int *cursor, int *len)
Erik Andersen13456d12000-03-16 08:09:57 +0000161{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000162 int j = 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000163
Eric Andersen72965e32000-07-04 06:22:18 +0000164/* Debug crap */
165//fprintf(stderr, "\nerik: len=%d, cursor=%d, strlen(command)='%d'\n", *len, *cursor, strlen(command));
166//xwrite(outputFd, command, *len);
167//*cursor = *len;
168
169
Erik Andersenc7c634b2000-03-19 05:28:55 +0000170 if (*cursor > 0) {
171 xwrite(outputFd, "\b \b", 3);
172 --*cursor;
Erik Andersenf0657d32000-04-12 17:49:52 +0000173 memmove(command + *cursor, command + *cursor + 1,
Erik Andersenc7c634b2000-03-19 05:28:55 +0000174 BUFSIZ - *cursor + 1);
Erik Andersen13456d12000-03-16 08:09:57 +0000175
Erik Andersenc7c634b2000-03-19 05:28:55 +0000176 for (j = *cursor; j < (BUFSIZ - 1); j++) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000177 if (!*(command + j))
Erik Andersenc7c634b2000-03-19 05:28:55 +0000178 break;
179 else
Erik Andersenf0657d32000-04-12 17:49:52 +0000180 xwrite(outputFd, (command + j), 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000181 }
182
183 xwrite(outputFd, " \b", 2);
184
185 while (j-- > *cursor)
186 xwrite(outputFd, "\b", 1);
187
188 --*len;
Erik Andersen13456d12000-03-16 08:09:57 +0000189 }
Erik Andersen13456d12000-03-16 08:09:57 +0000190}
191
Erik Andersenf0657d32000-04-12 17:49:52 +0000192/* Delete the char in front of the cursor */
193void input_delete(char* command, int outputFd, int cursor, int *len)
194{
195 int j = 0;
Erik Andersena2685732000-04-09 18:27:46 +0000196
Erik Andersenf0657d32000-04-12 17:49:52 +0000197 if (cursor == *len)
198 return;
199
200 memmove(command + cursor, command + cursor + 1,
201 BUFSIZ - cursor - 1);
202 for (j = cursor; j < (BUFSIZ - 1); j++) {
203 if (!*(command + j))
204 break;
205 else
206 xwrite(outputFd, (command + j), 1);
207 }
208
209 xwrite(outputFd, " \b", 2);
210
211 while (j-- > cursor)
212 xwrite(outputFd, "\b", 1);
213 --*len;
214}
215
216/* Move forward one charactor */
217void input_forward(int outputFd, int *cursor, int len)
218{
219 if (*cursor < len) {
220 xwrite(outputFd, "\033[C", 3);
221 ++*cursor;
222 }
223}
224
225/* Move back one charactor */
226void input_backward(int outputFd, int *cursor)
227{
228 if (*cursor > 0) {
229 xwrite(outputFd, "\033[D", 3);
230 --*cursor;
231 }
232}
233
234
235
236#ifdef BB_FEATURE_SH_TAB_COMPLETION
237char** username_tab_completion(char* command, int *num_matches)
Erik Andersen6273f652000-03-17 01:12:41 +0000238{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000239 char **matches = (char **) NULL;
240 *num_matches=0;
Erik Andersenf0657d32000-04-12 17:49:52 +0000241 fprintf(stderr, "\nin username_tab_completion\n");
Erik Andersenc7c634b2000-03-19 05:28:55 +0000242 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000243}
Erik Andersen1dbe3402000-03-19 10:46:06 +0000244
245#include <dirent.h>
Erik Andersenf0657d32000-04-12 17:49:52 +0000246char** exe_n_cwd_tab_completion(char* command, int *num_matches)
Erik Andersen6273f652000-03-17 01:12:41 +0000247{
Erik Andersen1dbe3402000-03-19 10:46:06 +0000248 char *dirName;
Matt Kraai322ae932000-09-13 02:46:14 +0000249 char **matches;
Erik Andersen1dbe3402000-03-19 10:46:06 +0000250 DIR *dir;
251 struct dirent *next;
252
Matt Kraai322ae932000-09-13 02:46:14 +0000253 matches = xmalloc( sizeof(char*)*50);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000254
Erik Andersen1dbe3402000-03-19 10:46:06 +0000255 /* Stick a wildcard onto the command, for later use */
256 strcat( command, "*");
Erik Andersenc7c634b2000-03-19 05:28:55 +0000257
Erik Andersen1dbe3402000-03-19 10:46:06 +0000258 /* Now wall the current directory */
259 dirName = get_current_dir_name();
260 dir = opendir(dirName);
261 if (!dir) {
262 /* Don't print an error, just shut up and return */
263 *num_matches=0;
264 return (matches);
265 }
266 while ((next = readdir(dir)) != NULL) {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000267
Erik Andersen1dbe3402000-03-19 10:46:06 +0000268 /* Some quick sanity checks */
269 if ((strcmp(next->d_name, "..") == 0)
270 || (strcmp(next->d_name, ".") == 0)) {
271 continue;
272 }
273 /* See if this matches */
274 if (check_wildcard_match(next->d_name, command) == TRUE) {
275 /* Cool, found a match. Add it to the list */
Matt Kraai322ae932000-09-13 02:46:14 +0000276 matches[*num_matches] = xmalloc(strlen(next->d_name)+1);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000277 strcpy( matches[*num_matches], next->d_name);
278 ++*num_matches;
279 //matches = realloc( matches, sizeof(char*)*(*num_matches));
280 }
281 }
282
Erik Andersenc7c634b2000-03-19 05:28:55 +0000283 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000284}
Erik Andersenf0657d32000-04-12 17:49:52 +0000285
Eric Andersena75466e2000-11-02 17:02:26 +0000286void input_tab(char* command, char* prompt, int outputFd, int *cursor, int *len, int lastWasTab)
Erik Andersenf0657d32000-04-12 17:49:52 +0000287{
288 /* Do TAB completion */
289 static int num_matches=0;
290 static char **matches = (char **) NULL;
Eric Andersena75466e2000-11-02 17:02:26 +0000291 int pos = *cursor;
Erik Andersenf0657d32000-04-12 17:49:52 +0000292
293
294 if (lastWasTab == FALSE) {
295 char *tmp, *tmp1, *matchBuf;
296
297 /* For now, we will not bother with trying to distinguish
298 * whether the cursor is in/at a command extression -- we
Eric Andersen74c66ad2000-06-16 19:57:44 +0000299 * will always try all possible matches. If you don't like
Erik Andersenf0657d32000-04-12 17:49:52 +0000300 * that then feel free to fix it.
301 */
302
303 /* Make a local copy of the string -- up
304 * to the position of the cursor */
Matt Kraai322ae932000-09-13 02:46:14 +0000305 matchBuf = (char *) xcalloc(BUFSIZ, sizeof(char));
Eric Andersena75466e2000-11-02 17:02:26 +0000306 strncpy(matchBuf, command, *cursor);
Erik Andersenf0657d32000-04-12 17:49:52 +0000307 tmp=matchBuf;
308
309 /* skip past any command seperator tokens */
310 while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
311 tmp=++tmp1;
312 /* skip any leading white space */
313 while (*tmp && isspace(*tmp))
314 ++tmp;
315 }
316
317 /* skip any leading white space */
318 while (*tmp && isspace(*tmp))
319 ++tmp;
320
321 /* Free up any memory already allocated */
322 if (matches) {
323 free(matches);
324 matches = (char **) NULL;
325 }
326
327 /* If the word starts with `~' and there is no slash in the word,
328 * then try completing this word as a username. */
329
330 /* FIXME -- this check is broken! */
331 if (*tmp == '~' && !strchr(tmp, '/'))
332 matches = username_tab_completion(tmp, &num_matches);
333
334 /* Try to match any executable in our path and everything
335 * in the current working directory that matches. */
336 if (!matches)
337 matches = exe_n_cwd_tab_completion(tmp, &num_matches);
338
339 /* Don't leak memory */
340 free( matchBuf);
341
342 /* Did we find exactly one match? */
343 if (matches && num_matches==1) {
344 /* write out the matched command */
345 strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos);
Eric Andersena75466e2000-11-02 17:02:26 +0000346 *len=strlen(command);
347 *cursor=*len;
Erik Andersenf0657d32000-04-12 17:49:52 +0000348 xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos);
Eric Andersena75466e2000-11-02 17:02:26 +0000349 return;
Erik Andersenf0657d32000-04-12 17:49:52 +0000350 }
351 } else {
352 /* Ok -- the last char was a TAB. Since they
353 * just hit TAB again, print a list of all the
354 * available choices... */
355 if ( matches && num_matches>0 ) {
356 int i, col;
357
358 /* Go to the next line */
359 xwrite(outputFd, "\n", 1);
360 /* Print the list of matches */
361 for (i=0,col=0; i<num_matches; i++) {
362 char foo[17];
363 sprintf(foo, "%-14s ", matches[i]);
364 col += xwrite(outputFd, foo, strlen(foo));
365 if (col > 60 && matches[i+1] != NULL) {
366 xwrite(outputFd, "\n", 1);
367 col = 0;
368 }
369 }
370 /* Go to the next line */
371 xwrite(outputFd, "\n", 1);
372 /* Rewrite the prompt */
373 xwrite(outputFd, prompt, strlen(prompt));
374 /* Rewrite the command */
Eric Andersena75466e2000-11-02 17:02:26 +0000375 xwrite(outputFd, command, *len);
Erik Andersenf0657d32000-04-12 17:49:52 +0000376 /* Put the cursor back to where it used to be */
Eric Andersena75466e2000-11-02 17:02:26 +0000377 for (cursor=len; *cursor > pos; cursor--)
Erik Andersenf0657d32000-04-12 17:49:52 +0000378 xwrite(outputFd, "\b", 1);
379 }
380 }
381}
382#endif
383
384void get_previous_history(struct history **hp, char* command)
385{
Eric Andersen91a44002000-07-19 17:37:57 +0000386 if ((*hp)->s)
387 free((*hp)->s);
Erik Andersenf0657d32000-04-12 17:49:52 +0000388 (*hp)->s = strdup(command);
389 *hp = (*hp)->p;
390}
391
392void get_next_history(struct history **hp, char* command)
393{
Eric Andersen91a44002000-07-19 17:37:57 +0000394 if ((*hp)->s)
395 free((*hp)->s);
Erik Andersenf0657d32000-04-12 17:49:52 +0000396 (*hp)->s = strdup(command);
397 *hp = (*hp)->n;
Erik Andersenf0657d32000-04-12 17:49:52 +0000398}
399
Erik Andersen6273f652000-03-17 01:12:41 +0000400/*
401 * This function is used to grab a character buffer
402 * from the input file descriptor and allows you to
403 * a string with full command editing (sortof like
404 * a mini readline).
405 *
406 * The following standard commands are not implemented:
407 * ESC-b -- Move back one word
408 * ESC-f -- Move forward one word
409 * ESC-d -- Delete back one word
410 * ESC-h -- Delete forward one word
411 * CTL-t -- Transpose two characters
412 *
413 * Furthermore, the "vi" command editing keys are not implemented.
414 *
415 * TODO: implement TAB command completion. :)
Erik Andersen6273f652000-03-17 01:12:41 +0000416 */
Erik Andersenf0657d32000-04-12 17:49:52 +0000417extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
Erik Andersen13456d12000-03-16 08:09:57 +0000418{
419
Erik Andersenf0657d32000-04-12 17:49:52 +0000420 int inputFd=fileno(stdin);
421 int outputFd=fileno(stdout);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000422 int nr = 0;
423 int len = 0;
424 int j = 0;
425 int cursor = 0;
426 int break_out = 0;
427 int ret = 0;
428 int lastWasTab = FALSE;
429 char c = 0;
430 struct history *hp = his_end;
Erik Andersen13456d12000-03-16 08:09:57 +0000431
Erik Andersenc7c634b2000-03-19 05:28:55 +0000432 if (!reset_term) {
Erik Andersen1d1d9502000-04-21 01:26:49 +0000433
434 getTermSettings(inputFd, (void*) &initial_settings);
435 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
436 new_settings.c_cc[VMIN] = 1;
437 new_settings.c_cc[VTIME] = 0;
438 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */
439 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
440 new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000441 reset_term = 1;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000442 }
Erik Andersen1d1d9502000-04-21 01:26:49 +0000443 setTermSettings(inputFd, (void*) &new_settings);
Erik Andersen13456d12000-03-16 08:09:57 +0000444
Erik Andersenf0657d32000-04-12 17:49:52 +0000445 memset(command, 0, BUFSIZ);
Erik Andersen13456d12000-03-16 08:09:57 +0000446
Erik Andersenc7c634b2000-03-19 05:28:55 +0000447 while (1) {
Erik Andersen6273f652000-03-17 01:12:41 +0000448
Erik Andersen13456d12000-03-16 08:09:57 +0000449 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000450 return;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000451 //fprintf(stderr, "got a '%c' (%d)\n", c, c);
Erik Andersenf3b3d172000-04-09 18:24:05 +0000452
Erik Andersen13456d12000-03-16 08:09:57 +0000453 switch (c) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000454 case '\n':
455 case '\r':
456 /* Enter */
457 *(command + len++ + 1) = c;
458 xwrite(outputFd, &c, 1);
459 break_out = 1;
460 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000461 case 1:
462 /* Control-a -- Beginning of line */
463 input_home(outputFd, &cursor);
Erik Andersenf0657d32000-04-12 17:49:52 +0000464 case 2:
465 /* Control-b -- Move back one character */
466 input_backward(outputFd, &cursor);
467 break;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000468 case 3:
469 /* Control-c -- leave the current line,
470 * and start over on the next line */
471
472 /* Go to the next line */
473 xwrite(outputFd, "\n", 1);
474
475 /* Rewrite the prompt */
476 xwrite(outputFd, prompt, strlen(prompt));
477
478 /* Reset the command string */
Eric Andersen4ac6cb52000-07-14 01:13:37 +0000479 memset(command, 0, BUFSIZ);
Erik Andersen1d1d9502000-04-21 01:26:49 +0000480 len = cursor = 0;
481
482 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000483 case 4:
484 /* Control-d -- Delete one character, or exit
485 * if the len=0 and no chars to delete */
486 if (len == 0) {
487 xwrite(outputFd, "exit", 4);
488 clean_up_and_die(0);
489 } else {
490 input_delete(command, outputFd, cursor, &len);
491 }
492 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000493 case 5:
494 /* Control-e -- End of line */
495 input_end(outputFd, &cursor, len);
496 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000497 case 6:
498 /* Control-f -- Move forward one character */
Erik Andersenf0657d32000-04-12 17:49:52 +0000499 input_forward(outputFd, &cursor, len);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000500 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000501 case '\b':
502 case DEL:
Erik Andersen1d1d9502000-04-21 01:26:49 +0000503 /* Control-h and DEL */
Erik Andersenf0657d32000-04-12 17:49:52 +0000504 input_backspace(command, outputFd, &cursor, &len);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000505 break;
506 case '\t':
Erik Andersena2685732000-04-09 18:27:46 +0000507#ifdef BB_FEATURE_SH_TAB_COMPLETION
Eric Andersena75466e2000-11-02 17:02:26 +0000508 input_tab(command, prompt, outputFd, &cursor,
509&len, lastWasTab);
Erik Andersena2685732000-04-09 18:27:46 +0000510#endif
Erik Andersenc7c634b2000-03-19 05:28:55 +0000511 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000512 case 14:
513 /* Control-n -- Get next command in history */
514 if (hp && hp->n && hp->n->s) {
515 get_next_history(&hp, command);
516 goto rewrite_line;
517 } else {
518 xwrite(outputFd, "\007", 1);
519 }
520 break;
521 case 16:
522 /* Control-p -- Get previous command from history */
523 if (hp && hp->p) {
524 get_previous_history(&hp, command);
525 goto rewrite_line;
526 } else {
527 xwrite(outputFd, "\007", 1);
528 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000529 break;
530 case ESC:{
531 /* escape sequence follows */
532 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000533 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000534
535 if (c == '[') { /* 91 */
536 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000537 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000538
539 switch (c) {
540 case 'A':
Erik Andersenf0657d32000-04-12 17:49:52 +0000541 /* Up Arrow -- Get previous command from history */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000542 if (hp && hp->p) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000543 get_previous_history(&hp, command);
544 goto rewrite_line;
545 } else {
546 xwrite(outputFd, "\007", 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000547 }
548 break;
549 case 'B':
Erik Andersenf0657d32000-04-12 17:49:52 +0000550 /* Down Arrow -- Get next command in history */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000551 if (hp && hp->n && hp->n->s) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000552 get_next_history(&hp, command);
553 goto rewrite_line;
554 } else {
555 xwrite(outputFd, "\007", 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000556 }
557 break;
558
Erik Andersenf0657d32000-04-12 17:49:52 +0000559 /* Rewrite the line with the selected history item */
560 rewrite_line:
561 /* erase old command from command line */
562 len = strlen(command)-strlen(hp->s);
Eric Andersen72965e32000-07-04 06:22:18 +0000563
564 while (len>cursor)
565 input_delete(command, outputFd, cursor, &len);
566 while (cursor>0)
Erik Andersenf0657d32000-04-12 17:49:52 +0000567 input_backspace(command, outputFd, &cursor, &len);
568 input_home(outputFd, &cursor);
569
Erik Andersenc7c634b2000-03-19 05:28:55 +0000570 /* write new command */
Erik Andersenf0657d32000-04-12 17:49:52 +0000571 strcpy(command, hp->s);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000572 len = strlen(hp->s);
Erik Andersenf0657d32000-04-12 17:49:52 +0000573 xwrite(outputFd, command, len);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000574 cursor = len;
575 break;
576 case 'C':
577 /* Right Arrow -- Move forward one character */
Erik Andersenf0657d32000-04-12 17:49:52 +0000578 input_forward(outputFd, &cursor, len);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000579 break;
580 case 'D':
581 /* Left Arrow -- Move back one character */
Erik Andersenf0657d32000-04-12 17:49:52 +0000582 input_backward(outputFd, &cursor);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000583 break;
584 case '3':
585 /* Delete */
Erik Andersenf0657d32000-04-12 17:49:52 +0000586 input_delete(command, outputFd, cursor, &len);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000587 break;
588 case '1':
589 /* Home (Ctrl-A) */
590 input_home(outputFd, &cursor);
591 break;
592 case '4':
593 /* End (Ctrl-E) */
594 input_end(outputFd, &cursor, len);
595 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000596 default:
597 xwrite(outputFd, "\007", 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000598 }
599 if (c == '1' || c == '3' || c == '4')
600 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000601 return; /* read 126 (~) */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000602 }
603 if (c == 'O') {
604 /* 79 */
605 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000606 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000607 switch (c) {
608 case 'H':
609 /* Home (xterm) */
610 input_home(outputFd, &cursor);
611 break;
612 case 'F':
613 /* End (xterm) */
614 input_end(outputFd, &cursor, len);
615 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000616 default:
617 xwrite(outputFd, "\007", 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000618 }
619 }
620 c = 0;
621 break;
622 }
623
624 default: /* If it's regular input, do the normal thing */
625
Erik Andersenf0657d32000-04-12 17:49:52 +0000626 if (!isprint(c)) { /* Skip non-printable characters */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000627 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000628 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000629
630 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
631 break;
632
633 len++;
634
635 if (cursor == (len - 1)) { /* Append if at the end of the line */
Erik Andersenf0657d32000-04-12 17:49:52 +0000636 *(command + cursor) = c;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000637 } else { /* Insert otherwise */
Erik Andersenf0657d32000-04-12 17:49:52 +0000638 memmove(command + cursor + 1, command + cursor,
Erik Andersenc7c634b2000-03-19 05:28:55 +0000639 len - cursor - 1);
640
Erik Andersenf0657d32000-04-12 17:49:52 +0000641 *(command + cursor) = c;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000642
643 for (j = cursor; j < len; j++)
Erik Andersenf0657d32000-04-12 17:49:52 +0000644 xwrite(outputFd, command + j, 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000645 for (; j > cursor; j--)
646 xwrite(outputFd, "\033[D", 3);
647 }
648
Erik Andersen13456d12000-03-16 08:09:57 +0000649 cursor++;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000650 xwrite(outputFd, &c, 1);
651 break;
Erik Andersen13456d12000-03-16 08:09:57 +0000652 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000653 if (c == '\t')
654 lastWasTab = TRUE;
655 else
656 lastWasTab = FALSE;
657
658 if (break_out) /* Enter is the command terminator, no more input. */
659 break;
660 }
661
662 nr = len + 1;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000663 setTermSettings(inputFd, (void *) &initial_settings);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000664 reset_term = 0;
665
666
667 /* Handle command history log */
Erik Andersenf0657d32000-04-12 17:49:52 +0000668 if (*(command)) {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000669
670 struct history *h = his_end;
671
672 if (!h) {
Eric Andersen91a44002000-07-19 17:37:57 +0000673 /* No previous history -- this memory is never freed */
Matt Kraai322ae932000-09-13 02:46:14 +0000674 h = his_front = xmalloc(sizeof(struct history));
675 h->n = xmalloc(sizeof(struct history));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000676
677 h->p = NULL;
Erik Andersenf0657d32000-04-12 17:49:52 +0000678 h->s = strdup(command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000679 h->n->p = h;
680 h->n->n = NULL;
681 h->n->s = NULL;
682 his_end = h->n;
683 history_counter++;
684 } else {
Eric Andersen91a44002000-07-19 17:37:57 +0000685 /* Add a new history command -- this memory is never freed */
Matt Kraai322ae932000-09-13 02:46:14 +0000686 h->n = xmalloc(sizeof(struct history));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000687
688 h->n->p = h;
689 h->n->n = NULL;
690 h->n->s = NULL;
Erik Andersenf0657d32000-04-12 17:49:52 +0000691 h->s = strdup(command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000692 his_end = h->n;
693
694 /* After max history, remove the oldest command */
695 if (history_counter >= MAX_HISTORY) {
696
697 struct history *p = his_front->n;
698
699 p->p = NULL;
700 free(his_front->s);
701 free(his_front);
702 his_front = p;
703 } else {
704 history_counter++;
705 }
Erik Andersen13456d12000-03-16 08:09:57 +0000706 }
Erik Andersen6273f652000-03-17 01:12:41 +0000707 }
Erik Andersen13456d12000-03-16 08:09:57 +0000708
Erik Andersenf0657d32000-04-12 17:49:52 +0000709 return;
Erik Andersen13456d12000-03-16 08:09:57 +0000710}
711
712extern void cmdedit_init(void)
713{
Eric Andersen501c88b2000-07-28 15:14:45 +0000714 if(exithandler_set == 0) {
715 atexit(cmdedit_reset_term); /* be sure to do this only once */
716 exithandler_set = 1;
717 }
Erik Andersen1d1d9502000-04-21 01:26:49 +0000718 signal(SIGKILL, clean_up_and_die);
Erik Andersenf0657d32000-04-12 17:49:52 +0000719 signal(SIGINT, clean_up_and_die);
720 signal(SIGQUIT, clean_up_and_die);
721 signal(SIGTERM, clean_up_and_die);
Erik Andersen13456d12000-03-16 08:09:57 +0000722}
Eric Andersen501c88b2000-07-28 15:14:45 +0000723
724/*
725** Undo the effects of cmdedit_init() as good as we can:
726** I am not aware of a way to revoke an atexit() handler,
727** but, fortunately, our particular handler can be made
728** a no-op by setting reset_term = 0.
729*/
730extern void cmdedit_terminate(void)
731{
732 cmdedit_reset_term();
733 reset_term = 0;
734 signal(SIGKILL, SIG_DFL);
735 signal(SIGINT, SIG_DFL);
736 signal(SIGQUIT, SIG_DFL);
737 signal(SIGTERM, SIG_DFL);
738}
739
740
741
Erik Andersenc7c634b2000-03-19 05:28:55 +0000742#endif /* BB_FEATURE_SH_COMMAND_EDITING */