source: MondoRescue/branches/stable/mindi-busybox/shell/cmdedit.c @ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 14 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 44.1 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Termios command line History and Editing.
4 *
5 * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
6 * Written by:   Vladimir Oleynik <dzo@simtreas.ru>
7 *
8 * Used ideas:
9 *      Adam Rogoyski    <rogoyski@cs.utexas.edu>
10 *      Dave Cinege      <dcinege@psychosis.com>
11 *      Jakub Jelinek (c) 1995
12 *      Erik Andersen    <andersen@codepoet.org> (Majorly adjusted for busybox)
13 *
14 * This code is 'as is' with no warranty.
15 *
16 *
17 */
18
19/*
20   Usage and Known bugs:
21   Terminal key codes are not extensive, and more will probably
22   need to be added. This version was created on Debian GNU/Linux 2.x.
23   Delete, Backspace, Home, End, and the arrow keys were tested
24   to work in an Xterm and console. Ctrl-A also works as Home.
25   Ctrl-E also works as End.
26
27   Small bugs (simple effect):
28   - not true viewing if terminal size (x*y symbols) less
29     size (prompt + editor`s line + 2 symbols)
30   - not true viewing if length prompt less terminal width
31 */
32
33
34#include "busybox.h"
35#include <stdio.h>
36#include <errno.h>
37#include <unistd.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/ioctl.h>
41#include <ctype.h>
42#include <signal.h>
43#include <limits.h>
44
45#include "../shell/cmdedit.h"
46
47
48#ifdef CONFIG_LOCALE_SUPPORT
49#define Isprint(c) isprint((c))
50#else
51#define Isprint(c) ( (c) >= ' ' && (c) != ((unsigned char)'\233') )
52#endif
53
54#ifdef TEST
55
56/* pretect redefined for test */
57#undef CONFIG_FEATURE_COMMAND_EDITING
58#undef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
59#undef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
60#undef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
61#undef CONFIG_FEATURE_CLEAN_UP
62
63#define CONFIG_FEATURE_COMMAND_EDITING
64#define CONFIG_FEATURE_COMMAND_TAB_COMPLETION
65#define CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
66#define CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
67#define CONFIG_FEATURE_CLEAN_UP
68
69#endif  /* TEST */
70
71#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
72#include <dirent.h>
73#include <sys/stat.h>
74#endif
75
76#ifdef CONFIG_FEATURE_COMMAND_EDITING
77
78#if defined(CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION) || defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
79#define CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
80#endif
81
82#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
83#include "pwd_.h"
84#endif  /* advanced FEATURES */
85
86
87/* Maximum length of the linked list for the command line history */
88#ifndef CONFIG_FEATURE_COMMAND_HISTORY
89#define MAX_HISTORY   15
90#else
91#define MAX_HISTORY   CONFIG_FEATURE_COMMAND_HISTORY
92#endif
93
94#if MAX_HISTORY < 1
95#warning cmdedit: You set MAX_HISTORY < 1. The history algorithm switched off.
96#else
97static char *history[MAX_HISTORY+1]; /* history + current */
98/* saved history lines */
99static int n_history;
100/* current pointer to history line */
101static int cur_history;
102#endif
103
104#include <termios.h>
105#define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
106#define getTermSettings(fd,argp) tcgetattr(fd, argp);
107
108/* Current termio and the previous termio before starting sh */
109static struct termios initial_settings, new_settings;
110
111
112static
113volatile int cmdedit_termw = 80;        /* actual terminal width */
114static
115volatile int handlers_sets = 0; /* Set next bites: */
116
117enum {
118    SET_ATEXIT = 1,         /* when atexit() has been called
119                   and get euid,uid,gid to fast compare */
120    SET_WCHG_HANDLERS = 2,  /* winchg signal handler */
121    SET_RESET_TERM = 4,     /* if the terminal needs to be reset upon exit */
122};
123
124
125static int cmdedit_x;           /* real x terminal position */
126static int cmdedit_y;           /* pseudoreal y terminal position */
127static int cmdedit_prmt_len;    /* lenght prompt without colores string */
128
129static int cursor;              /* required global for signal handler */
130static int len;                 /* --- "" - - "" - -"- --""-- --""--- */
131static char *command_ps;        /* --- "" - - "" - -"- --""-- --""--- */
132static
133#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
134    const
135#endif
136char *cmdedit_prompt;           /* --- "" - - "" - -"- --""-- --""--- */
137
138#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
139static char *user_buf = "";
140static char *home_pwd_buf = "";
141static int my_euid;
142#endif
143
144#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
145static char *hostname_buf;
146static int num_ok_lines = 1;
147#endif
148
149
150#ifdef  CONFIG_FEATURE_COMMAND_TAB_COMPLETION
151
152#ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
153static int my_euid;
154#endif
155
156static int my_uid;
157static int my_gid;
158
159#endif  /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
160
161static void cmdedit_setwidth(int w, int redraw_flg);
162
163static void win_changed(int nsig)
164{
165    static sighandler_t previous_SIGWINCH_handler;  /* for reset */
166
167    /*   emulate      || signal call */
168    if (nsig == -SIGWINCH || nsig == SIGWINCH) {
169        int width = 0;
170        get_terminal_width_height(0, &width, NULL);
171        cmdedit_setwidth(width, nsig == SIGWINCH);
172    }
173    /* Unix not all standart in recall signal */
174
175    if (nsig == -SIGWINCH)          /* save previous handler   */
176        previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
177    else if (nsig == SIGWINCH)      /* signaled called handler */
178        signal(SIGWINCH, win_changed);  /* set for next call       */
179    else                                            /* nsig == 0 */
180        /* set previous handler    */
181        signal(SIGWINCH, previous_SIGWINCH_handler);    /* reset    */
182}
183
184static void cmdedit_reset_term(void)
185{
186    if ((handlers_sets & SET_RESET_TERM) != 0) {
187/* sparc and other have broken termios support: use old termio handling. */
188        setTermSettings(STDIN_FILENO, (void *) &initial_settings);
189        handlers_sets &= ~SET_RESET_TERM;
190    }
191    if ((handlers_sets & SET_WCHG_HANDLERS) != 0) {
192        /* reset SIGWINCH handler to previous (default) */
193        win_changed(0);
194        handlers_sets &= ~SET_WCHG_HANDLERS;
195    }
196    fflush(stdout);
197}
198
199
200/* special for recount position for scroll and remove terminal margin effect */
201static void cmdedit_set_out_char(int next_char)
202{
203
204    int c = (int)((unsigned char) command_ps[cursor]);
205
206    if (c == 0)
207        c = ' ';        /* destroy end char? */
208#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
209    if (!Isprint(c)) {      /* Inverse put non-printable characters */
210        if (c >= 128)
211            c -= 128;
212        if (c < ' ')
213            c += '@';
214        if (c == 127)
215            c = '?';
216        printf("\033[7m%c\033[0m", c);
217    } else
218#endif
219        putchar(c);
220    if (++cmdedit_x >= cmdedit_termw) {
221        /* terminal is scrolled down */
222        cmdedit_y++;
223        cmdedit_x = 0;
224
225        if (!next_char)
226            next_char = ' ';
227        /* destroy "(auto)margin" */
228        putchar(next_char);
229        putchar('\b');
230    }
231    cursor++;
232}
233
234/* Move to end line. Bonus: rewrite line from cursor */
235static void input_end(void)
236{
237    while (cursor < len)
238        cmdedit_set_out_char(0);
239}
240
241/* Go to the next line */
242static void goto_new_line(void)
243{
244    input_end();
245    if (cmdedit_x)
246        putchar('\n');
247}
248
249
250static inline void out1str(const char *s)
251{
252    if ( s )
253        fputs(s, stdout);
254}
255
256static inline void beep(void)
257{
258    putchar('\007');
259}
260
261/* Move back one character */
262/* special for slow terminal */
263static void input_backward(int num)
264{
265    if (num > cursor)
266        num = cursor;
267    cursor -= num;          /* new cursor (in command, not terminal) */
268
269    if (cmdedit_x >= num) {         /* no to up line */
270        cmdedit_x -= num;
271        if (num < 4)
272            while (num-- > 0)
273                putchar('\b');
274
275        else
276            printf("\033[%dD", num);
277    } else {
278        int count_y;
279
280        if (cmdedit_x) {
281            putchar('\r');          /* back to first terminal pos.  */
282            num -= cmdedit_x;       /* set previous backward        */
283        }
284        count_y = 1 + num / cmdedit_termw;
285        printf("\033[%dA", count_y);
286        cmdedit_y -= count_y;
287        /*  require  forward  after  uping   */
288        cmdedit_x = cmdedit_termw * count_y - num;
289        printf("\033[%dC", cmdedit_x);  /* set term cursor   */
290    }
291}
292
293static void put_prompt(void)
294{
295    out1str(cmdedit_prompt);
296    cmdedit_x = cmdedit_prmt_len;   /* count real x terminal position */
297    cursor = 0;
298    cmdedit_y = 0;                  /* new quasireal y */
299}
300
301#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
302static void parse_prompt(const char *prmt_ptr)
303{
304    cmdedit_prompt = prmt_ptr;
305    cmdedit_prmt_len = strlen(prmt_ptr);
306    put_prompt();
307}
308#else
309static void parse_prompt(const char *prmt_ptr)
310{
311    int prmt_len = 0;
312    size_t cur_prmt_len = 0;
313    char  flg_not_length = '[';
314    char *prmt_mem_ptr = xcalloc(1, 1);
315    char *pwd_buf = xgetcwd(0);
316    char  buf2[PATH_MAX + 1];
317    char  buf[2];
318    char  c;
319    char *pbuf;
320
321    if (!pwd_buf) {
322        pwd_buf=(char *)bb_msg_unknown;
323    }
324
325    while (*prmt_ptr) {
326        pbuf    = buf;
327        pbuf[1] = 0;
328        c = *prmt_ptr++;
329        if (c == '\\') {
330            const char *cp = prmt_ptr;
331            int l;
332
333            c = bb_process_escape_sequence(&prmt_ptr);
334            if(prmt_ptr==cp) {
335              if (*cp == 0)
336                break;
337              c = *prmt_ptr++;
338              switch (c) {
339#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
340              case 'u':
341                pbuf = user_buf;
342                break;
343#endif
344              case 'h':
345                pbuf = hostname_buf;
346                if (pbuf == 0) {
347                    pbuf = xcalloc(256, 1);
348                    if (gethostname(pbuf, 255) < 0) {
349                        strcpy(pbuf, "?");
350                    } else {
351                        char *s = strchr(pbuf, '.');
352
353                        if (s)
354                            *s = 0;
355                    }
356                    hostname_buf = pbuf;
357                }
358                break;
359              case '$':
360                c = my_euid == 0 ? '#' : '$';
361                break;
362#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
363              case 'w':
364                pbuf = pwd_buf;
365                l = strlen(home_pwd_buf);
366                if (home_pwd_buf[0] != 0 &&
367                    strncmp(home_pwd_buf, pbuf, l) == 0 &&
368                    (pbuf[l]=='/' || pbuf[l]=='\0') &&
369                    strlen(pwd_buf+l)<PATH_MAX) {
370                    pbuf = buf2;
371                    *pbuf = '~';
372                    strcpy(pbuf+1, pwd_buf+l);
373                    }
374                break;
375#endif
376              case 'W':
377                pbuf = pwd_buf;
378                cp = strrchr(pbuf,'/');
379                if ( (cp != NULL) && (cp != pbuf) )
380                    pbuf += (cp-pbuf)+1;
381                break;
382              case '!':
383                snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines);
384                break;
385              case 'e': case 'E':     /* \e \E = \033 */
386                c = '\033';
387                break;
388              case 'x': case 'X':
389                for (l = 0; l < 3;) {
390                    int h;
391                    buf2[l++] = *prmt_ptr;
392                    buf2[l] = 0;
393                    h = strtol(buf2, &pbuf, 16);
394                    if (h > UCHAR_MAX || (pbuf - buf2) < l) {
395                        l--;
396                        break;
397                    }
398                    prmt_ptr++;
399                }
400                buf2[l] = 0;
401                c = (char)strtol(buf2, 0, 16);
402                if(c==0)
403                    c = '?';
404                pbuf = buf;
405                break;
406              case '[': case ']':
407                if (c == flg_not_length) {
408                    flg_not_length = flg_not_length == '[' ? ']' : '[';
409                    continue;
410                }
411                break;
412              }
413            }
414        }
415        if(pbuf == buf)
416            *pbuf = c;
417        cur_prmt_len = strlen(pbuf);
418        prmt_len += cur_prmt_len;
419        if (flg_not_length != ']')
420            cmdedit_prmt_len += cur_prmt_len;
421        prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
422    }
423    if(pwd_buf!=(char *)bb_msg_unknown)
424        free(pwd_buf);
425    cmdedit_prompt = prmt_mem_ptr;
426    put_prompt();
427}
428#endif
429
430
431/* draw prompt, editor line, and clear tail */
432static void redraw(int y, int back_cursor)
433{
434    if (y > 0)                              /* up to start y */
435        printf("\033[%dA", y);
436    putchar('\r');
437    put_prompt();
438    input_end();                            /* rewrite */
439    printf("\033[J");                       /* destroy tail after cursor */
440    input_backward(back_cursor);
441}
442
443#ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
444#define DELBUFSIZ 128
445static char *delbuf;  /* a (malloced) place to store deleted characters */
446static char *delp;
447static char newdelflag;      /* whether delbuf should be reused yet */
448#endif
449
450/* Delete the char in front of the cursor, optionally saving it
451 * for later putback */
452static void input_delete(int save)
453{
454    int j = cursor;
455
456    if (j == len)
457        return;
458
459#ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
460    if (save) {
461        if (newdelflag) {
462            if (!delbuf)
463                delbuf = malloc(DELBUFSIZ);
464            /* safe if malloc fails */
465            delp = delbuf;
466            newdelflag = 0;
467        }
468        if (delbuf && (delp - delbuf < DELBUFSIZ))
469            *delp++ = command_ps[j];
470    }
471#endif
472
473    strcpy(command_ps + j, command_ps + j + 1);
474    len--;
475    input_end();                    /* rewrite new line */
476    cmdedit_set_out_char(0);        /* destroy end char */
477    input_backward(cursor - j);     /* back to old pos cursor */
478}
479
480#ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
481static void put(void)
482{
483    int ocursor, j = delp - delbuf;
484    if (j == 0)
485        return;
486    ocursor = cursor;
487    /* open hole and then fill it */
488    memmove(command_ps + cursor + j, command_ps + cursor, len - cursor + 1);
489    strncpy(command_ps + cursor, delbuf, j);
490    len += j;
491    input_end();                    /* rewrite new line */
492    input_backward(cursor-ocursor-j+1); /* at end of new text */
493}
494#endif
495
496/* Delete the char in back of the cursor */
497static void input_backspace(void)
498{
499    if (cursor > 0) {
500        input_backward(1);
501        input_delete(0);
502    }
503}
504
505
506/* Move forward one character */
507static void input_forward(void)
508{
509    if (cursor < len)
510        cmdedit_set_out_char(command_ps[cursor + 1]);
511}
512
513static void cmdedit_setwidth(int w, int redraw_flg)
514{
515    cmdedit_termw = cmdedit_prmt_len + 2;
516    if (w <= cmdedit_termw) {
517        cmdedit_termw = cmdedit_termw % w;
518    }
519    if (w > cmdedit_termw) {
520        cmdedit_termw = w;
521
522        if (redraw_flg) {
523            /* new y for current cursor */
524            int new_y = (cursor + cmdedit_prmt_len) / w;
525
526            /* redraw */
527            redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
528            fflush(stdout);
529        }
530    }
531}
532
533static void cmdedit_init(void)
534{
535    cmdedit_prmt_len = 0;
536    if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
537        /* emulate usage handler to set handler and call yours work */
538        win_changed(-SIGWINCH);
539        handlers_sets |= SET_WCHG_HANDLERS;
540    }
541
542    if ((handlers_sets & SET_ATEXIT) == 0) {
543#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
544        struct passwd *entry;
545
546        my_euid = geteuid();
547        entry = getpwuid(my_euid);
548        if (entry) {
549            user_buf = bb_xstrdup(entry->pw_name);
550            home_pwd_buf = bb_xstrdup(entry->pw_dir);
551        }
552#endif
553
554#ifdef  CONFIG_FEATURE_COMMAND_TAB_COMPLETION
555
556#ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
557        my_euid = geteuid();
558#endif
559        my_uid = getuid();
560        my_gid = getgid();
561#endif  /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
562        handlers_sets |= SET_ATEXIT;
563        atexit(cmdedit_reset_term);     /* be sure to do this only once */
564    }
565}
566
567#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
568
569static char **matches;
570static int num_matches;
571static char *add_char_to_match;
572
573static void add_match(char *matched, int add_char)
574{
575    int nm = num_matches;
576    int nm1 = nm + 1;
577
578    matches = xrealloc(matches, nm1 * sizeof(char *));
579    add_char_to_match = xrealloc(add_char_to_match, nm1);
580    matches[nm] = matched;
581    add_char_to_match[nm] = (char)add_char;
582    num_matches++;
583}
584
585static int is_execute(const struct stat *st)
586{
587    if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
588        (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
589        (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
590        (st->st_mode & S_IXOTH)) return TRUE;
591    return FALSE;
592}
593
594#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
595
596static void username_tab_completion(char *ud, char *with_shash_flg)
597{
598    struct passwd *entry;
599    int userlen;
600
601    ud++;                           /* ~user/... to user/... */
602    userlen = strlen(ud);
603
604    if (with_shash_flg) {           /* "~/..." or "~user/..." */
605        char *sav_ud = ud - 1;
606        char *home = 0;
607        char *temp;
608
609        if (*ud == '/') {       /* "~/..."     */
610            home = home_pwd_buf;
611        } else {
612            /* "~user/..." */
613            temp = strchr(ud, '/');
614            *temp = 0;              /* ~user\0 */
615            entry = getpwnam(ud);
616            *temp = '/';            /* restore ~user/... */
617            ud = temp;
618            if (entry)
619                home = entry->pw_dir;
620        }
621        if (home) {
622            if ((userlen + strlen(home) + 1) < BUFSIZ) {
623                char temp2[BUFSIZ];     /* argument size */
624
625                /* /home/user/... */
626                sprintf(temp2, "%s%s", home, ud);
627                strcpy(sav_ud, temp2);
628            }
629        }
630    } else {
631        /* "~[^/]*" */
632        setpwent();
633
634        while ((entry = getpwent()) != NULL) {
635            /* Null usernames should result in all users as possible completions. */
636            if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
637                add_match(bb_xasprintf("~%s", entry->pw_name), '/');
638            }
639        }
640
641        endpwent();
642    }
643}
644#endif  /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */
645
646enum {
647    FIND_EXE_ONLY = 0,
648    FIND_DIR_ONLY = 1,
649    FIND_FILE_ONLY = 2,
650};
651
652#ifdef CONFIG_ASH
653const char *cmdedit_path_lookup;
654#else
655#define cmdedit_path_lookup getenv("PATH")
656#endif
657
658static int path_parse(char ***p, int flags)
659{
660    int npth;
661    const char *tmp;
662    const char *pth;
663
664    /* if not setenv PATH variable, to search cur dir "." */
665    if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 ||
666        /* PATH=<empty> or PATH=:<empty> */
667        *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
668        return 1;
669    }
670
671    tmp = pth;
672    npth = 0;
673
674    for (;;) {
675        npth++;                 /* count words is + 1 count ':' */
676        tmp = strchr(tmp, ':');
677        if (tmp) {
678            if (*++tmp == 0)
679                break;  /* :<empty> */
680        } else
681            break;
682    }
683
684    *p = xmalloc(npth * sizeof(char *));
685
686    tmp = pth;
687    (*p)[0] = bb_xstrdup(tmp);
688    npth = 1;                       /* count words is + 1 count ':' */
689
690    for (;;) {
691        tmp = strchr(tmp, ':');
692        if (tmp) {
693            (*p)[0][(tmp - pth)] = 0;       /* ':' -> '\0' */
694            if (*++tmp == 0)
695                break;                  /* :<empty> */
696        } else
697            break;
698        (*p)[npth++] = &(*p)[0][(tmp - pth)];   /* p[next]=p[0][&'\0'+1] */
699    }
700
701    return npth;
702}
703
704static char *add_quote_for_spec_chars(char *found, int add)
705{
706    int l = 0;
707    char *s = xmalloc((strlen(found) + 1) * 2);
708
709    while (*found) {
710        if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
711            s[l++] = '\\';
712        s[l++] = *found++;
713    }
714    if(add)
715        s[l++] = (char)add;
716    s[l] = 0;
717    return s;
718}
719
720static void exe_n_cwd_tab_completion(char *command, int type)
721{
722    DIR *dir;
723    struct dirent *next;
724    char dirbuf[BUFSIZ];
725    struct stat st;
726    char *path1[1];
727    char **paths = path1;
728    int npaths;
729    int i;
730    char *found;
731    char *pfind = strrchr(command, '/');
732
733    path1[0] = ".";
734
735    if (pfind == NULL) {
736        /* no dir, if flags==EXE_ONLY - get paths, else "." */
737        npaths = path_parse(&paths, type);
738        pfind = command;
739    } else {
740        /* with dir */
741        /* save for change */
742        strcpy(dirbuf, command);
743        /* set dir only */
744        dirbuf[(pfind - command) + 1] = 0;
745#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
746        if (dirbuf[0] == '~')   /* ~/... or ~user/... */
747            username_tab_completion(dirbuf, dirbuf);
748#endif
749        /* "strip" dirname in command */
750        pfind++;
751
752        paths[0] = dirbuf;
753        npaths = 1;                             /* only 1 dir */
754    }
755
756    for (i = 0; i < npaths; i++) {
757
758        dir = opendir(paths[i]);
759        if (!dir)                       /* Don't print an error */
760            continue;
761
762        while ((next = readdir(dir)) != NULL) {
763            char *str_found = next->d_name;
764            int add_chr = 0;
765
766            /* matched ? */
767            if (strncmp(str_found, pfind, strlen(pfind)))
768                continue;
769            /* not see .name without .match */
770            if (*str_found == '.' && *pfind == 0) {
771                if (*paths[i] == '/' && paths[i][1] == 0
772                    && str_found[1] == 0) str_found = "";   /* only "/" */
773                else
774                    continue;
775            }
776            found = concat_path_file(paths[i], str_found);
777            /* hmm, remover in progress? */
778            if (stat(found, &st) < 0)
779                goto cont;
780            /* find with dirs ? */
781            if (paths[i] != dirbuf)
782                strcpy(found, next->d_name);    /* only name */
783            if (S_ISDIR(st.st_mode)) {
784                /* name is directory      */
785                char *e = found + strlen(found) - 1;
786
787                add_chr = '/';
788                if(*e == '/')
789                    *e = '\0';
790            } else {
791                /* not put found file if search only dirs for cd */
792                if (type == FIND_DIR_ONLY)
793                    goto cont;
794                if (type == FIND_FILE_ONLY ||
795                    (type == FIND_EXE_ONLY && is_execute(&st)))
796                    add_chr = ' ';
797            }
798            /* Add it to the list */
799            add_match(found, add_chr);
800            continue;
801cont:
802            free(found);
803        }
804        closedir(dir);
805    }
806    if (paths != path1) {
807        free(paths[0]);                 /* allocated memory only in first member */
808        free(paths);
809    }
810}
811
812
813#define QUOT    (UCHAR_MAX+1)
814
815#define collapse_pos(is, in) { \
816    memmove(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
817    memmove(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
818
819static int find_match(char *matchBuf, int *len_with_quotes)
820{
821    int i, j;
822    int command_mode;
823    int c, c2;
824    int int_buf[BUFSIZ + 1];
825    int pos_buf[BUFSIZ + 1];
826
827    /* set to integer dimension characters and own positions */
828    for (i = 0;; i++) {
829        int_buf[i] = (int) ((unsigned char) matchBuf[i]);
830        if (int_buf[i] == 0) {
831            pos_buf[i] = -1;        /* indicator end line */
832            break;
833        } else
834            pos_buf[i] = i;
835    }
836
837    /* mask \+symbol and convert '\t' to ' ' */
838    for (i = j = 0; matchBuf[i]; i++, j++)
839        if (matchBuf[i] == '\\') {
840            collapse_pos(j, j + 1);
841            int_buf[j] |= QUOT;
842            i++;
843#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
844            if (matchBuf[i] == '\t')        /* algorithm equivalent */
845                int_buf[j] = ' ' | QUOT;
846#endif
847        }
848#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
849        else if (matchBuf[i] == '\t')
850            int_buf[j] = ' ';
851#endif
852
853    /* mask "symbols" or 'symbols' */
854    c2 = 0;
855    for (i = 0; int_buf[i]; i++) {
856        c = int_buf[i];
857        if (c == '\'' || c == '"') {
858            if (c2 == 0)
859                c2 = c;
860            else {
861                if (c == c2)
862                    c2 = 0;
863                else
864                    int_buf[i] |= QUOT;
865            }
866        } else if (c2 != 0 && c != '$')
867            int_buf[i] |= QUOT;
868    }
869
870    /* skip commands with arguments if line have commands delimiters */
871    /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
872    for (i = 0; int_buf[i]; i++) {
873        c = int_buf[i];
874        c2 = int_buf[i + 1];
875        j = i ? int_buf[i - 1] : -1;
876        command_mode = 0;
877        if (c == ';' || c == '&' || c == '|') {
878            command_mode = 1 + (c == c2);
879            if (c == '&') {
880                if (j == '>' || j == '<')
881                    command_mode = 0;
882            } else if (c == '|' && j == '>')
883                command_mode = 0;
884        }
885        if (command_mode) {
886            collapse_pos(0, i + command_mode);
887            i = -1;                         /* hack incremet */
888        }
889    }
890    /* collapse `command...` */
891    for (i = 0; int_buf[i]; i++)
892        if (int_buf[i] == '`') {
893            for (j = i + 1; int_buf[j]; j++)
894                if (int_buf[j] == '`') {
895                    collapse_pos(i, j + 1);
896                    j = 0;
897                    break;
898                }
899            if (j) {
900                /* not found close ` - command mode, collapse all previous */
901                collapse_pos(0, i + 1);
902                break;
903            } else
904                i--;                    /* hack incremet */
905        }
906
907    /* collapse (command...(command...)...) or {command...{command...}...} */
908    c = 0;                                          /* "recursive" level */
909    c2 = 0;
910    for (i = 0; int_buf[i]; i++)
911        if (int_buf[i] == '(' || int_buf[i] == '{') {
912            if (int_buf[i] == '(')
913                c++;
914            else
915                c2++;
916            collapse_pos(0, i + 1);
917            i = -1;                         /* hack incremet */
918        }
919    for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
920        if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
921            if (int_buf[i] == ')')
922                c--;
923            else
924                c2--;
925            collapse_pos(0, i + 1);
926            i = -1;                         /* hack incremet */
927        }
928
929    /* skip first not quote space */
930    for (i = 0; int_buf[i]; i++)
931        if (int_buf[i] != ' ')
932            break;
933    if (i)
934        collapse_pos(0, i);
935
936    /* set find mode for completion */
937    command_mode = FIND_EXE_ONLY;
938    for (i = 0; int_buf[i]; i++)
939        if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
940            if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
941                && matchBuf[pos_buf[0]]=='c'
942                && matchBuf[pos_buf[1]]=='d' )
943                command_mode = FIND_DIR_ONLY;
944            else {
945                command_mode = FIND_FILE_ONLY;
946                break;
947            }
948        }
949    /* "strlen" */
950    for (i = 0; int_buf[i]; i++);
951    /* find last word */
952    for (--i; i >= 0; i--) {
953        c = int_buf[i];
954        if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
955            collapse_pos(0, i + 1);
956            break;
957        }
958    }
959    /* skip first not quoted '\'' or '"' */
960    for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
961    /* collapse quote or unquote // or /~ */
962    while ((int_buf[i] & ~QUOT) == '/' &&
963            ((int_buf[i + 1] & ~QUOT) == '/'
964             || (int_buf[i + 1] & ~QUOT) == '~')) {
965        i++;
966    }
967
968    /* set only match and destroy quotes */
969    j = 0;
970    for (c = 0; pos_buf[i] >= 0; i++) {
971        matchBuf[c++] = matchBuf[pos_buf[i]];
972        j = pos_buf[i] + 1;
973    }
974    matchBuf[c] = 0;
975    /* old lenght matchBuf with quotes symbols */
976    *len_with_quotes = j ? j - pos_buf[0] : 0;
977
978    return command_mode;
979}
980
981/*
982   display by column original ideas from ls applet,
983   very optimize by my :)
984*/
985static void showfiles(void)
986{
987    int ncols, row;
988    int column_width = 0;
989    int nfiles = num_matches;
990    int nrows = nfiles;
991    char str_add_chr[2];
992    int l;
993
994    /* find the longest file name-  use that as the column width */
995    for (row = 0; row < nrows; row++) {
996        l = strlen(matches[row]);
997        if(add_char_to_match[row])
998            l++;
999        if (column_width < l)
1000            column_width = l;
1001    }
1002    column_width += 2;              /* min space for columns */
1003    ncols = cmdedit_termw / column_width;
1004
1005    if (ncols > 1) {
1006        nrows /= ncols;
1007        if(nfiles % ncols)
1008            nrows++;        /* round up fractionals */
1009    } else {
1010        ncols = 1;
1011    }
1012    str_add_chr[1] = 0;
1013    for (row = 0; row < nrows; row++) {
1014        int n = row;
1015        int nc;
1016        int acol;
1017
1018        for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
1019            str_add_chr[0] = add_char_to_match[n];
1020            acol = str_add_chr[0] ? column_width - 1 : column_width;
1021            printf("%s%s", matches[n], str_add_chr);
1022            l = strlen(matches[n]);
1023            while(l < acol) {
1024                putchar(' ');
1025                l++;
1026            }
1027        }
1028        str_add_chr[0] = add_char_to_match[n];
1029        printf("%s%s\n", matches[n], str_add_chr);
1030    }
1031}
1032
1033
1034static void input_tab(int *lastWasTab)
1035{
1036    /* Do TAB completion */
1037    if (lastWasTab == 0) {          /* free all memory */
1038        if (matches) {
1039            while (num_matches > 0)
1040                free(matches[--num_matches]);
1041            free(matches);
1042            matches = (char **) NULL;
1043            free(add_char_to_match);
1044            add_char_to_match = NULL;
1045        }
1046        return;
1047    }
1048    if (! *lastWasTab) {
1049
1050        char *tmp, *tmp1;
1051        int len_found;
1052        char matchBuf[BUFSIZ];
1053        int find_type;
1054        int recalc_pos;
1055
1056        *lastWasTab = TRUE;             /* flop trigger */
1057
1058        /* Make a local copy of the string -- up
1059         * to the position of the cursor */
1060        tmp = strncpy(matchBuf, command_ps, cursor);
1061        tmp[cursor] = 0;
1062
1063        find_type = find_match(matchBuf, &recalc_pos);
1064
1065        /* Free up any memory already allocated */
1066        input_tab(0);
1067
1068#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
1069        /* If the word starts with `~' and there is no slash in the word,
1070         * then try completing this word as a username. */
1071
1072        if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
1073            username_tab_completion(matchBuf, NULL);
1074        if (!matches)
1075#endif
1076        /* Try to match any executable in our path and everything
1077         * in the current working directory that matches.  */
1078            exe_n_cwd_tab_completion(matchBuf, find_type);
1079        /* Remove duplicate found and sort */
1080        if(matches) {
1081            int i, j, n, srt;
1082            /* bubble */
1083            n = num_matches;
1084            for(i=0; i<(n-1); i++)
1085                for(j=i+1; j<n; j++)
1086                if(matches[i]!=NULL && matches[j]!=NULL) {
1087                    srt = strcmp(matches[i], matches[j]);
1088                    if(srt == 0) {
1089                    free(matches[j]);
1090                    matches[j]=0;
1091                    } else if(srt > 0) {
1092                    tmp1 = matches[i];
1093                    matches[i] = matches[j];
1094                    matches[j] = tmp1;
1095                    srt = add_char_to_match[i];
1096                    add_char_to_match[i] = add_char_to_match[j];
1097                    add_char_to_match[j] = srt;
1098                    }
1099                }
1100            j = n;
1101            n = 0;
1102            for(i=0; i<j; i++)
1103                if(matches[i]) {
1104                matches[n]=matches[i];
1105                add_char_to_match[n]=add_char_to_match[i];
1106                n++;
1107                }
1108            num_matches = n;
1109        }
1110        /* Did we find exactly one match? */
1111        if (!matches || num_matches > 1) {
1112
1113            beep();
1114            if (!matches)
1115                return;         /* not found */
1116            /* find minimal match */
1117            tmp1 = bb_xstrdup(matches[0]);
1118            for (tmp = tmp1; *tmp; tmp++)
1119                for (len_found = 1; len_found < num_matches; len_found++)
1120                    if (matches[len_found][(tmp - tmp1)] != *tmp) {
1121                        *tmp = 0;
1122                        break;
1123                    }
1124            if (*tmp1 == 0) {        /* have unique */
1125                free(tmp1);
1126                return;
1127            }
1128            tmp = add_quote_for_spec_chars(tmp1, 0);
1129            free(tmp1);
1130        } else {                        /* one match */
1131            tmp = add_quote_for_spec_chars(matches[0], add_char_to_match[0]);
1132            /* for next completion current found */
1133            *lastWasTab = FALSE;
1134        }
1135        len_found = strlen(tmp);
1136        /* have space to placed match? */
1137        if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
1138
1139            /* before word for match   */
1140            command_ps[cursor - recalc_pos] = 0;
1141            /* save   tail line        */
1142            strcpy(matchBuf, command_ps + cursor);
1143            /* add    match            */
1144            strcat(command_ps, tmp);
1145            /* add    tail             */
1146            strcat(command_ps, matchBuf);
1147            /* back to begin word for match    */
1148            input_backward(recalc_pos);
1149            /* new pos                         */
1150            recalc_pos = cursor + len_found;
1151            /* new len                         */
1152            len = strlen(command_ps);
1153            /* write out the matched command   */
1154            redraw(cmdedit_y, len - recalc_pos);
1155        }
1156        free(tmp);
1157    } else {
1158        /* Ok -- the last char was a TAB.  Since they
1159         * just hit TAB again, print a list of all the
1160         * available choices... */
1161        if (matches && num_matches > 0) {
1162            int sav_cursor = cursor;        /* change goto_new_line() */
1163
1164            /* Go to the next line */
1165            goto_new_line();
1166            showfiles();
1167            redraw(0, len - sav_cursor);
1168        }
1169    }
1170}
1171#endif  /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
1172
1173#if MAX_HISTORY >= 1
1174static void get_previous_history(void)
1175{
1176    if(command_ps[0] != 0 || history[cur_history] == 0) {
1177        free(history[cur_history]);
1178        history[cur_history] = bb_xstrdup(command_ps);
1179    }
1180    cur_history--;
1181}
1182
1183static int get_next_history(void)
1184{
1185    int ch = cur_history;
1186
1187    if (ch < n_history) {
1188        get_previous_history(); /* save the current history line */
1189        return (cur_history = ch+1);
1190    } else {
1191        beep();
1192        return 0;
1193    }
1194}
1195
1196#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
1197void load_history ( const char *fromfile )
1198{
1199    FILE *fp;
1200    int hi;
1201
1202    /* cleanup old */
1203
1204    for(hi = n_history; hi > 0; ) {
1205        hi--;
1206        free ( history [hi] );
1207    }
1208
1209    if (( fp = fopen ( fromfile, "r" ))) {
1210
1211        for ( hi = 0; hi < MAX_HISTORY; ) {
1212            char * hl = bb_get_chomped_line_from_file(fp);
1213            int l;
1214
1215            if(!hl)
1216                break;
1217            l = strlen(hl);
1218            if(l >= BUFSIZ)
1219                hl[BUFSIZ-1] = 0;
1220            if(l == 0 || hl[0] == ' ') {
1221                free(hl);
1222                continue;
1223            }
1224            history [hi++] = hl;
1225        }
1226        fclose ( fp );
1227    }
1228    cur_history = n_history = hi;
1229}
1230
1231void save_history ( const char *tofile )
1232{
1233    FILE *fp = fopen ( tofile, "w" );
1234
1235    if ( fp ) {
1236        int i;
1237
1238        for ( i = 0; i < n_history; i++ ) {
1239            fprintf(fp, "%s\n", history [i]);
1240        }
1241        fclose ( fp );
1242    }
1243}
1244#endif
1245
1246#endif
1247
1248enum {
1249    ESC = 27,
1250    DEL = 127,
1251};
1252
1253
1254/*
1255 * This function is used to grab a character buffer
1256 * from the input file descriptor and allows you to
1257 * a string with full command editing (sort of like
1258 * a mini readline).
1259 *
1260 * The following standard commands are not implemented:
1261 * ESC-b -- Move back one word
1262 * ESC-f -- Move forward one word
1263 * ESC-d -- Delete back one word
1264 * ESC-h -- Delete forward one word
1265 * CTL-t -- Transpose two characters
1266 *
1267 * Minimalist vi-style command line editing available if configured.
1268 *  vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
1269 *
1270 */
1271
1272#ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1273static int vi_mode;
1274
1275void setvimode ( int viflag )
1276{
1277    vi_mode = viflag;
1278}
1279
1280static void
1281vi_Word_motion(char *command, int eat)
1282{
1283    while (cursor < len && !isspace(command[cursor]))
1284        input_forward();
1285    if (eat) while (cursor < len && isspace(command[cursor]))
1286        input_forward();
1287}
1288
1289static void
1290vi_word_motion(char *command, int eat)
1291{
1292    if (isalnum(command[cursor]) || command[cursor] == '_') {
1293        while (cursor < len &&
1294            (isalnum(command[cursor+1]) ||
1295                command[cursor+1] == '_'))
1296            input_forward();
1297    } else if (ispunct(command[cursor])) {
1298        while (cursor < len &&
1299            (ispunct(command[cursor+1])))
1300            input_forward();
1301    }
1302
1303    if (cursor < len)
1304        input_forward();
1305
1306    if (eat && cursor < len && isspace(command[cursor]))
1307        while (cursor < len && isspace(command[cursor]))
1308            input_forward();
1309}
1310
1311static void
1312vi_End_motion(char *command)
1313{
1314    input_forward();
1315    while (cursor < len && isspace(command[cursor]))
1316        input_forward();
1317    while (cursor < len-1 && !isspace(command[cursor+1]))
1318        input_forward();
1319}
1320
1321static void
1322vi_end_motion(char *command)
1323{
1324    if (cursor >= len-1)
1325        return;
1326    input_forward();
1327    while (cursor < len-1 && isspace(command[cursor]))
1328        input_forward();
1329    if (cursor >= len-1)
1330        return;
1331    if (isalnum(command[cursor]) || command[cursor] == '_') {
1332        while (cursor < len-1 &&
1333            (isalnum(command[cursor+1]) ||
1334                command[cursor+1] == '_'))
1335            input_forward();
1336    } else if (ispunct(command[cursor])) {
1337        while (cursor < len-1 &&
1338            (ispunct(command[cursor+1])))
1339            input_forward();
1340    }
1341}
1342
1343static void
1344vi_Back_motion(char *command)
1345{
1346    while (cursor > 0 && isspace(command[cursor-1]))
1347        input_backward(1);
1348    while (cursor > 0 && !isspace(command[cursor-1]))
1349        input_backward(1);
1350}
1351
1352static void
1353vi_back_motion(char *command)
1354{
1355    if (cursor <= 0)
1356        return;
1357    input_backward(1);
1358    while (cursor > 0 && isspace(command[cursor]))
1359        input_backward(1);
1360    if (cursor <= 0)
1361        return;
1362    if (isalnum(command[cursor]) || command[cursor] == '_') {
1363        while (cursor > 0 &&
1364            (isalnum(command[cursor-1]) ||
1365                command[cursor-1] == '_'))
1366            input_backward(1);
1367    } else if (ispunct(command[cursor])) {
1368        while (cursor > 0 &&
1369            (ispunct(command[cursor-1])))
1370            input_backward(1);
1371    }
1372}
1373#endif
1374
1375/*
1376 * the emacs and vi modes share much of the code in the big
1377 * command loop.  commands entered when in vi's command mode (aka
1378 * "escape mode") get an extra bit added to distinguish them --
1379 * this keeps them from being self-inserted.  this clutters the
1380 * big switch a bit, but keeps all the code in one place.
1381 */
1382
1383#define vbit 0x100
1384
1385/* leave out the "vi-mode"-only case labels if vi editing isn't
1386 * configured. */
1387#define vi_case(caselabel) USE_FEATURE_COMMAND_EDITING(caselabel)
1388
1389/* convert uppercase ascii to equivalent control char, for readability */
1390#define CNTRL(uc_char) ((uc_char) - 0x40)
1391
1392
1393int cmdedit_read_input(char *prompt, char command[BUFSIZ])
1394{
1395
1396    int break_out = 0;
1397    int lastWasTab = FALSE;
1398    unsigned char c;
1399    unsigned int ic;
1400#ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1401    unsigned int prevc;
1402    int vi_cmdmode = 0;
1403#endif
1404    /* prepare before init handlers */
1405    cmdedit_y = 0;  /* quasireal y, not true work if line > xt*yt */
1406    len = 0;
1407    command_ps = command;
1408
1409    getTermSettings(0, (void *) &initial_settings);
1410    memcpy(&new_settings, &initial_settings, sizeof(struct termios));
1411    new_settings.c_lflag &= ~ICANON;        /* unbuffered input */
1412    /* Turn off echoing and CTRL-C, so we can trap it */
1413    new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
1414    /* Hmm, in linux c_cc[] not parsed if set ~ICANON */
1415    new_settings.c_cc[VMIN] = 1;
1416    new_settings.c_cc[VTIME] = 0;
1417    /* Turn off CTRL-C, so we can trap it */
1418#       ifndef _POSIX_VDISABLE
1419#               define _POSIX_VDISABLE '\0'
1420#       endif
1421    new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
1422    command[0] = 0;
1423
1424    setTermSettings(0, (void *) &new_settings);
1425    handlers_sets |= SET_RESET_TERM;
1426
1427    /* Now initialize things */
1428    cmdedit_init();
1429    /* Print out the command prompt */
1430    parse_prompt(prompt);
1431
1432    while (1) {
1433
1434        fflush(stdout);                 /* buffered out to fast */
1435
1436        if (safe_read(0, &c, 1) < 1)
1437            /* if we can't read input then exit */
1438            goto prepare_to_die;
1439
1440        ic = c;
1441
1442#ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1443        newdelflag = 1;
1444        if (vi_cmdmode)
1445            ic |= vbit;
1446#endif
1447        switch (ic)
1448        {
1449        case '\n':
1450        case '\r':
1451        vi_case( case '\n'|vbit: )
1452        vi_case( case '\r'|vbit: )
1453            /* Enter */
1454            goto_new_line();
1455            break_out = 1;
1456            break;
1457        case CNTRL('A'):
1458        vi_case( case '0'|vbit: )
1459            /* Control-a -- Beginning of line */
1460            input_backward(cursor);
1461            break;
1462        case CNTRL('B'):
1463        vi_case( case 'h'|vbit: )
1464        vi_case( case '\b'|vbit: )
1465        vi_case( case DEL|vbit: )
1466            /* Control-b -- Move back one character */
1467            input_backward(1);
1468            break;
1469        case CNTRL('C'):
1470        vi_case( case CNTRL('C')|vbit: )
1471            /* Control-c -- stop gathering input */
1472            goto_new_line();
1473#ifndef CONFIG_ASH
1474            command[0] = 0;
1475            len = 0;
1476            lastWasTab = FALSE;
1477            put_prompt();
1478#else
1479            len = 0;
1480            break_out = -1; /* to control traps */
1481#endif
1482            break;
1483        case CNTRL('D'):
1484            /* Control-d -- Delete one character, or exit
1485             * if the len=0 and no chars to delete */
1486            if (len == 0) {
1487                    errno = 0;
1488prepare_to_die:
1489#if !defined(CONFIG_ASH)
1490                printf("exit");
1491                goto_new_line();
1492                /* cmdedit_reset_term() called in atexit */
1493                exit(EXIT_SUCCESS);
1494#else
1495                /* to control stopped jobs */
1496                len = break_out = -1;
1497                break;
1498#endif
1499            } else {
1500                input_delete(0);
1501            }
1502            break;
1503        case CNTRL('E'):
1504        vi_case( case '$'|vbit: )
1505            /* Control-e -- End of line */
1506            input_end();
1507            break;
1508        case CNTRL('F'):
1509        vi_case( case 'l'|vbit: )
1510        vi_case( case ' '|vbit: )
1511            /* Control-f -- Move forward one character */
1512            input_forward();
1513            break;
1514        case '\b':
1515        case DEL:
1516            /* Control-h and DEL */
1517            input_backspace();
1518            break;
1519        case '\t':
1520#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1521            input_tab(&lastWasTab);
1522#endif
1523            break;
1524        case CNTRL('K'):
1525            /* Control-k -- clear to end of line */
1526            *(command + cursor) = 0;
1527            len = cursor;
1528            printf("\033[J");
1529            break;
1530        case CNTRL('L'):
1531        vi_case( case CNTRL('L')|vbit: )
1532            /* Control-l -- clear screen */
1533            printf("\033[H");
1534            redraw(0, len-cursor);
1535            break;
1536#if MAX_HISTORY >= 1
1537        case CNTRL('N'):
1538        vi_case( case CNTRL('N')|vbit: )
1539        vi_case( case 'j'|vbit: )
1540            /* Control-n -- Get next command in history */
1541            if (get_next_history())
1542                goto rewrite_line;
1543            break;
1544        case CNTRL('P'):
1545        vi_case( case CNTRL('P')|vbit: )
1546        vi_case( case 'k'|vbit: )
1547            /* Control-p -- Get previous command from history */
1548            if (cur_history > 0) {
1549                get_previous_history();
1550                goto rewrite_line;
1551            } else {
1552                beep();
1553            }
1554            break;
1555#endif
1556        case CNTRL('U'):
1557        vi_case( case CNTRL('U')|vbit: )
1558            /* Control-U -- Clear line before cursor */
1559            if (cursor) {
1560                strcpy(command, command + cursor);
1561                redraw(cmdedit_y, len -= cursor);
1562            }
1563            break;
1564        case CNTRL('W'):
1565        vi_case( case CNTRL('W')|vbit: )
1566            /* Control-W -- Remove the last word */
1567            while (cursor > 0 && isspace(command[cursor-1]))
1568                input_backspace();
1569            while (cursor > 0 &&!isspace(command[cursor-1]))
1570                input_backspace();
1571            break;
1572#if CONFIG_FEATURE_COMMAND_EDITING_VI
1573        case 'i'|vbit:
1574            vi_cmdmode = 0;
1575            break;
1576        case 'I'|vbit:
1577            input_backward(cursor);
1578            vi_cmdmode = 0;
1579            break;
1580        case 'a'|vbit:
1581            input_forward();
1582            vi_cmdmode = 0;
1583            break;
1584        case 'A'|vbit:
1585            input_end();
1586            vi_cmdmode = 0;
1587            break;
1588        case 'x'|vbit:
1589            input_delete(1);
1590            break;
1591        case 'X'|vbit:
1592            if (cursor > 0) {
1593                input_backward(1);
1594                input_delete(1);
1595            }
1596            break;
1597        case 'W'|vbit:
1598            vi_Word_motion(command, 1);
1599            break;
1600        case 'w'|vbit:
1601            vi_word_motion(command, 1);
1602            break;
1603        case 'E'|vbit:
1604            vi_End_motion(command);
1605            break;
1606        case 'e'|vbit:
1607            vi_end_motion(command);
1608            break;
1609        case 'B'|vbit:
1610            vi_Back_motion(command);
1611            break;
1612        case 'b'|vbit:
1613            vi_back_motion(command);
1614            break;
1615        case 'C'|vbit:
1616            vi_cmdmode = 0;
1617            /* fall through */
1618        case 'D'|vbit:
1619            goto clear_to_eol;
1620
1621        case 'c'|vbit:
1622            vi_cmdmode = 0;
1623            /* fall through */
1624        case 'd'|vbit:
1625            {
1626            int nc, sc;
1627            sc = cursor;
1628            prevc = ic;
1629            if (safe_read(0, &c, 1) < 1)
1630                goto prepare_to_die;
1631            if (c == (prevc & 0xff)) {
1632                /* "cc", "dd" */
1633                input_backward(cursor);
1634                goto clear_to_eol;
1635                break;
1636            }
1637            switch(c) {
1638            case 'w':
1639            case 'W':
1640            case 'e':
1641            case 'E':
1642                switch (c) {
1643                case 'w':   /* "dw", "cw" */
1644                    vi_word_motion(command, vi_cmdmode);
1645                    break;
1646                case 'W':   /* 'dW', 'cW' */
1647                    vi_Word_motion(command, vi_cmdmode);
1648                    break;
1649                case 'e':   /* 'de', 'ce' */
1650                    vi_end_motion(command);
1651                    input_forward();
1652                    break;
1653                case 'E':   /* 'dE', 'cE' */
1654                    vi_End_motion(command);
1655                    input_forward();
1656                    break;
1657                }
1658                nc = cursor;
1659                input_backward(cursor - sc);
1660                while (nc-- > cursor)
1661                    input_delete(1);
1662                break;
1663            case 'b':  /* "db", "cb" */
1664            case 'B':  /* implemented as B */
1665                if (c == 'b')
1666                    vi_back_motion(command);
1667                else
1668                    vi_Back_motion(command);
1669                while (sc-- > cursor)
1670                    input_delete(1);
1671                break;
1672            case ' ':  /* "d ", "c " */
1673                input_delete(1);
1674                break;
1675            case '$':  /* "d$", "c$" */
1676            clear_to_eol:
1677                while (cursor < len)
1678                    input_delete(1);
1679                break;
1680            }
1681            }
1682            break;
1683        case 'p'|vbit:
1684            input_forward();
1685            /* fallthrough */
1686        case 'P'|vbit:
1687            put();
1688            break;
1689        case 'r'|vbit:
1690            if (safe_read(0, &c, 1) < 1)
1691                goto prepare_to_die;
1692            if (c == 0)
1693                beep();
1694            else {
1695                *(command + cursor) = c;
1696                putchar(c);
1697                putchar('\b');
1698            }
1699            break;
1700#endif /* CONFIG_FEATURE_COMMAND_EDITING_VI */
1701
1702        case ESC:
1703
1704#if CONFIG_FEATURE_COMMAND_EDITING_VI
1705            if (vi_mode) {
1706                /* ESC: insert mode --> command mode */
1707                vi_cmdmode = 1;
1708                input_backward(1);
1709                break;
1710            }
1711#endif
1712            /* escape sequence follows */
1713            if (safe_read(0, &c, 1) < 1)
1714                goto prepare_to_die;
1715            /* different vt100 emulations */
1716            if (c == '[' || c == 'O') {
1717        vi_case( case '['|vbit: )
1718        vi_case( case 'O'|vbit: )
1719                if (safe_read(0, &c, 1) < 1)
1720                    goto prepare_to_die;
1721            }
1722            if (c >= '1' && c <= '9') {
1723                unsigned char dummy;
1724
1725                if (safe_read(0, &dummy, 1) < 1)
1726                    goto prepare_to_die;
1727                if(dummy != '~')
1728                    c = 0;
1729            }
1730            switch (c) {
1731#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1732            case '\t':                      /* Alt-Tab */
1733
1734                input_tab(&lastWasTab);
1735                break;
1736#endif
1737#if MAX_HISTORY >= 1
1738            case 'A':
1739                /* Up Arrow -- Get previous command from history */
1740                if (cur_history > 0) {
1741                    get_previous_history();
1742                    goto rewrite_line;
1743                } else {
1744                    beep();
1745                }
1746                break;
1747            case 'B':
1748                /* Down Arrow -- Get next command in history */
1749                if (!get_next_history())
1750                    break;
1751                /* Rewrite the line with the selected history item */
1752rewrite_line:
1753                /* change command */
1754                len = strlen(strcpy(command, history[cur_history]));
1755                /* redraw and go to eol (bol, in vi */
1756#if CONFIG_FEATURE_COMMAND_EDITING_VI
1757                redraw(cmdedit_y, vi_mode ? 9999:0);
1758#else
1759                redraw(cmdedit_y, 0);
1760#endif
1761                break;
1762#endif
1763            case 'C':
1764                /* Right Arrow -- Move forward one character */
1765                input_forward();
1766                break;
1767            case 'D':
1768                /* Left Arrow -- Move back one character */
1769                input_backward(1);
1770                break;
1771            case '3':
1772                /* Delete */
1773                input_delete(0);
1774                break;
1775            case '1':
1776            case 'H':
1777                /* <Home> */
1778                input_backward(cursor);
1779                break;
1780            case '4':
1781            case 'F':
1782                /* <End> */
1783                input_end();
1784                break;
1785            default:
1786                c = 0;
1787                beep();
1788            }
1789            break;
1790
1791        default:        /* If it's regular input, do the normal thing */
1792#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1793            /* Control-V -- Add non-printable symbol */
1794            if (c == CNTRL('V')) {
1795                if (safe_read(0, &c, 1) < 1)
1796                    goto prepare_to_die;
1797                if (c == 0) {
1798                    beep();
1799                    break;
1800                }
1801            } else
1802#endif
1803            {
1804#if CONFIG_FEATURE_COMMAND_EDITING_VI
1805                if (vi_cmdmode)  /* don't self-insert */
1806                    break;
1807#endif
1808                if (!Isprint(c)) /* Skip non-printable characters */
1809                    break;
1810            }
1811
1812            if (len >= (BUFSIZ - 2))        /* Need to leave space for enter */
1813                break;
1814
1815            len++;
1816
1817            if (cursor == (len - 1)) {      /* Append if at the end of the line */
1818                *(command + cursor) = c;
1819                *(command + cursor + 1) = 0;
1820                cmdedit_set_out_char(0);
1821            } else {                        /* Insert otherwise */
1822                int sc = cursor;
1823
1824                memmove(command + sc + 1, command + sc, len - sc);
1825                *(command + sc) = c;
1826                sc++;
1827                /* rewrite from cursor */
1828                input_end();
1829                /* to prev x pos + 1 */
1830                input_backward(cursor - sc);
1831            }
1832
1833            break;
1834        }
1835        if (break_out)                  /* Enter is the command terminator, no more input. */
1836            break;
1837
1838        if (c != '\t')
1839            lastWasTab = FALSE;
1840    }
1841
1842    setTermSettings(0, (void *) &initial_settings);
1843    handlers_sets &= ~SET_RESET_TERM;
1844
1845#if MAX_HISTORY >= 1
1846    /* Handle command history log */
1847    /* cleanup may be saved current command line */
1848    if (len> 0) {                                      /* no put empty line */
1849        int i = n_history;
1850
1851        free(history[MAX_HISTORY]);
1852        history[MAX_HISTORY] = 0;
1853            /* After max history, remove the oldest command */
1854        if (i >= MAX_HISTORY) {
1855            free(history[0]);
1856            for(i = 0; i < (MAX_HISTORY-1); i++)
1857                history[i] = history[i+1];
1858        }
1859        history[i++] = bb_xstrdup(command);
1860        cur_history = i;
1861        n_history = i;
1862#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1863        num_ok_lines++;
1864#endif
1865    }
1866#else  /* MAX_HISTORY < 1 */
1867#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1868    if (len > 0) {              /* no put empty line */
1869        num_ok_lines++;
1870    }
1871#endif
1872#endif  /* MAX_HISTORY >= 1 */
1873    if (break_out > 0) {
1874        command[len++] = '\n';          /* set '\n' */
1875        command[len] = 0;
1876    }
1877#if defined(CONFIG_FEATURE_CLEAN_UP) && defined(CONFIG_FEATURE_COMMAND_TAB_COMPLETION)
1878    input_tab(0);                           /* strong free */
1879#endif
1880#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1881    free(cmdedit_prompt);
1882#endif
1883    cmdedit_reset_term();
1884    return len;
1885}
1886
1887
1888
1889#endif  /* CONFIG_FEATURE_COMMAND_EDITING */
1890
1891
1892#ifdef TEST
1893
1894const char *bb_applet_name = "debug stuff usage";
1895
1896#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1897#include <locale.h>
1898#endif
1899
1900int main(int argc, char **argv)
1901{
1902    char buff[BUFSIZ];
1903    char *prompt =
1904#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1905        "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:\
1906\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
1907\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
1908#else
1909        "% ";
1910#endif
1911
1912#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1913    setlocale(LC_ALL, "");
1914#endif
1915    while(1) {
1916        int l;
1917        l = cmdedit_read_input(prompt, buff);
1918        if(l > 0 && buff[l-1] == '\n') {
1919            buff[l-1] = 0;
1920            printf("*** cmdedit_read_input() returned line =%s=\n", buff);
1921        } else {
1922            break;
1923        }
1924    }
1925    printf("*** cmdedit_read_input() detect ^D\n");
1926    return 0;
1927}
1928
1929#endif  /* TEST */
Note: See TracBrowser for help on using the repository browser.