blob: 12c78dc763243f4533f746a12c5f6ccb50eeb1de [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
Mark Whitley59ab0252001-01-23 22:30:04 +000047static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */
Erik Andersen13456d12000-03-16 08:09:57 +000048
Mark Whitley59ab0252001-01-23 22:30:04 +000049enum {
50 ESC = 27,
51 DEL = 127,
52};
53
Erik Andersen6273f652000-03-17 01:12:41 +000054#define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0)
55#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
Erik Andersen13456d12000-03-16 08:09:57 +000056
57static struct history *his_front = NULL; /* First element in command line list */
58static struct history *his_end = NULL; /* Last element in command line list */
Erik Andersen1d1d9502000-04-21 01:26:49 +000059
60/* ED: sparc termios is broken: revert back to old termio handling. */
61#ifdef BB_FEATURE_USE_TERMIOS
62
63#if #cpu(sparc)
64# include <termio.h>
65# define termios termio
66# define setTermSettings(fd,argp) ioctl(fd,TCSETAF,argp)
67# define getTermSettings(fd,argp) ioctl(fd,TCGETA,argp)
68#else
69# include <termios.h>
70# define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
71# define getTermSettings(fd,argp) tcgetattr(fd, argp);
72#endif
73
74/* Current termio and the previous termio before starting sh */
Eric Andersen63a86222000-11-07 06:52:13 +000075static struct termios initial_settings, new_settings;
Erik Andersen8ea7d8c2000-05-20 00:40:08 +000076
77
78#ifndef _POSIX_VDISABLE
79#define _POSIX_VDISABLE '\0'
80#endif
81
Erik Andersen1d1d9502000-04-21 01:26:49 +000082#endif
83
84
Erik Andersen13456d12000-03-16 08:09:57 +000085
Erik Andersenf0657d32000-04-12 17:49:52 +000086static int cmdedit_termw = 80; /* actual terminal width */
87static int cmdedit_scroll = 27; /* width of EOL scrolling region */
Erik Andersen13456d12000-03-16 08:09:57 +000088static int history_counter = 0; /* Number of commands in history list */
Erik Andersenc7c634b2000-03-19 05:28:55 +000089static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */
Eric Andersen501c88b2000-07-28 15:14:45 +000090static int exithandler_set = 0; /* Set to true when atexit() has been called */
Eric Andersen86349772000-12-18 20:25:50 +000091
92
93/* Link into lash to reset context to 0
94 * on ^C and such */
95extern unsigned int shell_context;
96
Erik Andersen13456d12000-03-16 08:09:57 +000097
98struct history {
Erik Andersenc7c634b2000-03-19 05:28:55 +000099 char *s;
100 struct history *p;
101 struct history *n;
Erik Andersen13456d12000-03-16 08:09:57 +0000102};
103
Erik Andersenf0657d32000-04-12 17:49:52 +0000104#define xwrite write
Erik Andersen13456d12000-03-16 08:09:57 +0000105
Mark Whitley55380702000-07-13 17:20:23 +0000106/*
107 * TODO: Someday we want to implement 'horizontal scrolling' of the
108 * command-line when the user has typed more than the current width. This
109 * would allow the user to see a 'window' of what he has typed.
110 */
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000111static void cmdedit_setwidth(int w)
Erik Andersen13456d12000-03-16 08:09:57 +0000112{
Erik Andersen61677fe2000-04-13 01:18:56 +0000113 if (w > 20) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000114 cmdedit_termw = w;
115 cmdedit_scroll = w / 3;
Erik Andersen61677fe2000-04-13 01:18:56 +0000116 } else {
Mark Whitleyf57c9442000-12-07 19:56:48 +0000117 error_msg("\n*** Error: minimum screen width is 21\n");
Erik Andersen61677fe2000-04-13 01:18:56 +0000118 }
Erik Andersen13456d12000-03-16 08:09:57 +0000119}
120
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000121static void win_changed(int junk)
122{
123 struct winsize win = { 0, 0, 0, 0 };
124 ioctl(0, TIOCGWINSZ, &win);
125 if (win.ws_col > 0) {
126 cmdedit_setwidth( win.ws_col - 1);
127 }
128}
Erik Andersen61677fe2000-04-13 01:18:56 +0000129
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000130
131static void cmdedit_reset_term(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000132{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000133 if (reset_term)
Erik Andersena6c75222000-04-18 00:00:52 +0000134 /* sparc and other have broken termios support: use old termio handling. */
Erik Andersen1d1d9502000-04-21 01:26:49 +0000135 setTermSettings(fileno(stdin), (void*) &initial_settings);
Eric Andersenb040d4f2000-07-25 18:01:20 +0000136#ifdef BB_FEATURE_CLEAN_UP
137 if (his_front) {
138 struct history *n;
139 //while(his_front!=his_end) {
140 while(his_front!=his_end) {
141 n = his_front->n;
142 free(his_front->s);
143 free(his_front);
144 his_front=n;
145 }
146 }
147#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000148}
149
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000150static void clean_up_and_die(int sig)
Erik Andersen13456d12000-03-16 08:09:57 +0000151{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000152 cmdedit_reset_term();
Matt Kraai12f417e2001-01-18 02:57:08 +0000153 printf("\n");
Erik Andersen1d1d9502000-04-21 01:26:49 +0000154 if (sig!=SIGINT)
Matt Kraai3e856ce2000-12-01 02:55:13 +0000155 exit(EXIT_SUCCESS);
Erik Andersen13456d12000-03-16 08:09:57 +0000156}
157
Erik Andersenf0657d32000-04-12 17:49:52 +0000158/* Go to HOME position */
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000159static void input_home(int outputFd, int *cursor)
Erik Andersenf0657d32000-04-12 17:49:52 +0000160{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000161 while (*cursor > 0) {
162 xwrite(outputFd, "\b", 1);
163 --*cursor;
164 }
Erik Andersen13456d12000-03-16 08:09:57 +0000165}
166
Erik Andersenf0657d32000-04-12 17:49:52 +0000167/* Go to END position */
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000168static void input_end(int outputFd, int *cursor, int len)
Erik Andersen13456d12000-03-16 08:09:57 +0000169{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000170 while (*cursor < len) {
171 xwrite(outputFd, "\033[C", 3);
172 ++*cursor;
173 }
Erik Andersen13456d12000-03-16 08:09:57 +0000174}
175
Erik Andersenf0657d32000-04-12 17:49:52 +0000176/* Delete the char in back of the cursor */
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000177static void input_backspace(char* command, int outputFd, int *cursor, int *len)
Erik Andersen13456d12000-03-16 08:09:57 +0000178{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000179 int j = 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000180
Eric Andersen72965e32000-07-04 06:22:18 +0000181/* Debug crap */
182//fprintf(stderr, "\nerik: len=%d, cursor=%d, strlen(command)='%d'\n", *len, *cursor, strlen(command));
183//xwrite(outputFd, command, *len);
184//*cursor = *len;
185
186
Erik Andersenc7c634b2000-03-19 05:28:55 +0000187 if (*cursor > 0) {
188 xwrite(outputFd, "\b \b", 3);
189 --*cursor;
Erik Andersenf0657d32000-04-12 17:49:52 +0000190 memmove(command + *cursor, command + *cursor + 1,
Erik Andersenc7c634b2000-03-19 05:28:55 +0000191 BUFSIZ - *cursor + 1);
Erik Andersen13456d12000-03-16 08:09:57 +0000192
Erik Andersenc7c634b2000-03-19 05:28:55 +0000193 for (j = *cursor; j < (BUFSIZ - 1); j++) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000194 if (!*(command + j))
Erik Andersenc7c634b2000-03-19 05:28:55 +0000195 break;
196 else
Erik Andersenf0657d32000-04-12 17:49:52 +0000197 xwrite(outputFd, (command + j), 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000198 }
199
200 xwrite(outputFd, " \b", 2);
201
202 while (j-- > *cursor)
203 xwrite(outputFd, "\b", 1);
204
205 --*len;
Erik Andersen13456d12000-03-16 08:09:57 +0000206 }
Erik Andersen13456d12000-03-16 08:09:57 +0000207}
208
Erik Andersenf0657d32000-04-12 17:49:52 +0000209/* Delete the char in front of the cursor */
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000210static void input_delete(char* command, int outputFd, int cursor, int *len)
Erik Andersenf0657d32000-04-12 17:49:52 +0000211{
212 int j = 0;
Erik Andersena2685732000-04-09 18:27:46 +0000213
Erik Andersenf0657d32000-04-12 17:49:52 +0000214 if (cursor == *len)
215 return;
216
217 memmove(command + cursor, command + cursor + 1,
218 BUFSIZ - cursor - 1);
219 for (j = cursor; j < (BUFSIZ - 1); j++) {
220 if (!*(command + j))
221 break;
222 else
223 xwrite(outputFd, (command + j), 1);
224 }
225
226 xwrite(outputFd, " \b", 2);
227
228 while (j-- > cursor)
229 xwrite(outputFd, "\b", 1);
230 --*len;
231}
232
233/* Move forward one charactor */
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000234static void input_forward(int outputFd, int *cursor, int len)
Erik Andersenf0657d32000-04-12 17:49:52 +0000235{
236 if (*cursor < len) {
237 xwrite(outputFd, "\033[C", 3);
238 ++*cursor;
239 }
240}
241
242/* Move back one charactor */
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000243static void input_backward(int outputFd, int *cursor)
Erik Andersenf0657d32000-04-12 17:49:52 +0000244{
245 if (*cursor > 0) {
246 xwrite(outputFd, "\033[D", 3);
247 --*cursor;
248 }
249}
250
251
252
253#ifdef BB_FEATURE_SH_TAB_COMPLETION
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000254static char** username_tab_completion(char* command, int *num_matches)
Erik Andersen6273f652000-03-17 01:12:41 +0000255{
Erik Andersenc7c634b2000-03-19 05:28:55 +0000256 char **matches = (char **) NULL;
257 *num_matches=0;
Erik Andersenf0657d32000-04-12 17:49:52 +0000258 fprintf(stderr, "\nin username_tab_completion\n");
Erik Andersenc7c634b2000-03-19 05:28:55 +0000259 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000260}
Erik Andersen1dbe3402000-03-19 10:46:06 +0000261
262#include <dirent.h>
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000263static char** exe_n_cwd_tab_completion(char* command, int *num_matches)
Erik Andersen6273f652000-03-17 01:12:41 +0000264{
Erik Andersen1dbe3402000-03-19 10:46:06 +0000265 char *dirName;
Matt Kraai322ae932000-09-13 02:46:14 +0000266 char **matches;
Erik Andersen1dbe3402000-03-19 10:46:06 +0000267 DIR *dir;
268 struct dirent *next;
269
Matt Kraai322ae932000-09-13 02:46:14 +0000270 matches = xmalloc( sizeof(char*)*50);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000271
Erik Andersen1dbe3402000-03-19 10:46:06 +0000272 /* Stick a wildcard onto the command, for later use */
273 strcat( command, "*");
Erik Andersenc7c634b2000-03-19 05:28:55 +0000274
Erik Andersen1dbe3402000-03-19 10:46:06 +0000275 /* Now wall the current directory */
276 dirName = get_current_dir_name();
277 dir = opendir(dirName);
278 if (!dir) {
279 /* Don't print an error, just shut up and return */
280 *num_matches=0;
281 return (matches);
282 }
283 while ((next = readdir(dir)) != NULL) {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000284
Erik Andersen1dbe3402000-03-19 10:46:06 +0000285 /* Some quick sanity checks */
286 if ((strcmp(next->d_name, "..") == 0)
287 || (strcmp(next->d_name, ".") == 0)) {
288 continue;
289 }
290 /* See if this matches */
291 if (check_wildcard_match(next->d_name, command) == TRUE) {
292 /* Cool, found a match. Add it to the list */
Matt Kraai322ae932000-09-13 02:46:14 +0000293 matches[*num_matches] = xmalloc(strlen(next->d_name)+1);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000294 strcpy( matches[*num_matches], next->d_name);
295 ++*num_matches;
296 //matches = realloc( matches, sizeof(char*)*(*num_matches));
297 }
298 }
299
Erik Andersenc7c634b2000-03-19 05:28:55 +0000300 return (matches);
Erik Andersen6273f652000-03-17 01:12:41 +0000301}
Erik Andersenf0657d32000-04-12 17:49:52 +0000302
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000303static void input_tab(char* command, char* prompt, int outputFd, int *cursor, int *len, int lastWasTab)
Erik Andersenf0657d32000-04-12 17:49:52 +0000304{
305 /* Do TAB completion */
306 static int num_matches=0;
307 static char **matches = (char **) NULL;
Eric Andersena75466e2000-11-02 17:02:26 +0000308 int pos = *cursor;
Erik Andersenf0657d32000-04-12 17:49:52 +0000309
310
311 if (lastWasTab == FALSE) {
312 char *tmp, *tmp1, *matchBuf;
313
314 /* For now, we will not bother with trying to distinguish
315 * whether the cursor is in/at a command extression -- we
Eric Andersen74c66ad2000-06-16 19:57:44 +0000316 * will always try all possible matches. If you don't like
Erik Andersenf0657d32000-04-12 17:49:52 +0000317 * that then feel free to fix it.
318 */
319
320 /* Make a local copy of the string -- up
321 * to the position of the cursor */
Matt Kraai322ae932000-09-13 02:46:14 +0000322 matchBuf = (char *) xcalloc(BUFSIZ, sizeof(char));
Eric Andersena75466e2000-11-02 17:02:26 +0000323 strncpy(matchBuf, command, *cursor);
Erik Andersenf0657d32000-04-12 17:49:52 +0000324 tmp=matchBuf;
325
326 /* skip past any command seperator tokens */
327 while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
328 tmp=++tmp1;
329 /* skip any leading white space */
330 while (*tmp && isspace(*tmp))
331 ++tmp;
332 }
333
334 /* skip any leading white space */
335 while (*tmp && isspace(*tmp))
336 ++tmp;
337
338 /* Free up any memory already allocated */
339 if (matches) {
340 free(matches);
341 matches = (char **) NULL;
342 }
343
344 /* If the word starts with `~' and there is no slash in the word,
345 * then try completing this word as a username. */
346
347 /* FIXME -- this check is broken! */
348 if (*tmp == '~' && !strchr(tmp, '/'))
349 matches = username_tab_completion(tmp, &num_matches);
350
351 /* Try to match any executable in our path and everything
352 * in the current working directory that matches. */
353 if (!matches)
354 matches = exe_n_cwd_tab_completion(tmp, &num_matches);
355
356 /* Don't leak memory */
357 free( matchBuf);
358
359 /* Did we find exactly one match? */
360 if (matches && num_matches==1) {
361 /* write out the matched command */
362 strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos);
Eric Andersena75466e2000-11-02 17:02:26 +0000363 *len=strlen(command);
364 *cursor=*len;
Erik Andersenf0657d32000-04-12 17:49:52 +0000365 xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos);
Eric Andersena75466e2000-11-02 17:02:26 +0000366 return;
Erik Andersenf0657d32000-04-12 17:49:52 +0000367 }
368 } else {
369 /* Ok -- the last char was a TAB. Since they
370 * just hit TAB again, print a list of all the
371 * available choices... */
372 if ( matches && num_matches>0 ) {
373 int i, col;
374
375 /* Go to the next line */
376 xwrite(outputFd, "\n", 1);
377 /* Print the list of matches */
378 for (i=0,col=0; i<num_matches; i++) {
379 char foo[17];
380 sprintf(foo, "%-14s ", matches[i]);
381 col += xwrite(outputFd, foo, strlen(foo));
382 if (col > 60 && matches[i+1] != NULL) {
383 xwrite(outputFd, "\n", 1);
384 col = 0;
385 }
386 }
387 /* Go to the next line */
388 xwrite(outputFd, "\n", 1);
389 /* Rewrite the prompt */
390 xwrite(outputFd, prompt, strlen(prompt));
391 /* Rewrite the command */
Eric Andersena75466e2000-11-02 17:02:26 +0000392 xwrite(outputFd, command, *len);
Erik Andersenf0657d32000-04-12 17:49:52 +0000393 /* Put the cursor back to where it used to be */
Eric Andersena75466e2000-11-02 17:02:26 +0000394 for (cursor=len; *cursor > pos; cursor--)
Erik Andersenf0657d32000-04-12 17:49:52 +0000395 xwrite(outputFd, "\b", 1);
396 }
397 }
398}
399#endif
400
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000401static void get_previous_history(struct history **hp, char* command)
Erik Andersenf0657d32000-04-12 17:49:52 +0000402{
Eric Andersen91a44002000-07-19 17:37:57 +0000403 if ((*hp)->s)
404 free((*hp)->s);
Erik Andersenf0657d32000-04-12 17:49:52 +0000405 (*hp)->s = strdup(command);
406 *hp = (*hp)->p;
407}
408
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000409static void get_next_history(struct history **hp, char* command)
Erik Andersenf0657d32000-04-12 17:49:52 +0000410{
Eric Andersen91a44002000-07-19 17:37:57 +0000411 if ((*hp)->s)
412 free((*hp)->s);
Erik Andersenf0657d32000-04-12 17:49:52 +0000413 (*hp)->s = strdup(command);
414 *hp = (*hp)->n;
Erik Andersenf0657d32000-04-12 17:49:52 +0000415}
416
Erik Andersen6273f652000-03-17 01:12:41 +0000417/*
418 * This function is used to grab a character buffer
419 * from the input file descriptor and allows you to
420 * a string with full command editing (sortof like
421 * a mini readline).
422 *
423 * The following standard commands are not implemented:
424 * ESC-b -- Move back one word
425 * ESC-f -- Move forward one word
426 * ESC-d -- Delete back one word
427 * ESC-h -- Delete forward one word
428 * CTL-t -- Transpose two characters
429 *
430 * Furthermore, the "vi" command editing keys are not implemented.
431 *
432 * TODO: implement TAB command completion. :)
Erik Andersen6273f652000-03-17 01:12:41 +0000433 */
Erik Andersenf0657d32000-04-12 17:49:52 +0000434extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
Erik Andersen13456d12000-03-16 08:09:57 +0000435{
436
Erik Andersenf0657d32000-04-12 17:49:52 +0000437 int inputFd=fileno(stdin);
438 int outputFd=fileno(stdout);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000439 int nr = 0;
440 int len = 0;
441 int j = 0;
442 int cursor = 0;
443 int break_out = 0;
444 int ret = 0;
445 int lastWasTab = FALSE;
446 char c = 0;
447 struct history *hp = his_end;
Erik Andersen13456d12000-03-16 08:09:57 +0000448
Erik Andersenc7c634b2000-03-19 05:28:55 +0000449 if (!reset_term) {
Erik Andersen1d1d9502000-04-21 01:26:49 +0000450
451 getTermSettings(inputFd, (void*) &initial_settings);
452 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
453 new_settings.c_cc[VMIN] = 1;
454 new_settings.c_cc[VTIME] = 0;
455 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */
456 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
457 new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000458 reset_term = 1;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000459 }
Erik Andersen1d1d9502000-04-21 01:26:49 +0000460 setTermSettings(inputFd, (void*) &new_settings);
Erik Andersen13456d12000-03-16 08:09:57 +0000461
Erik Andersenf0657d32000-04-12 17:49:52 +0000462 memset(command, 0, BUFSIZ);
Erik Andersen13456d12000-03-16 08:09:57 +0000463
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000464 /* Print out the command prompt */
465 xwrite(outputFd, prompt, strlen(prompt));
466
Erik Andersenc7c634b2000-03-19 05:28:55 +0000467 while (1) {
Erik Andersen6273f652000-03-17 01:12:41 +0000468
Erik Andersen13456d12000-03-16 08:09:57 +0000469 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000470 return;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000471 //fprintf(stderr, "got a '%c' (%d)\n", c, c);
Erik Andersenf3b3d172000-04-09 18:24:05 +0000472
Erik Andersen13456d12000-03-16 08:09:57 +0000473 switch (c) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000474 case '\n':
475 case '\r':
476 /* Enter */
477 *(command + len++ + 1) = c;
478 xwrite(outputFd, &c, 1);
479 break_out = 1;
480 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000481 case 1:
482 /* Control-a -- Beginning of line */
483 input_home(outputFd, &cursor);
Erik Andersenf0657d32000-04-12 17:49:52 +0000484 case 2:
485 /* Control-b -- Move back one character */
486 input_backward(outputFd, &cursor);
487 break;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000488 case 3:
Eric Andersen86349772000-12-18 20:25:50 +0000489 /* Control-c -- stop gathering input */
490
491 /* Link into lash to reset context to 0 on ^C and such */
492 shell_context = 0;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000493
494 /* Go to the next line */
495 xwrite(outputFd, "\n", 1);
496
Eric Andersen86349772000-12-18 20:25:50 +0000497#if 0
Erik Andersen1d1d9502000-04-21 01:26:49 +0000498 /* Rewrite the prompt */
499 xwrite(outputFd, prompt, strlen(prompt));
500
501 /* Reset the command string */
Eric Andersen4ac6cb52000-07-14 01:13:37 +0000502 memset(command, 0, BUFSIZ);
Erik Andersen1d1d9502000-04-21 01:26:49 +0000503 len = cursor = 0;
Eric Andersen86349772000-12-18 20:25:50 +0000504#endif
505 return;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000506
Erik Andersenf0657d32000-04-12 17:49:52 +0000507 case 4:
508 /* Control-d -- Delete one character, or exit
509 * if the len=0 and no chars to delete */
510 if (len == 0) {
511 xwrite(outputFd, "exit", 4);
512 clean_up_and_die(0);
513 } else {
514 input_delete(command, outputFd, cursor, &len);
515 }
516 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000517 case 5:
518 /* Control-e -- End of line */
519 input_end(outputFd, &cursor, len);
520 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000521 case 6:
522 /* Control-f -- Move forward one character */
Erik Andersenf0657d32000-04-12 17:49:52 +0000523 input_forward(outputFd, &cursor, len);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000524 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000525 case '\b':
526 case DEL:
Erik Andersen1d1d9502000-04-21 01:26:49 +0000527 /* Control-h and DEL */
Erik Andersenf0657d32000-04-12 17:49:52 +0000528 input_backspace(command, outputFd, &cursor, &len);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000529 break;
530 case '\t':
Erik Andersena2685732000-04-09 18:27:46 +0000531#ifdef BB_FEATURE_SH_TAB_COMPLETION
Eric Andersena75466e2000-11-02 17:02:26 +0000532 input_tab(command, prompt, outputFd, &cursor,
533&len, lastWasTab);
Erik Andersena2685732000-04-09 18:27:46 +0000534#endif
Erik Andersenc7c634b2000-03-19 05:28:55 +0000535 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000536 case 14:
537 /* Control-n -- Get next command in history */
538 if (hp && hp->n && hp->n->s) {
539 get_next_history(&hp, command);
540 goto rewrite_line;
541 } else {
542 xwrite(outputFd, "\007", 1);
543 }
544 break;
545 case 16:
546 /* Control-p -- Get previous command from history */
547 if (hp && hp->p) {
548 get_previous_history(&hp, command);
549 goto rewrite_line;
550 } else {
551 xwrite(outputFd, "\007", 1);
552 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000553 break;
554 case ESC:{
555 /* escape sequence follows */
556 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000557 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000558
559 if (c == '[') { /* 91 */
560 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000561 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000562
563 switch (c) {
564 case 'A':
Erik Andersenf0657d32000-04-12 17:49:52 +0000565 /* Up Arrow -- Get previous command from history */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000566 if (hp && hp->p) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000567 get_previous_history(&hp, command);
568 goto rewrite_line;
569 } else {
570 xwrite(outputFd, "\007", 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000571 }
572 break;
573 case 'B':
Erik Andersenf0657d32000-04-12 17:49:52 +0000574 /* Down Arrow -- Get next command in history */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000575 if (hp && hp->n && hp->n->s) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000576 get_next_history(&hp, command);
577 goto rewrite_line;
578 } else {
579 xwrite(outputFd, "\007", 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000580 }
581 break;
582
Erik Andersenf0657d32000-04-12 17:49:52 +0000583 /* Rewrite the line with the selected history item */
584 rewrite_line:
585 /* erase old command from command line */
586 len = strlen(command)-strlen(hp->s);
Eric Andersen72965e32000-07-04 06:22:18 +0000587
588 while (len>cursor)
589 input_delete(command, outputFd, cursor, &len);
590 while (cursor>0)
Erik Andersenf0657d32000-04-12 17:49:52 +0000591 input_backspace(command, outputFd, &cursor, &len);
592 input_home(outputFd, &cursor);
593
Erik Andersenc7c634b2000-03-19 05:28:55 +0000594 /* write new command */
Erik Andersenf0657d32000-04-12 17:49:52 +0000595 strcpy(command, hp->s);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000596 len = strlen(hp->s);
Erik Andersenf0657d32000-04-12 17:49:52 +0000597 xwrite(outputFd, command, len);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000598 cursor = len;
599 break;
600 case 'C':
601 /* Right Arrow -- Move forward one character */
Erik Andersenf0657d32000-04-12 17:49:52 +0000602 input_forward(outputFd, &cursor, len);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000603 break;
604 case 'D':
605 /* Left Arrow -- Move back one character */
Erik Andersenf0657d32000-04-12 17:49:52 +0000606 input_backward(outputFd, &cursor);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000607 break;
608 case '3':
609 /* Delete */
Erik Andersenf0657d32000-04-12 17:49:52 +0000610 input_delete(command, outputFd, cursor, &len);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000611 break;
612 case '1':
613 /* Home (Ctrl-A) */
614 input_home(outputFd, &cursor);
615 break;
616 case '4':
617 /* End (Ctrl-E) */
618 input_end(outputFd, &cursor, len);
619 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000620 default:
621 xwrite(outputFd, "\007", 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000622 }
623 if (c == '1' || c == '3' || c == '4')
624 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000625 return; /* read 126 (~) */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000626 }
627 if (c == 'O') {
628 /* 79 */
629 if ((ret = read(inputFd, &c, 1)) < 1)
Erik Andersenf0657d32000-04-12 17:49:52 +0000630 return;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000631 switch (c) {
632 case 'H':
633 /* Home (xterm) */
634 input_home(outputFd, &cursor);
635 break;
636 case 'F':
637 /* End (xterm) */
638 input_end(outputFd, &cursor, len);
639 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000640 default:
641 xwrite(outputFd, "\007", 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000642 }
643 }
644 c = 0;
645 break;
646 }
647
648 default: /* If it's regular input, do the normal thing */
649
Erik Andersenf0657d32000-04-12 17:49:52 +0000650 if (!isprint(c)) { /* Skip non-printable characters */
Erik Andersenc7c634b2000-03-19 05:28:55 +0000651 break;
Erik Andersenf0657d32000-04-12 17:49:52 +0000652 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000653
654 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
655 break;
656
657 len++;
658
659 if (cursor == (len - 1)) { /* Append if at the end of the line */
Erik Andersenf0657d32000-04-12 17:49:52 +0000660 *(command + cursor) = c;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000661 } else { /* Insert otherwise */
Erik Andersenf0657d32000-04-12 17:49:52 +0000662 memmove(command + cursor + 1, command + cursor,
Erik Andersenc7c634b2000-03-19 05:28:55 +0000663 len - cursor - 1);
664
Erik Andersenf0657d32000-04-12 17:49:52 +0000665 *(command + cursor) = c;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000666
667 for (j = cursor; j < len; j++)
Erik Andersenf0657d32000-04-12 17:49:52 +0000668 xwrite(outputFd, command + j, 1);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000669 for (; j > cursor; j--)
670 xwrite(outputFd, "\033[D", 3);
671 }
672
Erik Andersen13456d12000-03-16 08:09:57 +0000673 cursor++;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000674 xwrite(outputFd, &c, 1);
675 break;
Erik Andersen13456d12000-03-16 08:09:57 +0000676 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000677 if (c == '\t')
678 lastWasTab = TRUE;
679 else
680 lastWasTab = FALSE;
681
682 if (break_out) /* Enter is the command terminator, no more input. */
683 break;
684 }
685
686 nr = len + 1;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000687 setTermSettings(inputFd, (void *) &initial_settings);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000688 reset_term = 0;
689
690
691 /* Handle command history log */
Erik Andersenf0657d32000-04-12 17:49:52 +0000692 if (*(command)) {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000693
694 struct history *h = his_end;
695
696 if (!h) {
Eric Andersen91a44002000-07-19 17:37:57 +0000697 /* No previous history -- this memory is never freed */
Matt Kraai322ae932000-09-13 02:46:14 +0000698 h = his_front = xmalloc(sizeof(struct history));
699 h->n = xmalloc(sizeof(struct history));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000700
701 h->p = NULL;
Erik Andersenf0657d32000-04-12 17:49:52 +0000702 h->s = strdup(command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000703 h->n->p = h;
704 h->n->n = NULL;
705 h->n->s = NULL;
706 his_end = h->n;
707 history_counter++;
708 } else {
Eric Andersen91a44002000-07-19 17:37:57 +0000709 /* Add a new history command -- this memory is never freed */
Matt Kraai322ae932000-09-13 02:46:14 +0000710 h->n = xmalloc(sizeof(struct history));
Erik Andersenc7c634b2000-03-19 05:28:55 +0000711
712 h->n->p = h;
713 h->n->n = NULL;
714 h->n->s = NULL;
Erik Andersenf0657d32000-04-12 17:49:52 +0000715 h->s = strdup(command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000716 his_end = h->n;
717
718 /* After max history, remove the oldest command */
719 if (history_counter >= MAX_HISTORY) {
720
721 struct history *p = his_front->n;
722
723 p->p = NULL;
724 free(his_front->s);
725 free(his_front);
726 his_front = p;
727 } else {
728 history_counter++;
729 }
Erik Andersen13456d12000-03-16 08:09:57 +0000730 }
Erik Andersen6273f652000-03-17 01:12:41 +0000731 }
Erik Andersen13456d12000-03-16 08:09:57 +0000732
Erik Andersenf0657d32000-04-12 17:49:52 +0000733 return;
Erik Andersen13456d12000-03-16 08:09:57 +0000734}
735
736extern void cmdedit_init(void)
737{
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000738 win_changed(0);
739 signal(SIGWINCH, win_changed);
740
Eric Andersen501c88b2000-07-28 15:14:45 +0000741 if(exithandler_set == 0) {
742 atexit(cmdedit_reset_term); /* be sure to do this only once */
743 exithandler_set = 1;
744 }
Erik Andersen1d1d9502000-04-21 01:26:49 +0000745 signal(SIGKILL, clean_up_and_die);
Erik Andersenf0657d32000-04-12 17:49:52 +0000746 signal(SIGINT, clean_up_and_die);
747 signal(SIGQUIT, clean_up_and_die);
748 signal(SIGTERM, clean_up_and_die);
Erik Andersen13456d12000-03-16 08:09:57 +0000749}
Eric Andersen501c88b2000-07-28 15:14:45 +0000750
751/*
752** Undo the effects of cmdedit_init() as good as we can:
753** I am not aware of a way to revoke an atexit() handler,
754** but, fortunately, our particular handler can be made
755** a no-op by setting reset_term = 0.
756*/
757extern void cmdedit_terminate(void)
758{
759 cmdedit_reset_term();
760 reset_term = 0;
761 signal(SIGKILL, SIG_DFL);
762 signal(SIGINT, SIG_DFL);
763 signal(SIGQUIT, SIG_DFL);
764 signal(SIGTERM, SIG_DFL);
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000765 signal(SIGWINCH, SIG_DFL);
Eric Andersen501c88b2000-07-28 15:14:45 +0000766}
767
768
769
Erik Andersenc7c634b2000-03-19 05:28:55 +0000770#endif /* BB_FEATURE_SH_COMMAND_EDITING */