Fix calls to {m,c,re}alloc so that they use x{m,c,re}alloc instead of
[oweals/busybox.git] / shell / cmdedit.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Termios command line History and Editting, originally 
4  * intended for NetBSD sh (ash)
5  * Copyright (c) 1999
6  *      Main code:            Adam Rogoyski <rogoyski@cs.utexas.edu> 
7  *      Etc:                  Dave Cinege <dcinege@psychosis.com>
8  *  Majorly adjusted/re-written for busybox:
9  *                            Erik Andersen <andersee@debian.org>
10  *
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
34 #include "internal.h"
35 #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>
42 #include <sys/ioctl.h>
43 #include <ctype.h>
44 #include <signal.h>
45
46
47 #define  MAX_HISTORY   15               /* Maximum length of the linked list for the command line history */
48
49 #define ESC     27
50 #define DEL     127
51 #define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0)
52 #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
53
54 static struct history *his_front = NULL;        /* First element in command line list */
55 static struct history *his_end = NULL;  /* Last element in command line list */
56
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 */
72 struct termios initial_settings, new_settings;
73
74
75 #ifndef _POSIX_VDISABLE
76 #define _POSIX_VDISABLE '\0'
77 #endif
78
79 #endif
80
81
82
83 static int cmdedit_termw = 80;  /* actual terminal width */
84 static int cmdedit_scroll = 27; /* width of EOL scrolling region */
85 static int history_counter = 0; /* Number of commands in history list */
86 static int reset_term = 0;              /* Set to true if the terminal needs to be reset upon exit */
87 static int exithandler_set = 0; /* Set to true when atexit() has been called */
88
89 struct history {
90         char *s;
91         struct history *p;
92         struct history *n;
93 };
94
95 #define xwrite write
96
97 /*
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  */
102 void
103 cmdedit_setwidth(int w)
104 {
105         if (w > 20) {
106                 cmdedit_termw = w;
107                 cmdedit_scroll = w / 3;
108         } else {
109                 errorMsg("\n*** Error: minimum screen width is 21\n");
110         }
111 }
112
113
114 void cmdedit_reset_term(void)
115 {
116         if (reset_term)
117                 /* sparc and other have broken termios support: use old termio handling. */
118                 setTermSettings(fileno(stdin), (void*) &initial_settings);
119 #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
131 }
132
133 void clean_up_and_die(int sig)
134 {
135         cmdedit_reset_term();
136         fprintf(stdout, "\n");
137         if (sig!=SIGINT)
138                 exit(TRUE);
139 }
140
141 /* Go to HOME position */
142 void input_home(int outputFd, int *cursor)
143 {
144         while (*cursor > 0) {
145                 xwrite(outputFd, "\b", 1);
146                 --*cursor;
147         }
148 }
149
150 /* Go to END position */
151 void input_end(int outputFd, int *cursor, int len)
152 {
153         while (*cursor < len) {
154                 xwrite(outputFd, "\033[C", 3);
155                 ++*cursor;
156         }
157 }
158
159 /* Delete the char in back of the cursor */
160 void input_backspace(char* command, int outputFd, int *cursor, int *len)
161 {
162         int j = 0;
163
164 /* 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
170         if (*cursor > 0) {
171                 xwrite(outputFd, "\b \b", 3);
172                 --*cursor;
173                 memmove(command + *cursor, command + *cursor + 1,
174                                 BUFSIZ - *cursor + 1);
175
176                 for (j = *cursor; j < (BUFSIZ - 1); j++) {
177                         if (!*(command + j))
178                                 break;
179                         else
180                                 xwrite(outputFd, (command + j), 1);
181                 }
182
183                 xwrite(outputFd, " \b", 2);
184
185                 while (j-- > *cursor)
186                         xwrite(outputFd, "\b", 1);
187
188                 --*len;
189         }
190 }
191
192 /* Delete the char in front of the cursor */
193 void input_delete(char* command, int outputFd, int cursor, int *len)
194 {
195         int j = 0;
196
197         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 */
217 void 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 */
226 void 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
237 char** username_tab_completion(char* command, int *num_matches)
238 {
239         char **matches = (char **) NULL;
240         *num_matches=0;
241         fprintf(stderr, "\nin username_tab_completion\n");
242         return (matches);
243 }
244
245 #include <dirent.h>
246 char** exe_n_cwd_tab_completion(char* command, int *num_matches)
247 {
248         char *dirName;
249         char **matches;
250         DIR *dir;
251         struct dirent *next;
252                         
253         matches = xmalloc( sizeof(char*)*50);
254
255         /* Stick a wildcard onto the command, for later use */
256         strcat( command, "*");
257
258         /* 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) {
267
268                 /* 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 */
276                         matches[*num_matches] = xmalloc(strlen(next->d_name)+1);
277                         strcpy( matches[*num_matches], next->d_name);
278                         ++*num_matches;
279                         //matches = realloc( matches, sizeof(char*)*(*num_matches));
280                 }
281         }
282
283         return (matches);
284 }
285
286 void input_tab(char* command, char* prompt, int outputFd, int *cursor, int *len)
287 {
288         /* Do TAB completion */
289         static int num_matches=0;
290         static char **matches = (char **) NULL;
291         int pos = cursor;
292
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
299                  * will always try all possible matches.  If you don't like
300                  * 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 */
305                 matchBuf = (char *) xcalloc(BUFSIZ, sizeof(char));
306                 strncpy(matchBuf, command, cursor);
307                 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);
346                         len=strlen(command);
347                         cursor=len;
348                         xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos);
349                         break;
350                 }
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 */
375                         xwrite(outputFd, command, len);
376                         /* Put the cursor back to where it used to be */
377                         for (cursor=len; cursor > pos; cursor--)
378                                 xwrite(outputFd, "\b", 1);
379                 }
380         }
381 }
382 #endif
383
384 void get_previous_history(struct history **hp, char* command)
385 {
386         if ((*hp)->s)
387                 free((*hp)->s);
388         (*hp)->s = strdup(command);
389         *hp = (*hp)->p;
390 }
391
392 void get_next_history(struct history **hp, char* command)
393 {
394         if ((*hp)->s)
395                 free((*hp)->s);
396         (*hp)->s = strdup(command);
397         *hp = (*hp)->n;
398 }
399
400 /*
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. :)
416  */
417 extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
418 {
419
420         int inputFd=fileno(stdin);
421         int outputFd=fileno(stdout);
422         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;
431
432         if (!reset_term) {
433                 
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 */
441                 reset_term = 1;
442         }
443         setTermSettings(inputFd, (void*) &new_settings);
444
445         memset(command, 0, BUFSIZ);
446
447         while (1) {
448
449                 if ((ret = read(inputFd, &c, 1)) < 1)
450                         return;
451                 //fprintf(stderr, "got a '%c' (%d)\n", c, c);
452
453                 switch (c) {
454                 case '\n':
455                 case '\r':
456                         /* Enter */
457                         *(command + len++ + 1) = c;
458                         xwrite(outputFd, &c, 1);
459                         break_out = 1;
460                         break;
461                 case 1:
462                         /* Control-a -- Beginning of line */
463                         input_home(outputFd, &cursor);
464                 case 2:
465                         /* Control-b -- Move back one character */
466                         input_backward(outputFd, &cursor);
467                         break;
468                 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 */
479                         memset(command, 0, BUFSIZ);
480                         len = cursor = 0;
481
482                         break;
483                 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;
493                 case 5:
494                         /* Control-e -- End of line */
495                         input_end(outputFd, &cursor, len);
496                         break;
497                 case 6:
498                         /* Control-f -- Move forward one character */
499                         input_forward(outputFd, &cursor, len);
500                         break;
501                 case '\b':
502                 case DEL:
503                         /* Control-h and DEL */
504                         input_backspace(command, outputFd, &cursor, &len);
505                         break;
506                 case '\t':
507 #ifdef BB_FEATURE_SH_TAB_COMPLETION
508                         input_tab(command, prompt, outputFd, &cursor, &len);
509 #endif
510                         break;
511                 case 14:
512                         /* Control-n -- Get next command in history */
513                         if (hp && hp->n && hp->n->s) {
514                                 get_next_history(&hp, command);
515                                 goto rewrite_line;
516                         } else {
517                                 xwrite(outputFd, "\007", 1);
518                         }
519                         break;
520                 case 16:
521                         /* Control-p -- Get previous command from history */
522                         if (hp && hp->p) {
523                                 get_previous_history(&hp, command);
524                                 goto rewrite_line;
525                         } else {
526                                 xwrite(outputFd, "\007", 1);
527                         }
528                         break;
529                 case ESC:{
530                                 /* escape sequence follows */
531                                 if ((ret = read(inputFd, &c, 1)) < 1)
532                                         return;
533
534                                 if (c == '[') { /* 91 */
535                                         if ((ret = read(inputFd, &c, 1)) < 1)
536                                                 return;
537
538                                         switch (c) {
539                                         case 'A':
540                                                 /* Up Arrow -- Get previous command from history */
541                                                 if (hp && hp->p) {
542                                                         get_previous_history(&hp, command);
543                                                         goto rewrite_line;
544                                                 } else {
545                                                         xwrite(outputFd, "\007", 1);
546                                                 }
547                                                 break;
548                                         case 'B':
549                                                 /* Down Arrow -- Get next command in history */
550                                                 if (hp && hp->n && hp->n->s) {
551                                                         get_next_history(&hp, command);
552                                                         goto rewrite_line;
553                                                 } else {
554                                                         xwrite(outputFd, "\007", 1);
555                                                 }
556                                                 break;
557
558                                                 /* Rewrite the line with the selected history item */
559                                           rewrite_line:
560                                                 /* erase old command from command line */
561                                                 len = strlen(command)-strlen(hp->s);
562
563                                                 while (len>cursor)
564                                                         input_delete(command, outputFd, cursor, &len);
565                                                 while (cursor>0)
566                                                         input_backspace(command, outputFd, &cursor, &len);
567                                                 input_home(outputFd, &cursor);
568                                                 
569                                                 /* write new command */
570                                                 strcpy(command, hp->s);
571                                                 len = strlen(hp->s);
572                                                 xwrite(outputFd, command, len);
573                                                 cursor = len;
574                                                 break;
575                                         case 'C':
576                                                 /* Right Arrow -- Move forward one character */
577                                                 input_forward(outputFd, &cursor, len);
578                                                 break;
579                                         case 'D':
580                                                 /* Left Arrow -- Move back one character */
581                                                 input_backward(outputFd, &cursor);
582                                                 break;
583                                         case '3':
584                                                 /* Delete */
585                                                 input_delete(command, outputFd, cursor, &len);
586                                                 break;
587                                         case '1':
588                                                 /* Home (Ctrl-A) */
589                                                 input_home(outputFd, &cursor);
590                                                 break;
591                                         case '4':
592                                                 /* End (Ctrl-E) */
593                                                 input_end(outputFd, &cursor, len);
594                                                 break;
595                                         default:
596                                                 xwrite(outputFd, "\007", 1);
597                                         }
598                                         if (c == '1' || c == '3' || c == '4')
599                                                 if ((ret = read(inputFd, &c, 1)) < 1)
600                                                         return; /* read 126 (~) */
601                                 }
602                                 if (c == 'O') {
603                                         /* 79 */
604                                         if ((ret = read(inputFd, &c, 1)) < 1)
605                                                 return;
606                                         switch (c) {
607                                         case 'H':
608                                                 /* Home (xterm) */
609                                                 input_home(outputFd, &cursor);
610                                                 break;
611                                         case 'F':
612                                                 /* End (xterm) */
613                                                 input_end(outputFd, &cursor, len);
614                                                 break;
615                                         default:
616                                                 xwrite(outputFd, "\007", 1);
617                                         }
618                                 }
619                                 c = 0;
620                                 break;
621                         }
622
623                 default:                                /* If it's regular input, do the normal thing */
624
625                         if (!isprint(c)) {      /* Skip non-printable characters */
626                                 break;
627                         }
628
629                         if (len >= (BUFSIZ - 2))        /* Need to leave space for enter */
630                                 break;
631
632                         len++;
633
634                         if (cursor == (len - 1)) {      /* Append if at the end of the line */
635                                 *(command + cursor) = c;
636                         } else {                        /* Insert otherwise */
637                                 memmove(command + cursor + 1, command + cursor,
638                                                 len - cursor - 1);
639
640                                 *(command + cursor) = c;
641
642                                 for (j = cursor; j < len; j++)
643                                         xwrite(outputFd, command + j, 1);
644                                 for (; j > cursor; j--)
645                                         xwrite(outputFd, "\033[D", 3);
646                         }
647
648                         cursor++;
649                         xwrite(outputFd, &c, 1);
650                         break;
651                 }
652                 if (c == '\t')
653                         lastWasTab = TRUE;
654                 else
655                         lastWasTab = FALSE;
656
657                 if (break_out)                  /* Enter is the command terminator, no more input. */
658                         break;
659         }
660
661         nr = len + 1;
662         setTermSettings(inputFd, (void *) &initial_settings);
663         reset_term = 0;
664
665
666         /* Handle command history log */
667         if (*(command)) {
668
669                 struct history *h = his_end;
670
671                 if (!h) {
672                         /* No previous history -- this memory is never freed */
673                         h = his_front = xmalloc(sizeof(struct history));
674                         h->n = xmalloc(sizeof(struct history));
675
676                         h->p = NULL;
677                         h->s = strdup(command);
678                         h->n->p = h;
679                         h->n->n = NULL;
680                         h->n->s = NULL;
681                         his_end = h->n;
682                         history_counter++;
683                 } else {
684                         /* Add a new history command -- this memory is never freed */
685                         h->n = xmalloc(sizeof(struct history));
686
687                         h->n->p = h;
688                         h->n->n = NULL;
689                         h->n->s = NULL;
690                         h->s = strdup(command);
691                         his_end = h->n;
692
693                         /* After max history, remove the oldest command */
694                         if (history_counter >= MAX_HISTORY) {
695
696                                 struct history *p = his_front->n;
697
698                                 p->p = NULL;
699                                 free(his_front->s);
700                                 free(his_front);
701                                 his_front = p;
702                         } else {
703                                 history_counter++;
704                         }
705                 }
706         }
707
708         return;
709 }
710
711 extern void cmdedit_init(void)
712 {
713         if(exithandler_set == 0) {
714                 atexit(cmdedit_reset_term);     /* be sure to do this only once */
715                 exithandler_set = 1;
716         }
717         signal(SIGKILL, clean_up_and_die);
718         signal(SIGINT, clean_up_and_die);
719         signal(SIGQUIT, clean_up_and_die);
720         signal(SIGTERM, clean_up_and_die);
721 }
722
723 /*
724 ** Undo the effects of cmdedit_init() as good as we can:
725 ** I am not aware of a way to revoke an atexit() handler,
726 ** but, fortunately, our particular handler can be made
727 ** a no-op by setting reset_term = 0.
728 */
729 extern void cmdedit_terminate(void)
730 {
731         cmdedit_reset_term();
732         reset_term = 0;
733         signal(SIGKILL, SIG_DFL);
734         signal(SIGINT, SIG_DFL);
735         signal(SIGQUIT, SIG_DFL);
736         signal(SIGTERM, SIG_DFL);
737 }
738
739
740
741 #endif                                                  /* BB_FEATURE_SH_COMMAND_EDITING */