diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | cmdedit.c | 1521 | ||||
-rw-r--r-- | shell/cmdedit.c | 1521 |
3 files changed, 2057 insertions, 986 deletions
@@ -78,6 +78,7 @@ BB_SRC_DIR = . | |||
78 | # to something more interesting, like "powerpc-linux-". | 78 | # to something more interesting, like "powerpc-linux-". |
79 | CROSS = | 79 | CROSS = |
80 | CC = $(CROSS)gcc | 80 | CC = $(CROSS)gcc |
81 | AR = $(CROSS)ar | ||
81 | STRIPTOOL = $(CROSS)strip | 82 | STRIPTOOL = $(CROSS)strip |
82 | 83 | ||
83 | # To compile vs uClibc, just use the compiler wrapper built by uClibc... | 84 | # To compile vs uClibc, just use the compiler wrapper built by uClibc... |
@@ -1,21 +1,18 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | 1 | /* vi: set sw=4 ts=4: */ |
2 | /* | 2 | /* |
3 | * Termios command line History and Editting, originally | 3 | * Termios command line History and Editting. |
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 | * | 4 | * |
11 | * You may use this code as you wish, so long as the original author(s) | 5 | * Copyright (c) 1986-2001 may safely be consumed by a BSD or GPL license. |
12 | * are attributed in any redistributions of the source code. | 6 | * Written by: Vladimir Oleynik <vodz@usa.net> |
13 | * This code is 'as is' with no warranty. | 7 | * |
14 | * This code may safely be consumed by a BSD or GPL license. | 8 | * Used ideas: |
9 | * Adam Rogoyski <rogoyski@cs.utexas.edu> | ||
10 | * Dave Cinege <dcinege@psychosis.com> | ||
11 | * Jakub Jelinek (c) 1995 | ||
12 | * Erik Andersen <andersee@debian.org> (Majorly adjusted for busybox) | ||
15 | * | 13 | * |
16 | * v 0.5 19990328 Initial release | 14 | * This code is 'as is' with no warranty. |
17 | * | 15 | * |
18 | * Future plans: Simple file and path name completion. (like BASH) | ||
19 | * | 16 | * |
20 | */ | 17 | */ |
21 | 18 | ||
@@ -27,20 +24,46 @@ | |||
27 | to work in an Xterm and console. Ctrl-A also works as Home. | 24 | to work in an Xterm and console. Ctrl-A also works as Home. |
28 | Ctrl-E also works as End. | 25 | Ctrl-E also works as End. |
29 | 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 | */ | ||
30 | 31 | ||
31 | Editor with vertical scrolling and completion by | ||
32 | Vladimir Oleynik. vodz@usa.net (c) 2001 | ||
33 | 32 | ||
34 | Small bug: not true work if terminal size (x*y symbols) less | 33 | #define TEST |
35 | size (prompt + editor`s line + 2 symbols) | ||
36 | */ | ||
37 | 34 | ||
38 | 35 | ||
36 | #ifndef TEST | ||
39 | 37 | ||
40 | #include "busybox.h" | 38 | #include "busybox.h" |
41 | 39 | ||
40 | #define D(x) | ||
41 | |||
42 | #else | ||
43 | |||
44 | #define BB_FEATURE_SH_COMMAND_EDITING | ||
45 | #define BB_FEATURE_SH_TAB_COMPLETION | ||
46 | #define BB_FEATURE_USERNAME_COMPLETION | ||
47 | #define BB_FEATURE_NONPRINTABLE_INVERSE_PUT | ||
48 | #define BB_FEATURE_BASH_STYLE_PROMT | ||
49 | #define BB_FEATURE_CLEAN_UP | ||
50 | |||
51 | #define TRUE 1 | ||
52 | #define FALSE 0 | ||
53 | #define D(x) x | ||
54 | |||
55 | #endif /* TEST */ | ||
56 | |||
42 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | 57 | #ifdef BB_FEATURE_SH_COMMAND_EDITING |
43 | 58 | ||
59 | #ifndef BB_FEATURE_SH_TAB_COMPLETION | ||
60 | #undef BB_FEATURE_USERNAME_COMPLETION | ||
61 | #endif | ||
62 | |||
63 | #if defined(BB_FEATURE_USERNAME_COMPLETION) || defined(BB_FEATURE_BASH_STYLE_PROMT) | ||
64 | #define BB_FEATURE_GETUSERNAME_AND_HOMEDIR | ||
65 | #endif | ||
66 | |||
44 | #include <stdio.h> | 67 | #include <stdio.h> |
45 | #include <errno.h> | 68 | #include <errno.h> |
46 | #include <unistd.h> | 69 | #include <unistd.h> |
@@ -49,26 +72,61 @@ | |||
49 | #include <sys/ioctl.h> | 72 | #include <sys/ioctl.h> |
50 | #include <ctype.h> | 73 | #include <ctype.h> |
51 | #include <signal.h> | 74 | #include <signal.h> |
75 | #include <limits.h> | ||
52 | 76 | ||
53 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | 77 | #ifdef BB_FEATURE_SH_TAB_COMPLETION |
78 | #include <dirent.h> | ||
54 | #include <sys/stat.h> | 79 | #include <sys/stat.h> |
55 | #endif | 80 | #endif |
56 | 81 | ||
82 | #ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR | ||
83 | #ifndef TEST | ||
57 | #include "pwd_grp/pwd.h" | 84 | #include "pwd_grp/pwd.h" |
85 | #else | ||
86 | #include <pwd.h> | ||
87 | #endif /* TEST */ | ||
88 | #endif /* advanced FEATURES */ | ||
58 | 89 | ||
59 | 90 | ||
60 | static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */ | 91 | #ifdef TEST |
92 | void *xrealloc(void *old, size_t size) | ||
93 | { | ||
94 | return realloc(old, size); | ||
95 | } | ||
61 | 96 | ||
62 | enum { | 97 | void *xmalloc(size_t size) |
63 | ESC = 27, | 98 | { |
64 | DEL = 127, | 99 | return malloc(size); |
100 | } | ||
101 | char *xstrdup(const char *s) | ||
102 | { | ||
103 | return strdup(s); | ||
104 | } | ||
105 | |||
106 | void *xcalloc(size_t size, size_t se) | ||
107 | { | ||
108 | return calloc(size, se); | ||
109 | } | ||
110 | |||
111 | #define error_msg(s, d) fprintf(stderr, s, d) | ||
112 | #endif | ||
113 | |||
114 | |||
115 | struct history { | ||
116 | char *s; | ||
117 | struct history *p; | ||
118 | struct history *n; | ||
65 | }; | 119 | }; |
66 | 120 | ||
67 | #define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0) | 121 | /* Maximum length of the linked list for the command line history */ |
68 | #define whitespace(c) (((c) == ' ') || ((c) == '\t')) | 122 | static const int MAX_HISTORY = 15; |
123 | |||
124 | /* First element in command line list */ | ||
125 | static struct history *his_front = NULL; | ||
126 | |||
127 | /* Last element in command line list */ | ||
128 | static struct history *his_end = NULL; | ||
69 | 129 | ||
70 | static struct history *his_front = NULL; /* First element in command line list */ | ||
71 | static struct history *his_end = NULL; /* Last element in command line list */ | ||
72 | 130 | ||
73 | /* ED: sparc termios is broken: revert back to old termio handling. */ | 131 | /* ED: sparc termios is broken: revert back to old termio handling. */ |
74 | 132 | ||
@@ -87,87 +145,100 @@ static struct history *his_end = NULL; /* Last element in command line list */ | |||
87 | static struct termios initial_settings, new_settings; | 145 | static struct termios initial_settings, new_settings; |
88 | 146 | ||
89 | 147 | ||
90 | #ifndef _POSIX_VDISABLE | 148 | #ifndef _POSIX_VDISABLE |
91 | #define _POSIX_VDISABLE '\0' | 149 | #define _POSIX_VDISABLE '\0' |
92 | #endif | 150 | #endif |
93 | 151 | ||
94 | 152 | ||
95 | static | 153 | static |
96 | volatile int cmdedit_termw; /* actual terminal width */ | 154 | volatile int cmdedit_termw = 80; /* actual terminal width */ |
97 | static int history_counter = 0; /* Number of commands in history list */ | 155 | static int history_counter = 0; /* Number of commands in history list */ |
98 | |||
99 | static | 156 | static |
100 | volatile int handlers_sets = 0; /* Set next bites | 157 | volatile int handlers_sets = 0; /* Set next bites: */ |
101 | when atexit() has been called | 158 | |
102 | and set many "terminates" signal handlers | ||
103 | and winchg signal handler | ||
104 | and if the terminal needs to be reset upon exit | ||
105 | */ | ||
106 | enum { | 159 | enum { |
107 | SET_ATEXIT = 1, | 160 | SET_ATEXIT = 1, /* when atexit() has been called and |
108 | SET_TERM_HANDLERS = 2, | 161 | get euid,uid,gid to fast compare */ |
109 | SET_WCHG_HANDLERS = 4, | 162 | SET_TERM_HANDLERS = 2, /* set many terminates signal handlers */ |
110 | SET_RESET_TERM = 8, | 163 | SET_WCHG_HANDLERS = 4, /* winchg signal handler */ |
164 | SET_RESET_TERM = 8, /* if the terminal needs to be reset upon exit */ | ||
111 | }; | 165 | }; |
112 | 166 | ||
113 | |||
114 | static int cmdedit_x; /* real x terminal position, | ||
115 | require put prompt in start x position */ | ||
116 | static int cmdedit_y; /* pseudoreal y terminal position */ | ||
117 | static int cmdedit_prmt_len; /* for fast running, without duplicate calculate */ | ||
118 | 167 | ||
119 | static int cursor; /* required global for signal handler */ | 168 | static int cmdedit_x; /* real x terminal position */ |
120 | static int len; /* --- "" - - "" - -"- --""-- --""--- */ | 169 | static int cmdedit_y; /* pseudoreal y terminal position */ |
121 | static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */ | 170 | static int cmdedit_prmt_len; /* lenght prompt without colores string */ |
122 | static const char *cmdedit_prompt;/* --- "" - - "" - -"- --""-- --""--- */ | ||
123 | 171 | ||
124 | /* Link into lash to reset context to 0 | 172 | static int cursor; /* required global for signal handler */ |
125 | * on ^C and such */ | 173 | static int len; /* --- "" - - "" - -"- --""-- --""--- */ |
174 | static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */ | ||
175 | static | ||
176 | #ifndef BB_FEATURE_BASH_STYLE_PROMT | ||
177 | const | ||
178 | #endif | ||
179 | char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */ | ||
180 | |||
181 | /* Link into lash to reset context to 0 on ^C and such */ | ||
126 | extern unsigned int shell_context; | 182 | extern unsigned int shell_context; |
127 | 183 | ||
128 | 184 | ||
129 | struct history { | 185 | #ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR |
130 | char *s; | 186 | static char *user_buf = ""; |
131 | struct history *p; | 187 | static char *home_pwd_buf = ""; |
132 | struct history *n; | 188 | static int my_euid; |
133 | }; | 189 | #endif |
190 | |||
191 | #ifdef BB_FEATURE_BASH_STYLE_PROMT | ||
192 | static char *hostname_buf = ""; | ||
193 | static int num_ok_lines = 1; | ||
194 | #endif | ||
195 | |||
196 | |||
197 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | ||
198 | |||
199 | #ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIR | ||
200 | static int my_euid; | ||
201 | #endif | ||
202 | |||
203 | static int my_uid; | ||
204 | static int my_gid; | ||
205 | |||
206 | #endif /* BB_FEATURE_SH_TAB_COMPLETION */ | ||
207 | |||
134 | 208 | ||
135 | static void cmdedit_setwidth(int w, int redraw_flg); | 209 | static void cmdedit_setwidth(int w, int redraw_flg); |
136 | 210 | ||
137 | static void win_changed(int nsig) | 211 | static void win_changed(int nsig) |
138 | { | 212 | { |
139 | struct winsize win = { 0, 0, 0, 0 }; | 213 | struct winsize win = { 0, 0, 0, 0 }; |
140 | static __sighandler_t previous_SIGWINCH_handler; /* for reset */ | 214 | static __sighandler_t previous_SIGWINCH_handler; /* for reset */ |
141 | 215 | ||
142 | /* emulate signal call if not called as a sig handler */ | 216 | /* emulate || signal call */ |
143 | if(nsig == -SIGWINCH || nsig == SIGWINCH) { | 217 | if (nsig == -SIGWINCH || nsig == SIGWINCH) { |
144 | ioctl(0, TIOCGWINSZ, &win); | 218 | ioctl(0, TIOCGWINSZ, &win); |
145 | if (win.ws_col > 0) { | 219 | if (win.ws_col > 0) { |
146 | cmdedit_setwidth( win.ws_col, nsig == SIGWINCH ); | 220 | cmdedit_setwidth(win.ws_col, nsig == SIGWINCH); |
147 | } else { | 221 | } |
148 | /* Default to 79 if their console doesn't want to share */ | ||
149 | cmdedit_setwidth( 79, nsig == SIGWINCH ); | ||
150 | } | ||
151 | } | 222 | } |
152 | |||
153 | /* Unix not all standart in recall signal */ | 223 | /* Unix not all standart in recall signal */ |
154 | 224 | ||
155 | if(nsig == -SIGWINCH) /* save previous handler */ | 225 | if (nsig == -SIGWINCH) /* save previous handler */ |
156 | previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); | 226 | previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); |
157 | else if(nsig == SIGWINCH) /* signaled called handler */ | 227 | else if (nsig == SIGWINCH) /* signaled called handler */ |
158 | signal(SIGWINCH, win_changed); /* set for next call */ | 228 | signal(SIGWINCH, win_changed); /* set for next call */ |
159 | else /* set previous handler */ | 229 | else /* nsig == 0 */ |
160 | signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */ | 230 | /* set previous handler */ |
231 | signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */ | ||
161 | } | 232 | } |
162 | 233 | ||
163 | static void cmdedit_reset_term(void) | 234 | static void cmdedit_reset_term(void) |
164 | { | 235 | { |
165 | if((handlers_sets & SET_RESET_TERM)!=0) { | 236 | if ((handlers_sets & SET_RESET_TERM) != 0) { |
166 | /* sparc and other have broken termios support: use old termio handling. */ | 237 | /* sparc and other have broken termios support: use old termio handling. */ |
167 | setTermSettings(fileno(stdin), (void*) &initial_settings); | 238 | setTermSettings(fileno(stdin), (void *) &initial_settings); |
168 | handlers_sets &= ~SET_RESET_TERM; | 239 | handlers_sets &= ~SET_RESET_TERM; |
169 | } | 240 | } |
170 | if((handlers_sets & SET_WCHG_HANDLERS)!=0) { | 241 | if ((handlers_sets & SET_WCHG_HANDLERS) != 0) { |
171 | /* reset SIGWINCH handler to previous (default) */ | 242 | /* reset SIGWINCH handler to previous (default) */ |
172 | win_changed(0); | 243 | win_changed(0); |
173 | handlers_sets &= ~SET_WCHG_HANDLERS; | 244 | handlers_sets &= ~SET_WCHG_HANDLERS; |
@@ -176,28 +247,45 @@ static void cmdedit_reset_term(void) | |||
176 | #ifdef BB_FEATURE_CLEAN_UP | 247 | #ifdef BB_FEATURE_CLEAN_UP |
177 | if (his_front) { | 248 | if (his_front) { |
178 | struct history *n; | 249 | struct history *n; |
250 | |||
179 | //while(his_front!=his_end) { | 251 | //while(his_front!=his_end) { |
180 | while(his_front!=his_end) { | 252 | while (his_front != his_end) { |
181 | n = his_front->n; | 253 | n = his_front->n; |
182 | free(his_front->s); | 254 | free(his_front->s); |
183 | free(his_front); | 255 | free(his_front); |
184 | his_front=n; | 256 | his_front = n; |
185 | } | 257 | } |
186 | } | 258 | } |
187 | #endif | 259 | #endif |
188 | } | 260 | } |
189 | 261 | ||
190 | 262 | ||
191 | |||
192 | /* special for recount position for scroll and remove terminal margin effect */ | 263 | /* special for recount position for scroll and remove terminal margin effect */ |
193 | static void cmdedit_set_out_char(int c, int next_char) { | 264 | static void cmdedit_set_out_char(int next_char) |
194 | putchar(c); | 265 | { |
195 | if(++cmdedit_x>=cmdedit_termw) { | 266 | |
267 | int c = command_ps[cursor]; | ||
268 | |||
269 | if (c == 0) | ||
270 | c = ' '; /* destroy end char? */ | ||
271 | #ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT | ||
272 | if (!isprint(c)) { /* Inverse put non-printable characters */ | ||
273 | if (((unsigned char) c) >= 128) | ||
274 | c -= 128; | ||
275 | if (((unsigned char) c) < ' ') | ||
276 | c += '@'; | ||
277 | if (c == 127) | ||
278 | c = '?'; | ||
279 | printf("\033[7m%c\033[0m", c); | ||
280 | } else | ||
281 | #endif | ||
282 | putchar(c); | ||
283 | if (++cmdedit_x >= cmdedit_termw) { | ||
196 | /* terminal is scrolled down */ | 284 | /* terminal is scrolled down */ |
197 | cmdedit_y++; | 285 | cmdedit_y++; |
198 | cmdedit_x=0; | 286 | cmdedit_x = 0; |
199 | 287 | ||
200 | if(!next_char) | 288 | if (!next_char) |
201 | next_char = ' '; | 289 | next_char = ' '; |
202 | /* destroy "(auto)margin" */ | 290 | /* destroy "(auto)margin" */ |
203 | putchar(next_char); | 291 | putchar(next_char); |
@@ -206,56 +294,221 @@ static void cmdedit_set_out_char(int c, int next_char) { | |||
206 | cursor++; | 294 | cursor++; |
207 | } | 295 | } |
208 | 296 | ||
209 | /* Move to end line. Bonus: rewrite line from cursor without use | 297 | /* Move to end line. Bonus: rewrite line from cursor */ |
210 | special control terminal strings, also saved size and speed! */ | 298 | static void input_end(void) |
211 | static void input_end (void) { | 299 | { |
212 | while(cursor < len) | 300 | while (cursor < len) |
213 | cmdedit_set_out_char(command_ps[cursor], 0); | 301 | cmdedit_set_out_char(0); |
214 | } | 302 | } |
215 | 303 | ||
216 | /* Go to the next line */ | 304 | /* Go to the next line */ |
217 | static void goto_new_line(void) { | 305 | static void goto_new_line(void) |
306 | { | ||
218 | input_end(); | 307 | input_end(); |
219 | cmdedit_set_out_char('\n', 0); | 308 | if (cmdedit_x) |
309 | putchar('\n'); | ||
220 | } | 310 | } |
221 | 311 | ||
222 | 312 | ||
223 | static inline void out1str(const char *s) { fputs (s, stdout); } | 313 | static inline void out1str(const char *s) |
224 | static inline void beep (void) { putchar('\007'); } | 314 | { |
315 | fputs(s, stdout); | ||
316 | } | ||
317 | static inline void beep(void) | ||
318 | { | ||
319 | putchar('\007'); | ||
320 | } | ||
225 | 321 | ||
226 | /* Go to HOME position */ | 322 | /* Move back one charactor */ |
227 | static void input_home(void) | 323 | /* special for slow terminal */ |
324 | static void input_backward(int num) | ||
228 | { | 325 | { |
229 | while(cmdedit_y>0) { /* up to start y */ | 326 | if (num > cursor) |
230 | out1str("\033[A"); | 327 | num = cursor; |
231 | cmdedit_y--; | 328 | cursor -= num; /* new cursor (in command, not terminal) */ |
329 | |||
330 | if (cmdedit_x >= num) { /* no to up line */ | ||
331 | cmdedit_x -= num; | ||
332 | if (num < 4) | ||
333 | while (num-- > 0) | ||
334 | putchar('\b'); | ||
335 | |||
336 | else | ||
337 | printf("\033[%dD", num); | ||
338 | } else { | ||
339 | int count_y; | ||
340 | |||
341 | if (cmdedit_x) { | ||
342 | putchar('\r'); /* back to first terminal pos. */ | ||
343 | num -= cmdedit_x; /* set previous backward */ | ||
344 | } | ||
345 | count_y = 1 + num / cmdedit_termw; | ||
346 | printf("\033[%dA", count_y); | ||
347 | cmdedit_y -= count_y; | ||
348 | /* require forward after uping */ | ||
349 | cmdedit_x = cmdedit_termw * count_y - num; | ||
350 | printf("\033[%dC", cmdedit_x); /* set term cursor */ | ||
232 | } | 351 | } |
233 | putchar('\r'); | 352 | } |
234 | cursor = 0; | 353 | |
354 | static void put_prompt(void) | ||
355 | { | ||
235 | out1str(cmdedit_prompt); | 356 | out1str(cmdedit_prompt); |
236 | cmdedit_x = cmdedit_prmt_len; | 357 | cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */ |
358 | cursor = 0; | ||
359 | } | ||
237 | 360 | ||
361 | #ifdef BB_FEATURE_BASH_STYLE_PROMT | ||
362 | static void | ||
363 | add_to_prompt(char **prmt_mem_ptr, int *alm, int *prmt_len, | ||
364 | const char *addb) | ||
365 | { | ||
366 | int l = strlen(addb); | ||
367 | |||
368 | *prmt_len += l; | ||
369 | if (*alm < (*prmt_len) + 1) { | ||
370 | *alm = (*prmt_len) + 1; | ||
371 | *prmt_mem_ptr = xrealloc(*prmt_mem_ptr, *alm); | ||
372 | } | ||
373 | strcat(*prmt_mem_ptr, addb); | ||
238 | } | 374 | } |
375 | #endif | ||
239 | 376 | ||
240 | /* Move back one charactor */ | 377 | static void parse_prompt(const char *prmt_ptr) |
241 | static void input_backward(void) { | 378 | { |
242 | if (cursor > 0) { | 379 | #ifdef BB_FEATURE_BASH_STYLE_PROMT |
243 | cursor--; | 380 | int alm = strlen(prmt_ptr) + 1; /* supposedly require memory */ |
244 | if(cmdedit_x!=0) { /* no first position in terminal line */ | 381 | int prmt_len = 0; |
245 | putchar('\b'); | 382 | int sub_len = 0; |
246 | cmdedit_x--; | 383 | int flg_not_length = '['; |
384 | char *prmt_mem_ptr = xstrdup(prmt_ptr); | ||
385 | char pwd_buf[PATH_MAX + 1]; | ||
386 | char buf[16]; | ||
387 | int c; | ||
388 | |||
389 | pwd_buf[0] = 0; | ||
390 | *prmt_mem_ptr = 0; | ||
391 | |||
392 | while (*prmt_ptr) { | ||
393 | c = *prmt_ptr++; | ||
394 | if (c == '\\') { | ||
395 | c = *prmt_ptr; | ||
396 | if (c == 0) | ||
397 | break; | ||
398 | prmt_ptr++; | ||
399 | switch (c) { | ||
400 | case 'u': | ||
401 | add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, user_buf); | ||
402 | continue; | ||
403 | case 'h': | ||
404 | if (hostname_buf[0] == 0) { | ||
405 | hostname_buf = xcalloc(256, 1); | ||
406 | if (gethostname(hostname_buf, 255) < 0) { | ||
407 | strcpy(hostname_buf, "?"); | ||
408 | } else { | ||
409 | char *s = strchr(hostname_buf, '.'); | ||
410 | |||
411 | if (s) | ||
412 | *s = 0; | ||
413 | } | ||
414 | } | ||
415 | add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, | ||
416 | hostname_buf); | ||
417 | continue; | ||
418 | case '$': | ||
419 | c = my_euid == 0 ? '#' : '$'; | ||
420 | break; | ||
421 | case 'w': | ||
422 | if (pwd_buf[0] == 0) { | ||
423 | int l; | ||
424 | |||
425 | getcwd(pwd_buf, PATH_MAX); | ||
426 | l = strlen(home_pwd_buf); | ||
427 | if (home_pwd_buf[0] != 0 && | ||
428 | strncmp(home_pwd_buf, pwd_buf, l) == 0) { | ||
429 | strcpy(pwd_buf + 1, pwd_buf + l); | ||
430 | pwd_buf[0] = '~'; | ||
431 | } | ||
432 | } | ||
433 | add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, pwd_buf); | ||
434 | continue; | ||
435 | case '!': | ||
436 | snprintf(buf, sizeof(buf), "%d", num_ok_lines); | ||
437 | add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, buf); | ||
438 | continue; | ||
439 | case 'e': | ||
440 | case 'E': /* \e \E = \033 */ | ||
441 | c = '\033'; | ||
442 | break; | ||
443 | case 'x': | ||
444 | case 'X': | ||
445 | case '0': | ||
446 | case '1': | ||
447 | case '2': | ||
448 | case '3': | ||
449 | case '4': | ||
450 | case '5': | ||
451 | case '6': | ||
452 | case '7':{ | ||
453 | int l; | ||
454 | int ho = 0; | ||
455 | char *eho; | ||
456 | |||
457 | if (c == 'X') | ||
458 | c = 'x'; | ||
459 | |||
460 | for (l = 0; l < 3;) { | ||
461 | |||
462 | buf[l++] = *prmt_ptr; | ||
463 | buf[l] = 0; | ||
464 | ho = strtol(buf, &eho, c == 'x' ? 16 : 8); | ||
465 | if (ho > UCHAR_MAX || (eho - buf) < l) { | ||
466 | l--; | ||
467 | break; | ||
468 | } | ||
469 | prmt_ptr++; | ||
470 | } | ||
471 | buf[l] = 0; | ||
472 | ho = strtol(buf, 0, c == 'x' ? 16 : 8); | ||
473 | c = ho == 0 ? '?' : (char) ho; | ||
474 | break; | ||
247 | } | 475 | } |
248 | else { | 476 | case '[': |
249 | out1str("\033[A"); /* up */ | 477 | case ']': |
250 | cmdedit_y--; | 478 | if (c == flg_not_length) { |
251 | 479 | flg_not_length = flg_not_length == '[' ? ']' : '['; | |
252 | /* to end in current terminal line */ | 480 | continue; |
253 | while(cmdedit_x<(cmdedit_termw-1)) { | ||
254 | out1str("\033[C"); | ||
255 | cmdedit_x++; | ||
256 | } | 481 | } |
482 | break; | ||
257 | } | 483 | } |
484 | } | ||
485 | buf[0] = c; | ||
486 | buf[1] = 0; | ||
487 | add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, buf); | ||
488 | if (flg_not_length == ']') | ||
489 | sub_len++; | ||
258 | } | 490 | } |
491 | cmdedit_prmt_len = prmt_len - sub_len; | ||
492 | cmdedit_prompt = prmt_mem_ptr; | ||
493 | #else | ||
494 | cmdedit_prompt = prmt_ptr; | ||
495 | cmdedit_prmt_len = strlen(prmt_ptr); | ||
496 | #endif | ||
497 | put_prompt(); | ||
498 | } | ||
499 | |||
500 | |||
501 | /* draw promt, editor line, and clear tail */ | ||
502 | static void redraw(int y, int back_cursor) | ||
503 | { | ||
504 | if (y > 0) /* up to start y */ | ||
505 | printf("\033[%dA", y); | ||
506 | cmdedit_y = 0; /* new quasireal y */ | ||
507 | putchar('\r'); | ||
508 | put_prompt(); | ||
509 | input_end(); /* rewrite */ | ||
510 | printf("\033[J"); /* destroy tail after cursor */ | ||
511 | input_backward(back_cursor); | ||
259 | } | 512 | } |
260 | 513 | ||
261 | /* Delete the char in front of the cursor */ | 514 | /* Delete the char in front of the cursor */ |
@@ -265,21 +518,20 @@ static void input_delete(void) | |||
265 | 518 | ||
266 | if (j == len) | 519 | if (j == len) |
267 | return; | 520 | return; |
268 | 521 | ||
269 | memmove (command_ps + j, command_ps + j + 1, BUFSIZ - j - 1); | 522 | strcpy(command_ps + j, command_ps + j + 1); |
270 | len--; | 523 | len--; |
271 | input_end(); /* rewtite new line */ | 524 | input_end(); /* rewtite new line */ |
272 | cmdedit_set_out_char(' ', 0); /* destroy end char */ | 525 | cmdedit_set_out_char(0); /* destroy end char */ |
273 | while (j < cursor) | 526 | input_backward(cursor - j); /* back to old pos cursor */ |
274 | input_backward(); /* back to old pos cursor */ | ||
275 | } | 527 | } |
276 | 528 | ||
277 | /* Delete the char in back of the cursor */ | 529 | /* Delete the char in back of the cursor */ |
278 | static void input_backspace(void) | 530 | static void input_backspace(void) |
279 | { | 531 | { |
280 | if (cursor > 0) { | 532 | if (cursor > 0) { |
281 | input_backward(); | 533 | input_backward(1); |
282 | input_delete (); | 534 | input_delete(); |
283 | } | 535 | } |
284 | } | 536 | } |
285 | 537 | ||
@@ -287,382 +539,653 @@ static void input_backspace(void) | |||
287 | /* Move forward one charactor */ | 539 | /* Move forward one charactor */ |
288 | static void input_forward(void) | 540 | static void input_forward(void) |
289 | { | 541 | { |
290 | if (cursor < len) | 542 | if (cursor < len) |
291 | cmdedit_set_out_char(command_ps[cursor], command_ps[cursor + 1]); | 543 | cmdedit_set_out_char(command_ps[cursor + 1]); |
292 | } | 544 | } |
293 | 545 | ||
294 | 546 | ||
295 | static void clean_up_and_die(int sig) | 547 | static void clean_up_and_die(int sig) |
296 | { | 548 | { |
297 | goto_new_line(); | 549 | goto_new_line(); |
298 | if (sig!=SIGINT) | 550 | if (sig != SIGINT) |
299 | exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */ | 551 | exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */ |
300 | cmdedit_reset_term(); | 552 | cmdedit_reset_term(); |
301 | } | 553 | } |
302 | 554 | ||
303 | static void cmdedit_setwidth(int w, int redraw_flg) | 555 | static void cmdedit_setwidth(int w, int redraw_flg) |
304 | { | 556 | { |
305 | cmdedit_termw = cmdedit_prmt_len+2; | 557 | cmdedit_termw = cmdedit_prmt_len + 2; |
306 | if (w > cmdedit_termw) { | 558 | if (w > cmdedit_termw) { |
307 | 559 | ||
308 | cmdedit_termw = w; | 560 | cmdedit_termw = w; |
309 | 561 | ||
310 | if(redraw_flg) { | 562 | if (redraw_flg) { |
311 | int sav_cursor = cursor; | 563 | /* new y for current cursor */ |
312 | 564 | int new_y = (cursor + cmdedit_prmt_len) / w; | |
313 | /* set variables for new terminal size */ | ||
314 | cmdedit_y = sav_cursor/w; | ||
315 | cmdedit_x = sav_cursor-cmdedit_y*w; | ||
316 | 565 | ||
317 | /* redraw */ | 566 | /* redraw */ |
318 | input_home(); | 567 | redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor); |
319 | input_end(); | 568 | fflush(stdout); |
320 | while(sav_cursor<cursor) | ||
321 | input_backward(); | ||
322 | } | 569 | } |
323 | } else { | 570 | } else { |
324 | error_msg("\n*** Error: minimum screen width is %d", cmdedit_termw); | 571 | error_msg("\n*** Error: minimum screen width is %d", |
572 | cmdedit_termw); | ||
325 | } | 573 | } |
326 | } | 574 | } |
327 | 575 | ||
328 | extern void cmdedit_init(void) | 576 | extern void cmdedit_init(void) |
329 | { | 577 | { |
330 | if((handlers_sets & SET_WCHG_HANDLERS)==0) { | 578 | if ((handlers_sets & SET_WCHG_HANDLERS) == 0) { |
331 | /* pretend we received a signal in order to set term size and sig handling */ | 579 | /* emulate usage handler to set handler and call yours work */ |
332 | win_changed(-SIGWINCH); | 580 | win_changed(-SIGWINCH); |
333 | handlers_sets |= SET_WCHG_HANDLERS; | 581 | handlers_sets |= SET_WCHG_HANDLERS; |
334 | } | 582 | } |
335 | 583 | ||
336 | if((handlers_sets & SET_ATEXIT)==0) { | 584 | if ((handlers_sets & SET_ATEXIT) == 0) { |
337 | atexit(cmdedit_reset_term); /* be sure to do this only once */ | 585 | #ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR |
586 | struct passwd *entry; | ||
587 | |||
588 | my_euid = geteuid(); | ||
589 | entry = getpwuid(my_euid); | ||
590 | if (entry) { | ||
591 | user_buf = xstrdup(entry->pw_name); | ||
592 | home_pwd_buf = xstrdup(entry->pw_dir); | ||
593 | } | ||
594 | #endif | ||
595 | |||
596 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | ||
597 | |||
598 | #ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIR | ||
599 | my_euid = geteuid(); | ||
600 | #endif | ||
601 | my_uid = getuid(); | ||
602 | my_gid = getgid(); | ||
603 | #endif /* BB_FEATURE_SH_TAB_COMPLETION */ | ||
338 | handlers_sets |= SET_ATEXIT; | 604 | handlers_sets |= SET_ATEXIT; |
605 | atexit(cmdedit_reset_term); /* be sure to do this only once */ | ||
339 | } | 606 | } |
340 | if((handlers_sets & SET_TERM_HANDLERS)==0) { | 607 | |
608 | if ((handlers_sets & SET_TERM_HANDLERS) == 0) { | ||
341 | signal(SIGKILL, clean_up_and_die); | 609 | signal(SIGKILL, clean_up_and_die); |
342 | signal(SIGINT, clean_up_and_die); | 610 | signal(SIGINT, clean_up_and_die); |
343 | signal(SIGQUIT, clean_up_and_die); | 611 | signal(SIGQUIT, clean_up_and_die); |
344 | signal(SIGTERM, clean_up_and_die); | 612 | signal(SIGTERM, clean_up_and_die); |
345 | handlers_sets |= SET_TERM_HANDLERS; | 613 | handlers_sets |= SET_TERM_HANDLERS; |
346 | } | 614 | } |
615 | |||
347 | } | 616 | } |
348 | 617 | ||
349 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | 618 | #ifdef BB_FEATURE_SH_TAB_COMPLETION |
350 | 619 | ||
620 | static int is_execute(const struct stat *st) | ||
621 | { | ||
622 | if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) || | ||
623 | (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) || | ||
624 | (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) || | ||
625 | (st->st_mode & S_IXOTH)) return TRUE; | ||
626 | return FALSE; | ||
627 | } | ||
628 | |||
351 | #ifdef BB_FEATURE_USERNAME_COMPLETION | 629 | #ifdef BB_FEATURE_USERNAME_COMPLETION |
352 | static char** username_tab_completion(char *ud, int *num_matches) | 630 | |
631 | static char **username_tab_completion(char *ud, int *num_matches) | ||
353 | { | 632 | { |
354 | static struct passwd *entry; | 633 | struct passwd *entry; |
355 | int userlen; | 634 | int userlen; |
356 | char **matches = (char **) NULL; | 635 | char *temp; |
357 | char *temp; | ||
358 | int nm = 0; | ||
359 | 636 | ||
360 | setpwent (); | ||
361 | userlen = strlen (ud + 1); | ||
362 | 637 | ||
363 | while ((entry = getpwent ()) != NULL) { | 638 | ud++; /* ~user/... to user/... */ |
364 | /* Null usernames should result in all users as possible completions. */ | 639 | userlen = strlen(ud); |
365 | if (!userlen || !strncmp (ud + 1, entry->pw_name, userlen)) { | ||
366 | 640 | ||
367 | temp = xmalloc (3 + strlen (entry->pw_name)); | 641 | if (num_matches == 0) { /* "~/..." or "~user/..." */ |
368 | sprintf(temp, "~%s/", entry->pw_name); | 642 | char *sav_ud = ud - 1; |
643 | char *home = 0; | ||
369 | 644 | ||
370 | matches = xrealloc(matches, (nm+1)*sizeof(char *)); | 645 | if (*ud == '/') { /* "~/..." */ |
371 | matches[nm++] = temp; | 646 | home = home_pwd_buf; |
372 | } | 647 | } else { |
373 | } | 648 | /* "~user/..." */ |
649 | temp = strchr(ud, '/'); | ||
650 | *temp = 0; /* ~user\0 */ | ||
651 | entry = getpwnam(ud); | ||
652 | *temp = '/'; /* restore ~user/... */ | ||
653 | ud = temp; | ||
654 | if (entry) | ||
655 | home = entry->pw_dir; | ||
656 | } | ||
657 | if (home) { | ||
658 | if ((userlen + strlen(home) + 1) < BUFSIZ) { | ||
659 | char temp2[BUFSIZ]; /* argument size */ | ||
374 | 660 | ||
375 | endpwent (); | 661 | /* /home/user/... */ |
376 | (*num_matches) = nm; | 662 | sprintf(temp2, "%s%s", home, ud); |
377 | return (matches); | 663 | strcpy(sav_ud, temp2); |
664 | } | ||
665 | } | ||
666 | return 0; /* void, result save to argument :-) */ | ||
667 | } else { | ||
668 | /* "~[^/]*" */ | ||
669 | char **matches = (char **) NULL; | ||
670 | int nm = 0; | ||
671 | |||
672 | setpwent(); | ||
673 | |||
674 | while ((entry = getpwent()) != NULL) { | ||
675 | /* Null usernames should result in all users as possible completions. */ | ||
676 | if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) { | ||
677 | |||
678 | temp = xmalloc(3 + strlen(entry->pw_name)); | ||
679 | sprintf(temp, "~%s/", entry->pw_name); | ||
680 | matches = xrealloc(matches, (nm + 1) * sizeof(char *)); | ||
681 | |||
682 | matches[nm++] = temp; | ||
683 | } | ||
684 | } | ||
685 | |||
686 | endpwent(); | ||
687 | (*num_matches) = nm; | ||
688 | return (matches); | ||
689 | } | ||
378 | } | 690 | } |
379 | #endif | 691 | #endif /* BB_FEATURE_USERNAME_COMPLETION */ |
380 | 692 | ||
381 | enum { | 693 | enum { |
382 | FIND_EXE_ONLY = 0, | 694 | FIND_EXE_ONLY = 0, |
383 | FIND_DIR_ONLY = 1, | 695 | FIND_DIR_ONLY = 1, |
384 | FIND_FILE_ONLY = 2, | 696 | FIND_FILE_ONLY = 2, |
385 | }; | 697 | }; |
386 | 698 | ||
387 | #include <dirent.h> | ||
388 | |||
389 | static int path_parse(char ***p, int flags) | 699 | static int path_parse(char ***p, int flags) |
390 | { | 700 | { |
391 | int npth; | 701 | int npth; |
392 | char *tmp; | 702 | char *tmp; |
393 | char *pth; | 703 | char *pth; |
394 | 704 | ||
395 | if(flags!=FIND_EXE_ONLY || (pth=getenv("PATH"))==0) { | ||
396 | /* if not setenv PATH variable, to search cur dir "." */ | 705 | /* if not setenv PATH variable, to search cur dir "." */ |
397 | (*p) = xmalloc(sizeof(char *)); | 706 | if (flags != FIND_EXE_ONLY || (pth = getenv("PATH")) == 0 || |
398 | (*p)[0] = xstrdup("."); | 707 | /* PATH=<empty> or PATH=:<empty> */ |
708 | *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) { | ||
399 | return 1; | 709 | return 1; |
400 | } | 710 | } |
401 | 711 | ||
402 | tmp = pth; | 712 | tmp = pth; |
403 | npth=0; | 713 | npth = 0; |
404 | 714 | ||
405 | for(;;) { | 715 | for (;;) { |
406 | npth++; /* count words is + 1 count ':' */ | 716 | npth++; /* count words is + 1 count ':' */ |
407 | tmp = strchr(tmp, ':'); | 717 | tmp = strchr(tmp, ':'); |
408 | if(tmp) | 718 | if (tmp) { |
409 | tmp++; | 719 | if (*++tmp == 0) |
410 | else | 720 | break; /* :<empty> */ |
721 | } else | ||
411 | break; | 722 | break; |
412 | } | 723 | } |
413 | 724 | ||
414 | *p = xmalloc(npth*sizeof(char *)); | 725 | *p = xmalloc(npth * sizeof(char *)); |
415 | 726 | ||
416 | tmp = pth; | 727 | tmp = pth; |
417 | (*p)[0] = xstrdup(tmp); | 728 | (*p)[0] = xstrdup(tmp); |
418 | npth=1; /* count words is + 1 count ':' */ | 729 | npth = 1; /* count words is + 1 count ':' */ |
419 | 730 | ||
420 | for(;;) { | 731 | for (;;) { |
421 | tmp = strchr(tmp, ':'); | 732 | tmp = strchr(tmp, ':'); |
422 | if(tmp) { | 733 | if (tmp) { |
423 | (*p)[0][(tmp-pth)]=0; /* ':' -> '\0'*/ | 734 | (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */ |
424 | tmp++; | 735 | if (*++tmp == 0) |
736 | break; /* :<empty> */ | ||
425 | } else | 737 | } else |
426 | break; | 738 | break; |
427 | (*p)[npth++] = &(*p)[0][(tmp-pth)]; /* p[next]=p[0][&'\0'+1] */ | 739 | (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */ |
428 | } | 740 | } |
429 | 741 | ||
430 | return npth; | 742 | return npth; |
431 | } | 743 | } |
432 | 744 | ||
433 | static char** exe_n_cwd_tab_completion(char* command, int *num_matches, int type) | 745 | static char *add_quote_for_spec_chars(char *found) |
434 | { | 746 | { |
435 | char *dirName; | 747 | int l = 0; |
436 | char **matches = 0; | 748 | char *s = xmalloc((strlen(found) + 1) * 2); |
437 | DIR *dir; | ||
438 | struct dirent *next; | ||
439 | char cmd [BUFSIZ+4]; | ||
440 | char *dirbuf; | ||
441 | char found [BUFSIZ+4]; | ||
442 | int nm = *num_matches; | ||
443 | struct stat st; | ||
444 | char **paths; | ||
445 | int npaths; | ||
446 | int i; | ||
447 | char full_pth[BUFSIZ+4+PATH_MAX]; | ||
448 | 749 | ||
750 | while (*found) { | ||
751 | if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found)) | ||
752 | s[l++] = '\\'; | ||
753 | s[l++] = *found++; | ||
754 | } | ||
755 | s[l] = 0; | ||
756 | return s; | ||
757 | } | ||
449 | 758 | ||
450 | strcpy(cmd, command); /* save for change (last '/' to '\0') */ | 759 | static char **exe_n_cwd_tab_completion(char *command, int *num_matches, |
760 | int type) | ||
761 | { | ||
451 | 762 | ||
452 | dirName = strrchr(cmd, '/'); | 763 | char **matches = 0; |
453 | if(dirName==NULL) { | 764 | DIR *dir; |
765 | struct dirent *next; | ||
766 | char dirbuf[BUFSIZ]; | ||
767 | int nm = *num_matches; | ||
768 | struct stat st; | ||
769 | char *path1[1]; | ||
770 | char **paths = path1; | ||
771 | int npaths; | ||
772 | int i; | ||
773 | char found[BUFSIZ + 4 + PATH_MAX]; | ||
774 | char *pfind = strrchr(command, '/'); | ||
775 | |||
776 | path1[0] = "."; | ||
777 | |||
778 | if (pfind == NULL) { | ||
454 | /* no dir, if flags==EXE_ONLY - get paths, else "." */ | 779 | /* no dir, if flags==EXE_ONLY - get paths, else "." */ |
455 | npaths = path_parse(&paths, type); | 780 | npaths = path_parse(&paths, type); |
456 | if(npaths==0) | 781 | pfind = command; |
457 | return 0; | ||
458 | } else { | 782 | } else { |
459 | /* with dir */ | 783 | /* with dir */ |
784 | /* save for change */ | ||
785 | strcpy(dirbuf, command); | ||
786 | /* set dir only */ | ||
787 | dirbuf[(pfind - command) + 1] = 0; | ||
788 | #ifdef BB_FEATURE_USERNAME_COMPLETION | ||
789 | if (dirbuf[0] == '~') /* ~/... or ~user/... */ | ||
790 | username_tab_completion(dirbuf, 0); | ||
791 | #endif | ||
792 | /* "strip" dirname in command */ | ||
793 | pfind++; | ||
460 | 794 | ||
461 | /* save dir */ | ||
462 | dirbuf = xstrdup(cmd); | ||
463 | /* set only dirname */ | ||
464 | dirbuf[(dirName-cmd)+1]=0; | ||
465 | |||
466 | /* strip dirname in cmd */ | ||
467 | strcpy(cmd, dirName+1); | ||
468 | |||
469 | paths = xmalloc(sizeof(char*)); | ||
470 | paths[0] = dirbuf; | 795 | paths[0] = dirbuf; |
471 | npaths = 1; /* only 1 dir */ | 796 | npaths = 1; /* only 1 dir */ |
472 | } | 797 | } |
473 | 798 | ||
474 | for(i=0; i < npaths; i++) { | 799 | for (i = 0; i < npaths; i++) { |
475 | 800 | ||
476 | dir = opendir(paths[i]); | 801 | dir = opendir(paths[i]); |
477 | if (!dir) { | 802 | if (!dir) /* Don't print an error */ |
478 | /* Don't print an error, just shut up and return */ | 803 | continue; |
479 | return (matches); | 804 | |
480 | } | 805 | while ((next = readdir(dir)) != NULL) { |
481 | while ((next = readdir(dir)) != NULL) { | 806 | const char *str_merge = "%s/%s"; |
807 | char *str_found = next->d_name; | ||
808 | |||
482 | /* matched ? */ | 809 | /* matched ? */ |
483 | if(strncmp(next->d_name, cmd, strlen(cmd))) | 810 | if (strncmp(str_found, pfind, strlen(pfind))) |
484 | continue; | 811 | continue; |
485 | /* not see .name without .match */ | 812 | /* not see .name without .match */ |
486 | if(*next->d_name == '.' && *cmd != '.') | 813 | if (*str_found == '.' && *pfind == 0) { |
487 | continue; | 814 | if (*paths[i] == '/' && paths[i][1] == 0 |
488 | sprintf(full_pth, "%s/%s", paths[i], next->d_name); | 815 | && str_found[1] == 0) str_found = ""; /* only "/" */ |
489 | /* hmm, remover in progress? */ | 816 | else |
490 | if(stat(full_pth, &st)<0) | ||
491 | continue; | 817 | continue; |
492 | /* Cool, found a match. */ | 818 | } |
819 | if (paths[i][strlen(paths[i]) - 1] == '/') | ||
820 | str_merge = "%s%s"; | ||
821 | sprintf(found, str_merge, paths[i], str_found); | ||
822 | /* hmm, remover in progress? */ | ||
823 | if (stat(found, &st) < 0) | ||
824 | continue; | ||
825 | /* find with dirs ? */ | ||
826 | if (paths[i] != dirbuf) | ||
827 | strcpy(found, next->d_name); /* only name */ | ||
493 | if (S_ISDIR(st.st_mode)) { | 828 | if (S_ISDIR(st.st_mode)) { |
494 | /* name is directory */ | 829 | /* name is directory */ |
495 | strcpy(found, next->d_name); | 830 | /* algorithmic only "/" ? */ |
496 | strcat(found, "/"); | 831 | if (*str_found) |
497 | if(type==FIND_DIR_ONLY) | 832 | strcat(found, "/"); |
498 | strcat(found, " "); | 833 | str_found = add_quote_for_spec_chars(found); |
499 | } else { | 834 | } else { |
500 | /* not put found file if search only dirs for cd */ | 835 | /* not put found file if search only dirs for cd */ |
501 | if(type==FIND_DIR_ONLY) | 836 | if (type == FIND_DIR_ONLY) |
502 | continue; | 837 | continue; |
503 | strcpy(found, next->d_name); | 838 | str_found = add_quote_for_spec_chars(found); |
504 | strcat(found, " "); | 839 | if (type == FIND_FILE_ONLY || |
505 | } | 840 | (type == FIND_EXE_ONLY && is_execute(&st) == TRUE)) |
841 | strcat(str_found, " "); | ||
842 | } | ||
506 | /* Add it to the list */ | 843 | /* Add it to the list */ |
507 | matches = xrealloc(matches, (nm+1)*sizeof(char *)); | 844 | matches = xrealloc(matches, (nm + 1) * sizeof(char *)); |
508 | matches[nm++] = xstrdup(found); | 845 | |
846 | matches[nm++] = str_found; | ||
509 | } | 847 | } |
848 | closedir(dir); | ||
849 | } | ||
850 | if (paths != path1) { | ||
851 | free(paths[0]); /* allocated memory only in first member */ | ||
852 | free(paths); | ||
510 | } | 853 | } |
511 | free(paths[0]); /* allocate memory only in first member */ | ||
512 | free(paths); | ||
513 | *num_matches = nm; | 854 | *num_matches = nm; |
514 | return (matches); | 855 | return (matches); |
515 | } | 856 | } |
516 | 857 | ||
517 | static void input_tab(int lastWasTab) | 858 | static int match_compare(const void *a, const void *b) |
518 | { | 859 | { |
519 | /* Do TAB completion */ | 860 | return strcmp(*(char **) a, *(char **) b); |
520 | static int num_matches; | 861 | } |
521 | static char **matches; | ||
522 | 862 | ||
523 | char matchBuf[BUFSIZ]; | ||
524 | 863 | ||
525 | int pos = cursor; | ||
526 | int find_type=FIND_FILE_ONLY; | ||
527 | 864 | ||
865 | #define QUOT (UCHAR_MAX+1) | ||
528 | 866 | ||
529 | if (lastWasTab == FALSE) { | 867 | #define collapse_pos(is, in) { \ |
530 | char *tmp, *tmp1; | 868 | memcpy(int_buf+is, int_buf+in, (BUFSIZ+1-is-in)*sizeof(int)); \ |
531 | int len_found; | 869 | memcpy(pos_buf+is, pos_buf+in, (BUFSIZ+1-is-in)*sizeof(int)); } |
532 | 870 | ||
533 | /* For now, we will not bother with trying to distinguish | 871 | static int find_match(char *matchBuf, int *len_with_quotes) |
534 | * whether the cursor is in/at a command extression -- we | 872 | { |
535 | * will always try all possible matches. If you don't like | 873 | int i, j; |
536 | * that then feel free to fix it. | 874 | int command_mode; |
537 | */ | 875 | int c, c2; |
538 | 876 | int int_buf[BUFSIZ + 1]; | |
539 | /* Make a local copy of the string -- up | 877 | int pos_buf[BUFSIZ + 1]; |
540 | * to the position of the cursor */ | 878 | |
541 | memset(matchBuf, 0, BUFSIZ); | 879 | /* set to integer dimension characters and own positions */ |
542 | tmp = strncpy(matchBuf, command_ps, cursor); | 880 | for (i = 0;; i++) { |
881 | int_buf[i] = (int) ((unsigned char) matchBuf[i]); | ||
882 | if (int_buf[i] == 0) { | ||
883 | pos_buf[i] = -1; /* indicator end line */ | ||
884 | break; | ||
885 | } else | ||
886 | pos_buf[i] = i; | ||
887 | } | ||
543 | 888 | ||
544 | /* skip past any command seperator tokens */ | 889 | /* mask \+symbol and convert '\t' to ' ' */ |
545 | while ( (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { | 890 | for (i = j = 0; matchBuf[i]; i++, j++) |
546 | tmp = ++tmp1; | 891 | if (matchBuf[i] == '\\') { |
892 | collapse_pos(j, j + 1); | ||
893 | int_buf[j] |= QUOT; | ||
894 | i++; | ||
895 | #ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT | ||
896 | if (matchBuf[i] == '\t') /* algorithm equivalent */ | ||
897 | int_buf[j] = ' ' | QUOT; | ||
898 | #endif | ||
547 | } | 899 | } |
900 | #ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT | ||
901 | else if (matchBuf[i] == '\t') | ||
902 | int_buf[j] = ' '; | ||
903 | #endif | ||
548 | 904 | ||
549 | /* skip any leading white space */ | 905 | /* mask "symbols" or 'symbols' */ |
550 | while (*tmp == ' ') | 906 | c2 = 0; |
551 | tmp++; | 907 | for (i = 0; int_buf[i]; i++) { |
908 | c = int_buf[i]; | ||
909 | if (c == '\'' || c == '"') { | ||
910 | if (c2 == 0) | ||
911 | c2 = c; | ||
912 | else { | ||
913 | if (c == c2) | ||
914 | c2 = 0; | ||
915 | else | ||
916 | int_buf[i] |= QUOT; | ||
917 | } | ||
918 | } else if (c2 != 0 && c != '$') | ||
919 | int_buf[i] |= QUOT; | ||
920 | } | ||
552 | 921 | ||
553 | if(strncmp(tmp, "cd ", 3)==0) | 922 | /* skip commands with arguments if line have commands delimiters */ |
554 | find_type = FIND_DIR_ONLY; | 923 | /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */ |
555 | else if(strchr(tmp, ' ')==NULL) | 924 | for (i = 0; int_buf[i]; i++) { |
556 | find_type = FIND_EXE_ONLY; | 925 | c = int_buf[i]; |
926 | c2 = int_buf[i + 1]; | ||
927 | j = i ? int_buf[i - 1] : -1; | ||
928 | command_mode = 0; | ||
929 | if (c == ';' || c == '&' || c == '|') { | ||
930 | command_mode = 1 + (c == c2); | ||
931 | if (c == '&') { | ||
932 | if (j == '>' || j == '<') | ||
933 | command_mode = 0; | ||
934 | } else if (c == '|' && j == '>') | ||
935 | command_mode = 0; | ||
936 | } | ||
937 | if (command_mode) { | ||
938 | collapse_pos(0, i + command_mode); | ||
939 | i = -1; /* hack incremet */ | ||
940 | } | ||
941 | } | ||
942 | /* collapse `command...` */ | ||
943 | for (i = 0; int_buf[i]; i++) | ||
944 | if (int_buf[i] == '`') { | ||
945 | for (j = i + 1; int_buf[j]; j++) | ||
946 | if (int_buf[j] == '`') { | ||
947 | collapse_pos(i, j + 1); | ||
948 | j = 0; | ||
949 | break; | ||
950 | } | ||
951 | if (j) { | ||
952 | /* not found close ` - command mode, collapse all previous */ | ||
953 | collapse_pos(0, i + 1); | ||
954 | break; | ||
955 | } else | ||
956 | i--; /* hack incremet */ | ||
957 | } | ||
557 | 958 | ||
558 | /* find begin curent word */ | 959 | /* collapse (command...(command...)...) or {command...{command...}...} */ |
559 | if( (tmp1=strrchr(tmp, ' ')) != NULL) { | 960 | c = 0; /* "recursive" level */ |
560 | tmp = ++tmp1; | 961 | c2 = 0; |
962 | for (i = 0; int_buf[i]; i++) | ||
963 | if (int_buf[i] == '(' || int_buf[i] == '{') { | ||
964 | if (int_buf[i] == '(') | ||
965 | c++; | ||
966 | else | ||
967 | c2++; | ||
968 | collapse_pos(0, i + 1); | ||
969 | i = -1; /* hack incremet */ | ||
970 | } | ||
971 | for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) | ||
972 | if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) { | ||
973 | if (int_buf[i] == ')') | ||
974 | c--; | ||
975 | else | ||
976 | c2--; | ||
977 | collapse_pos(0, i + 1); | ||
978 | i = -1; /* hack incremet */ | ||
561 | } | 979 | } |
562 | strcpy(matchBuf, tmp); | ||
563 | 980 | ||
564 | /* Free up any memory already allocated */ | 981 | /* skip first not quote space */ |
982 | for (i = 0; int_buf[i]; i++) | ||
983 | if (int_buf[i] != ' ') | ||
984 | break; | ||
985 | if (i) | ||
986 | collapse_pos(0, i); | ||
987 | |||
988 | /* set find mode for completion */ | ||
989 | command_mode = FIND_EXE_ONLY; | ||
990 | for (i = 0; int_buf[i]; i++) | ||
991 | if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { | ||
992 | if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY | ||
993 | && strncmp(&matchBuf[pos_buf[0]], "cd", 2) == 0) | ||
994 | command_mode = FIND_DIR_ONLY; | ||
995 | else { | ||
996 | command_mode = FIND_FILE_ONLY; | ||
997 | break; | ||
998 | } | ||
999 | } | ||
1000 | /* "strlen" */ | ||
1001 | for (i = 0; int_buf[i]; i++); | ||
1002 | /* find last word */ | ||
1003 | for (--i; i >= 0; i--) { | ||
1004 | c = int_buf[i]; | ||
1005 | if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') { | ||
1006 | collapse_pos(0, i + 1); | ||
1007 | break; | ||
1008 | } | ||
1009 | } | ||
1010 | /* skip first not quoted '\'' or '"' */ | ||
1011 | for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++); | ||
1012 | /* collapse quote or unquote // or /~ */ | ||
1013 | while ((int_buf[i] & ~QUOT) == '/' && ( | ||
1014 | (int_buf[i + 1] & ~QUOT) == '/' | ||
1015 | || (int_buf[i + 1] & ~QUOT) == | ||
1016 | '~')) i++; | ||
1017 | if (i) | ||
1018 | collapse_pos(0, i); | ||
1019 | |||
1020 | /* set only match and destroy quotes */ | ||
1021 | j = 0; | ||
1022 | for (i = 0; pos_buf[i] >= 0; i++) { | ||
1023 | matchBuf[i] = matchBuf[pos_buf[i]]; | ||
1024 | j = pos_buf[i] + 1; | ||
1025 | } | ||
1026 | matchBuf[i] = 0; | ||
1027 | /* old lenght matchBuf with quotes symbols */ | ||
1028 | *len_with_quotes = j ? j - pos_buf[0] : 0; | ||
1029 | |||
1030 | return command_mode; | ||
1031 | } | ||
1032 | |||
1033 | |||
1034 | static void input_tab(int *lastWasTab) | ||
1035 | { | ||
1036 | /* Do TAB completion */ | ||
1037 | static int num_matches; | ||
1038 | static char **matches; | ||
1039 | |||
1040 | if (lastWasTab == 0) { /* free all memory */ | ||
565 | if (matches) { | 1041 | if (matches) { |
566 | while(num_matches>0) | 1042 | while (num_matches > 0) |
567 | free(matches[--num_matches]); | 1043 | free(matches[--num_matches]); |
568 | free(matches); | 1044 | free(matches); |
569 | matches = (char **) NULL; | 1045 | matches = (char **) NULL; |
570 | } | 1046 | } |
1047 | return; | ||
1048 | } | ||
1049 | if (*lastWasTab == FALSE) { | ||
1050 | |||
1051 | char *tmp; | ||
1052 | int len_found; | ||
1053 | char matchBuf[BUFSIZ]; | ||
1054 | int find_type; | ||
1055 | int recalc_pos; | ||
1056 | |||
1057 | *lastWasTab = TRUE; /* flop trigger */ | ||
1058 | |||
1059 | /* Make a local copy of the string -- up | ||
1060 | * to the position of the cursor */ | ||
1061 | tmp = strncpy(matchBuf, command_ps, cursor); | ||
1062 | tmp[cursor] = 0; | ||
1063 | |||
1064 | find_type = find_match(matchBuf, &recalc_pos); | ||
1065 | |||
1066 | /* Free up any memory already allocated */ | ||
1067 | input_tab(0); | ||
571 | 1068 | ||
572 | #ifdef BB_FEATURE_USERNAME_COMPLETION | 1069 | #ifdef BB_FEATURE_USERNAME_COMPLETION |
573 | /* If the word starts with `~' and there is no slash in the word, | 1070 | /* If the word starts with `~' and there is no slash in the word, |
574 | * then try completing this word as a username. */ | 1071 | * then try completing this word as a username. */ |
575 | 1072 | ||
576 | if (matchBuf[0]=='~' && strchr(matchBuf, '/')==0) { | 1073 | if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0) |
577 | matches = username_tab_completion(matchBuf, &num_matches); | 1074 | matches = username_tab_completion(matchBuf, &num_matches); |
578 | } | ||
579 | #endif | 1075 | #endif |
580 | /* Try to match any executable in our path and everything | 1076 | /* Try to match any executable in our path and everything |
581 | * in the current working directory that matches. */ | 1077 | * in the current working directory that matches. */ |
582 | if (!matches) | 1078 | if (!matches) |
583 | matches = exe_n_cwd_tab_completion(matchBuf, &num_matches, find_type); | 1079 | matches = |
1080 | exe_n_cwd_tab_completion(matchBuf, &num_matches, | ||
1081 | find_type); | ||
584 | 1082 | ||
585 | /* Did we find exactly one match? */ | 1083 | /* Did we find exactly one match? */ |
586 | if(!matches || num_matches>1) { | 1084 | if (!matches || num_matches > 1) { |
1085 | char *tmp1; | ||
1086 | |||
587 | beep(); | 1087 | beep(); |
588 | return; | 1088 | if (!matches) |
1089 | return; /* not found */ | ||
1090 | /* sort */ | ||
1091 | qsort(matches, num_matches, sizeof(char *), match_compare); | ||
1092 | |||
1093 | /* find minimal match */ | ||
1094 | tmp = xstrdup(matches[0]); | ||
1095 | for (tmp1 = tmp; *tmp1; tmp1++) | ||
1096 | for (len_found = 1; len_found < num_matches; len_found++) | ||
1097 | if (matches[len_found][(tmp1 - tmp)] != *tmp1) { | ||
1098 | *tmp1 = 0; | ||
1099 | break; | ||
1100 | } | ||
1101 | if (*tmp == 0) { /* have unique */ | ||
1102 | free(tmp); | ||
1103 | return; | ||
1104 | } | ||
1105 | } else { /* one match */ | ||
1106 | tmp = matches[0]; | ||
1107 | /* for next completion current found */ | ||
1108 | *lastWasTab = FALSE; | ||
589 | } | 1109 | } |
590 | 1110 | ||
591 | len_found = strlen(matches[0]); | 1111 | len_found = strlen(tmp); |
592 | |||
593 | /* have space to placed match? */ | 1112 | /* have space to placed match? */ |
594 | if ( (len_found-strlen(matchBuf)+len) < BUFSIZ ) { | 1113 | if ((len_found - strlen(matchBuf) + len) < BUFSIZ) { |
595 | 1114 | ||
596 | int recalc_pos = len; | 1115 | /* before word for match */ |
597 | 1116 | command_ps[cursor - recalc_pos] = 0; | |
598 | /* before word for match */ | 1117 | /* save tail line */ |
599 | command_ps[pos-strlen(matchBuf)]=0; | 1118 | strcpy(matchBuf, command_ps + cursor); |
600 | 1119 | /* add match */ | |
601 | /* tail line */ | 1120 | strcat(command_ps, tmp); |
602 | strcpy(matchBuf, command_ps+pos); | 1121 | /* add tail */ |
603 | |||
604 | /* add match */ | ||
605 | strcat(command_ps, matches[0]); | ||
606 | /* add tail */ | ||
607 | strcat(command_ps, matchBuf); | 1122 | strcat(command_ps, matchBuf); |
608 | 1123 | /* back to begin word for match */ | |
609 | /* write out the matched command */ | 1124 | input_backward(recalc_pos); |
610 | len=strlen(command_ps); | 1125 | /* new pos */ |
611 | recalc_pos = len-recalc_pos+pos; | 1126 | recalc_pos = cursor + len_found; |
612 | input_end(); /* write */ | 1127 | /* new len */ |
613 | while(recalc_pos<cursor) | 1128 | len = strlen(command_ps); |
614 | input_backward(); | 1129 | /* write out the matched command */ |
615 | return; | 1130 | input_end(); |
1131 | input_backward(cursor - recalc_pos); | ||
616 | } | 1132 | } |
1133 | if (tmp != matches[0]) | ||
1134 | free(tmp); | ||
617 | } else { | 1135 | } else { |
618 | /* Ok -- the last char was a TAB. Since they | 1136 | /* Ok -- the last char was a TAB. Since they |
619 | * just hit TAB again, print a list of all the | 1137 | * just hit TAB again, print a list of all the |
620 | * available choices... */ | 1138 | * available choices... */ |
621 | if ( matches && num_matches>0 ) { | 1139 | if (matches && num_matches > 0) { |
622 | int i, col; | 1140 | int i, col, l; |
623 | int sav_cursor = cursor; | 1141 | int sav_cursor = cursor; /* change goto_new_line() */ |
624 | 1142 | ||
625 | /* Go to the next line */ | 1143 | /* Go to the next line */ |
626 | goto_new_line(); | 1144 | goto_new_line(); |
627 | for (i=0,col=0; i<num_matches; i++) { | 1145 | for (i = 0, col = 0; i < num_matches; i++) { |
628 | printf("%s ", matches[i]); | 1146 | l = strlen(matches[i]); |
629 | col += strlen(matches[i])+2; | 1147 | if (l < 14) |
630 | col -= (col/cmdedit_termw)*cmdedit_termw; | 1148 | l = 14; |
631 | if (col > 60 && matches[i+1] != NULL) { | 1149 | printf("%-14s ", matches[i]); |
1150 | if ((l += 2) > 16) | ||
1151 | while (l % 16) { | ||
1152 | putchar(' '); | ||
1153 | l++; | ||
1154 | } | ||
1155 | col += l; | ||
1156 | col -= (col / cmdedit_termw) * cmdedit_termw; | ||
1157 | if (col > 60 && matches[i + 1] != NULL) { | ||
632 | putchar('\n'); | 1158 | putchar('\n'); |
633 | col = 0; | 1159 | col = 0; |
634 | } | 1160 | } |
635 | } | 1161 | } |
636 | /* Go to the next line and rewrite the prompt */ | 1162 | /* Go to the next line and rewrite */ |
637 | printf("\n%s", cmdedit_prompt); | 1163 | putchar('\n'); |
638 | cmdedit_x = cmdedit_prmt_len; | 1164 | redraw(0, len - sav_cursor); |
639 | cmdedit_y = 0; | ||
640 | cursor = 0; | ||
641 | input_end(); /* Rewrite the command */ | ||
642 | /* Put the cursor back to where it used to be */ | ||
643 | while (sav_cursor < cursor) | ||
644 | input_backward(); | ||
645 | } | 1165 | } |
646 | } | 1166 | } |
647 | } | 1167 | } |
648 | #endif | 1168 | #endif /* BB_FEATURE_SH_TAB_COMPLETION */ |
649 | 1169 | ||
650 | static void get_previous_history(struct history **hp, char* command) | 1170 | static void get_previous_history(struct history **hp, struct history *p) |
651 | { | 1171 | { |
652 | if ((*hp)->s) | 1172 | if ((*hp)->s) |
653 | free((*hp)->s); | 1173 | free((*hp)->s); |
654 | (*hp)->s = strdup(command); | 1174 | (*hp)->s = xstrdup(command_ps); |
655 | *hp = (*hp)->p; | 1175 | *hp = p; |
656 | } | 1176 | } |
657 | 1177 | ||
658 | static void get_next_history(struct history **hp, char* command) | 1178 | static inline void get_next_history(struct history **hp) |
659 | { | 1179 | { |
660 | if ((*hp)->s) | 1180 | get_previous_history(hp, (*hp)->n); |
661 | free((*hp)->s); | ||
662 | (*hp)->s = strdup(command); | ||
663 | *hp = (*hp)->n; | ||
664 | } | 1181 | } |
665 | 1182 | ||
1183 | enum { | ||
1184 | ESC = 27, | ||
1185 | DEL = 127, | ||
1186 | }; | ||
1187 | |||
1188 | |||
666 | /* | 1189 | /* |
667 | * This function is used to grab a character buffer | 1190 | * This function is used to grab a character buffer |
668 | * from the input file descriptor and allows you to | 1191 | * from the input file descriptor and allows you to |
@@ -678,95 +1201,78 @@ static void get_next_history(struct history **hp, char* command) | |||
678 | * | 1201 | * |
679 | * Furthermore, the "vi" command editing keys are not implemented. | 1202 | * Furthermore, the "vi" command editing keys are not implemented. |
680 | * | 1203 | * |
681 | * TODO: implement TAB command completion. :) | ||
682 | */ | 1204 | */ |
683 | extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | 1205 | extern void cmdedit_read_input(char *prompt, char command[BUFSIZ]) |
684 | { | 1206 | { |
685 | 1207 | ||
686 | int inputFd=fileno(stdin); | 1208 | int inputFd = fileno(stdin); |
687 | 1209 | ||
688 | int j = 0; | ||
689 | int break_out = 0; | 1210 | int break_out = 0; |
690 | int ret = 0; | ||
691 | int lastWasTab = FALSE; | 1211 | int lastWasTab = FALSE; |
692 | char c = 0; | 1212 | char c = 0; |
693 | struct history *hp = his_end; | 1213 | struct history *hp = his_end; |
694 | 1214 | ||
1215 | /* prepare before init handlers */ | ||
1216 | cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */ | ||
695 | len = 0; | 1217 | len = 0; |
696 | cursor = 0; | ||
697 | command_ps = command; | 1218 | command_ps = command; |
698 | 1219 | ||
699 | if (new_settings.c_cc[VMIN]==0) { | 1220 | if (new_settings.c_cc[VMIN] == 0) { /* first call */ |
700 | 1221 | ||
701 | getTermSettings(inputFd, (void*) &initial_settings); | 1222 | getTermSettings(inputFd, (void *) &initial_settings); |
702 | memcpy(&new_settings, &initial_settings, sizeof(struct termios)); | 1223 | memcpy(&new_settings, &initial_settings, sizeof(struct termios)); |
1224 | |||
703 | new_settings.c_cc[VMIN] = 1; | 1225 | new_settings.c_cc[VMIN] = 1; |
704 | new_settings.c_cc[VTIME] = 0; | 1226 | new_settings.c_cc[VTIME] = 0; |
705 | new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */ | 1227 | new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */ |
706 | new_settings.c_lflag &= ~ICANON; /* unbuffered input */ | 1228 | new_settings.c_lflag &= ~ICANON; /* unbuffered input */ |
707 | new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */ | 1229 | new_settings.c_lflag &= ~(ECHO | ECHOCTL | ECHONL); /* Turn off echoing */ |
708 | } | 1230 | } |
709 | setTermSettings(inputFd, (void*) &new_settings); | ||
710 | handlers_sets |= SET_RESET_TERM; | ||
711 | 1231 | ||
712 | memset(command, 0, BUFSIZ); | 1232 | command[0] = 0; |
713 | 1233 | ||
714 | cmdedit_init(); | 1234 | setTermSettings(inputFd, (void *) &new_settings); |
1235 | handlers_sets |= SET_RESET_TERM; | ||
715 | 1236 | ||
1237 | cmdedit_init(); | ||
716 | /* Print out the command prompt */ | 1238 | /* Print out the command prompt */ |
717 | cmdedit_prompt = prompt; | 1239 | parse_prompt(prompt); |
718 | cmdedit_prmt_len = strlen(prompt); | ||
719 | printf("%s", prompt); | ||
720 | cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */ | ||
721 | cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */ | ||
722 | |||
723 | 1240 | ||
724 | while (1) { | 1241 | while (1) { |
725 | 1242 | ||
726 | fflush(stdout); /* buffered out to fast */ | 1243 | fflush(stdout); /* buffered out to fast */ |
727 | 1244 | ||
728 | if ((ret = read(inputFd, &c, 1)) < 1) | 1245 | if (read(inputFd, &c, 1) < 1) |
729 | return; | 1246 | return; |
730 | //fprintf(stderr, "got a '%c' (%d)\n", c, c); | ||
731 | 1247 | ||
732 | switch (c) { | 1248 | switch (c) { |
733 | case '\n': | 1249 | case '\n': |
734 | case '\r': | 1250 | case '\r': |
735 | /* Enter */ | 1251 | /* Enter */ |
736 | *(command + len) = c; | 1252 | goto_new_line(); |
737 | len++; | ||
738 | input_end (); | ||
739 | break_out = 1; | 1253 | break_out = 1; |
740 | break; | 1254 | break; |
741 | case 1: | 1255 | case 1: |
742 | /* Control-a -- Beginning of line */ | 1256 | /* Control-a -- Beginning of line */ |
743 | input_home(); | 1257 | input_backward(cursor); |
744 | break; | 1258 | break; |
745 | case 2: | 1259 | case 2: |
746 | /* Control-b -- Move back one character */ | 1260 | /* Control-b -- Move back one character */ |
747 | input_backward(); | 1261 | input_backward(1); |
748 | break; | 1262 | break; |
749 | case 3: | 1263 | case 3: |
750 | /* Control-c -- stop gathering input */ | 1264 | /* Control-c -- stop gathering input */ |
751 | 1265 | ||
752 | /* Link into lash to reset context to 0 on ^C and such */ | 1266 | /* Link into lash to reset context to 0 on ^C and such */ |
753 | shell_context = 0; | 1267 | shell_context = 0; |
754 | 1268 | ||
755 | /* Go to the next line */ | 1269 | /* Go to the next line */ |
756 | goto_new_line(); | 1270 | goto_new_line(); |
1271 | command[0] = 0; | ||
757 | 1272 | ||
758 | #if 0 | ||
759 | /* Rewrite the prompt */ | ||
760 | printf("%s", prompt); | ||
761 | |||
762 | /* Reset the command string */ | ||
763 | memset(command, 0, BUFSIZ); | ||
764 | len = cursor = 0; | ||
765 | #endif | ||
766 | return; | 1273 | return; |
767 | |||
768 | case 4: | 1274 | case 4: |
769 | /* Control-d -- Delete one character, or exit | 1275 | /* Control-d -- Delete one character, or exit |
770 | * if the len=0 and no chars to delete */ | 1276 | * if the len=0 and no chars to delete */ |
771 | if (len == 0) { | 1277 | if (len == 0) { |
772 | printf("exit"); | 1278 | printf("exit"); |
@@ -790,13 +1296,13 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
790 | break; | 1296 | break; |
791 | case '\t': | 1297 | case '\t': |
792 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | 1298 | #ifdef BB_FEATURE_SH_TAB_COMPLETION |
793 | input_tab(lastWasTab); | 1299 | input_tab(&lastWasTab); |
794 | #endif | 1300 | #endif |
795 | break; | 1301 | break; |
796 | case 14: | 1302 | case 14: |
797 | /* Control-n -- Get next command in history */ | 1303 | /* Control-n -- Get next command in history */ |
798 | if (hp && hp->n && hp->n->s) { | 1304 | if (hp && hp->n && hp->n->s) { |
799 | get_next_history(&hp, command); | 1305 | get_next_history(&hp); |
800 | goto rewrite_line; | 1306 | goto rewrite_line; |
801 | } else { | 1307 | } else { |
802 | beep(); | 1308 | beep(); |
@@ -805,114 +1311,111 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
805 | case 16: | 1311 | case 16: |
806 | /* Control-p -- Get previous command from history */ | 1312 | /* Control-p -- Get previous command from history */ |
807 | if (hp && hp->p) { | 1313 | if (hp && hp->p) { |
808 | get_previous_history(&hp, command); | 1314 | get_previous_history(&hp, hp->p); |
809 | goto rewrite_line; | 1315 | goto rewrite_line; |
810 | } else { | 1316 | } else { |
811 | beep(); | 1317 | beep(); |
812 | } | 1318 | } |
813 | break; | 1319 | break; |
1320 | case 21: | ||
1321 | /* Control-U -- Clear line before cursor */ | ||
1322 | if (cursor) { | ||
1323 | strcpy(command, command + cursor); | ||
1324 | redraw(cmdedit_y, len -= cursor); | ||
1325 | } | ||
1326 | break; | ||
1327 | |||
814 | case ESC:{ | 1328 | case ESC:{ |
815 | /* escape sequence follows */ | 1329 | /* escape sequence follows */ |
816 | if ((ret = read(inputFd, &c, 1)) < 1) | 1330 | if (read(inputFd, &c, 1) < 1) |
1331 | return; | ||
1332 | /* different vt100 emulations */ | ||
1333 | if (c == '[' || c == 'O') { | ||
1334 | if (read(inputFd, &c, 1) < 1) | ||
817 | return; | 1335 | return; |
1336 | } | ||
1337 | switch (c) { | ||
1338 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | ||
1339 | case '\t': /* Alt-Tab */ | ||
818 | 1340 | ||
819 | if (c == '[') { /* 91 */ | 1341 | input_tab(&lastWasTab); |
820 | if ((ret = read(inputFd, &c, 1)) < 1) | 1342 | break; |
821 | return; | 1343 | #endif |
822 | 1344 | case 'A': | |
823 | switch (c) { | 1345 | /* Up Arrow -- Get previous command from history */ |
824 | case 'A': | 1346 | if (hp && hp->p) { |
825 | /* Up Arrow -- Get previous command from history */ | 1347 | get_previous_history(&hp, hp->p); |
826 | if (hp && hp->p) { | 1348 | goto rewrite_line; |
827 | get_previous_history(&hp, command); | 1349 | } else { |
828 | goto rewrite_line; | 1350 | beep(); |
829 | } else { | ||
830 | beep(); | ||
831 | } | ||
832 | break; | ||
833 | case 'B': | ||
834 | /* Down Arrow -- Get next command in history */ | ||
835 | if (hp && hp->n && hp->n->s) { | ||
836 | get_next_history(&hp, command); | ||
837 | goto rewrite_line; | ||
838 | } else { | ||
839 | beep(); | ||
840 | } | ||
841 | break; | ||
842 | |||
843 | /* Rewrite the line with the selected history item */ | ||
844 | rewrite_line: | ||
845 | /* return to begin of line */ | ||
846 | input_home (); | ||
847 | /* for next memmoves without set '\0' */ | ||
848 | memset (command, 0, BUFSIZ); | ||
849 | /* change command */ | ||
850 | strcpy (command, hp->s); | ||
851 | /* write new command */ | ||
852 | for (j=0; command[j]; j++) | ||
853 | cmdedit_set_out_char(command[j], 0); | ||
854 | ret = cursor; | ||
855 | /* erase tail if required */ | ||
856 | for (j = ret; j < len; j++) | ||
857 | cmdedit_set_out_char(' ', 0); | ||
858 | /* and backward cursor */ | ||
859 | for (j = ret; j < len; j++) | ||
860 | input_backward(); | ||
861 | len = cursor; /* set new len */ | ||
862 | break; | ||
863 | case 'C': | ||
864 | /* Right Arrow -- Move forward one character */ | ||
865 | input_forward(); | ||
866 | break; | ||
867 | case 'D': | ||
868 | /* Left Arrow -- Move back one character */ | ||
869 | input_backward(); | ||
870 | break; | ||
871 | case '3': | ||
872 | /* Delete */ | ||
873 | input_delete(); | ||
874 | break; | ||
875 | case '1': | ||
876 | /* Home (Ctrl-A) */ | ||
877 | input_home(); | ||
878 | break; | ||
879 | case '4': | ||
880 | /* End (Ctrl-E) */ | ||
881 | input_end(); | ||
882 | break; | ||
883 | default: | ||
884 | beep(); | ||
885 | } | ||
886 | if (c == '1' || c == '3' || c == '4') | ||
887 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
888 | return; /* read 126 (~) */ | ||
889 | } | 1351 | } |
890 | if (c == 'O') { | 1352 | break; |
891 | /* 79 */ | 1353 | case 'B': |
892 | if ((ret = read(inputFd, &c, 1)) < 1) | 1354 | /* Down Arrow -- Get next command in history */ |
893 | return; | 1355 | if (hp && hp->n && hp->n->s) { |
894 | switch (c) { | 1356 | get_next_history(&hp); |
895 | case 'H': | 1357 | goto rewrite_line; |
896 | /* Home (xterm) */ | 1358 | } else { |
897 | input_home(); | 1359 | beep(); |
898 | break; | ||
899 | case 'F': | ||
900 | /* End (xterm) */ | ||
901 | input_end(); | ||
902 | break; | ||
903 | default: | ||
904 | beep(); | ||
905 | } | ||
906 | } | 1360 | } |
907 | c = 0; | ||
908 | break; | 1361 | break; |
1362 | |||
1363 | /* Rewrite the line with the selected history item */ | ||
1364 | rewrite_line: | ||
1365 | /* change command */ | ||
1366 | len = strlen(strcpy(command, hp->s)); | ||
1367 | /* redraw and go to end line */ | ||
1368 | redraw(cmdedit_y, 0); | ||
1369 | break; | ||
1370 | case 'C': | ||
1371 | /* Right Arrow -- Move forward one character */ | ||
1372 | input_forward(); | ||
1373 | break; | ||
1374 | case 'D': | ||
1375 | /* Left Arrow -- Move back one character */ | ||
1376 | input_backward(1); | ||
1377 | break; | ||
1378 | case '3': | ||
1379 | /* Delete */ | ||
1380 | input_delete(); | ||
1381 | break; | ||
1382 | case '1': | ||
1383 | case 'H': | ||
1384 | /* Home (Ctrl-A) */ | ||
1385 | input_backward(cursor); | ||
1386 | break; | ||
1387 | case '4': | ||
1388 | case 'F': | ||
1389 | /* End (Ctrl-E) */ | ||
1390 | input_end(); | ||
1391 | break; | ||
1392 | default: | ||
1393 | if (!(c >= '1' && c <= '9')) | ||
1394 | c = 0; | ||
1395 | beep(); | ||
909 | } | 1396 | } |
1397 | if (c >= '1' && c <= '9') | ||
1398 | do | ||
1399 | if (read(inputFd, &c, 1) < 1) | ||
1400 | return; | ||
1401 | while (c != '~'); | ||
1402 | break; | ||
1403 | } | ||
910 | 1404 | ||
911 | default: /* If it's regular input, do the normal thing */ | 1405 | default: /* If it's regular input, do the normal thing */ |
912 | 1406 | #ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT | |
913 | if (!isprint(c)) { /* Skip non-printable characters */ | 1407 | /* Control-V -- Add non-printable symbol */ |
1408 | if (c == 22) { | ||
1409 | if (read(inputFd, &c, 1) < 1) | ||
1410 | return; | ||
1411 | if (c == 0) { | ||
1412 | beep(); | ||
1413 | break; | ||
1414 | } | ||
1415 | } else | ||
1416 | #endif | ||
1417 | if (!isprint(c)) /* Skip non-printable characters */ | ||
914 | break; | 1418 | break; |
915 | } | ||
916 | 1419 | ||
917 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ | 1420 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ |
918 | break; | 1421 | break; |
@@ -921,45 +1424,41 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
921 | 1424 | ||
922 | if (cursor == (len - 1)) { /* Append if at the end of the line */ | 1425 | if (cursor == (len - 1)) { /* Append if at the end of the line */ |
923 | *(command + cursor) = c; | 1426 | *(command + cursor) = c; |
924 | cmdedit_set_out_char(c, command[cursor+1]); | 1427 | *(command + cursor + 1) = 0; |
1428 | cmdedit_set_out_char(0); | ||
925 | } else { /* Insert otherwise */ | 1429 | } else { /* Insert otherwise */ |
926 | memmove(command + cursor + 1, command + cursor, | 1430 | int sc = cursor; |
927 | len - cursor - 1); | ||
928 | 1431 | ||
929 | *(command + cursor) = c; | 1432 | memmove(command + sc + 1, command + sc, len - sc); |
930 | j = cursor+1; | 1433 | *(command + sc) = c; |
1434 | sc++; | ||
931 | /* rewrite from cursor */ | 1435 | /* rewrite from cursor */ |
932 | input_end (); | 1436 | input_end(); |
933 | /* to prev x pos + 1 */ | 1437 | /* to prev x pos + 1 */ |
934 | while(cursor > j) | 1438 | input_backward(cursor - sc); |
935 | input_backward(); | ||
936 | } | 1439 | } |
937 | 1440 | ||
938 | break; | 1441 | break; |
939 | } | 1442 | } |
940 | if (c == '\t') | ||
941 | lastWasTab = TRUE; | ||
942 | else | ||
943 | lastWasTab = FALSE; | ||
944 | |||
945 | if (break_out) /* Enter is the command terminator, no more input. */ | 1443 | if (break_out) /* Enter is the command terminator, no more input. */ |
946 | break; | 1444 | break; |
1445 | |||
1446 | if (c != '\t') | ||
1447 | lastWasTab = FALSE; | ||
947 | } | 1448 | } |
948 | 1449 | ||
949 | setTermSettings (inputFd, (void *) &initial_settings); | 1450 | setTermSettings(inputFd, (void *) &initial_settings); |
950 | handlers_sets &= ~SET_RESET_TERM; | 1451 | handlers_sets &= ~SET_RESET_TERM; |
951 | 1452 | ||
952 | /* Handle command history log */ | 1453 | /* Handle command history log */ |
953 | if (len>1) { /* no put empty line (only '\n') */ | 1454 | if (len) { /* no put empty line */ |
954 | 1455 | ||
955 | struct history *h = his_end; | 1456 | struct history *h = his_end; |
956 | char *ss; | 1457 | char *ss; |
957 | 1458 | ||
958 | command[len-1] = 0; /* destroy end '\n' */ | 1459 | ss = xstrdup(command); /* duplicate */ |
959 | ss = strdup(command); /* duplicate without '\n' */ | ||
960 | command[len-1] = '\n'; /* restore '\n' */ | ||
961 | 1460 | ||
962 | if (!h) { | 1461 | if (h == 0) { |
963 | /* No previous history -- this memory is never freed */ | 1462 | /* No previous history -- this memory is never freed */ |
964 | h = his_front = xmalloc(sizeof(struct history)); | 1463 | h = his_front = xmalloc(sizeof(struct history)); |
965 | h->n = xmalloc(sizeof(struct history)); | 1464 | h->n = xmalloc(sizeof(struct history)); |
@@ -994,8 +1493,18 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
994 | history_counter++; | 1493 | history_counter++; |
995 | } | 1494 | } |
996 | } | 1495 | } |
1496 | #if defined(BB_FEATURE_BASH_STYLE_PROMT) | ||
1497 | num_ok_lines++; | ||
1498 | #endif | ||
997 | } | 1499 | } |
998 | 1500 | command[len++] = '\n'; /* set '\n' */ | |
1501 | command[len] = 0; | ||
1502 | #if defined(BB_FEATURE_CLEAN_UP) && defined(BB_FEATURE_SH_TAB_COMPLETION) | ||
1503 | input_tab(0); /* strong free */ | ||
1504 | #endif | ||
1505 | #if defined(BB_FEATURE_BASH_STYLE_PROMT) | ||
1506 | free(cmdedit_prompt); | ||
1507 | #endif | ||
999 | return; | 1508 | return; |
1000 | } | 1509 | } |
1001 | 1510 | ||
@@ -1004,7 +1513,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
1004 | extern void cmdedit_terminate(void) | 1513 | extern void cmdedit_terminate(void) |
1005 | { | 1514 | { |
1006 | cmdedit_reset_term(); | 1515 | cmdedit_reset_term(); |
1007 | if((handlers_sets & SET_TERM_HANDLERS)!=0) { | 1516 | if ((handlers_sets & SET_TERM_HANDLERS) != 0) { |
1008 | signal(SIGKILL, SIG_DFL); | 1517 | signal(SIGKILL, SIG_DFL); |
1009 | signal(SIGINT, SIG_DFL); | 1518 | signal(SIGINT, SIG_DFL); |
1010 | signal(SIGQUIT, SIG_DFL); | 1519 | signal(SIGQUIT, SIG_DFL); |
@@ -1014,6 +1523,32 @@ extern void cmdedit_terminate(void) | |||
1014 | } | 1523 | } |
1015 | } | 1524 | } |
1016 | 1525 | ||
1526 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ | ||
1527 | |||
1528 | |||
1529 | #ifdef TEST | ||
1530 | |||
1531 | unsigned int shell_context; | ||
1017 | 1532 | ||
1533 | int main(int argc, char **argv) | ||
1534 | { | ||
1535 | char buff[BUFSIZ]; | ||
1536 | char *prompt = | ||
1537 | #if defined(BB_FEATURE_BASH_STYLE_PROMT) | ||
1538 | "\\[\\033[32;1m\\]\\u@\\[\\033[33;1m\\]\\h:\ | ||
1539 | \\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \ | ||
1540 | \\!\\[\\033[36;1m\\]\\$ \\[\\033[0m\\]"; | ||
1541 | #else | ||
1542 | "% "; | ||
1543 | #endif | ||
1544 | |||
1545 | shell_context = 1; | ||
1546 | do { | ||
1547 | cmdedit_read_input(prompt, buff); | ||
1548 | printf("*** cmdedit_read_input() returned line =%s=\n", buff); | ||
1549 | } while (shell_context); | ||
1550 | printf("*** cmdedit_read_input() detect ^C\n"); | ||
1551 | return 0; | ||
1552 | } | ||
1018 | 1553 | ||
1019 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ | 1554 | #endif /* TEST */ |
diff --git a/shell/cmdedit.c b/shell/cmdedit.c index 2e57b9a34..6a53c12f6 100644 --- a/shell/cmdedit.c +++ b/shell/cmdedit.c | |||
@@ -1,21 +1,18 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | 1 | /* vi: set sw=4 ts=4: */ |
2 | /* | 2 | /* |
3 | * Termios command line History and Editting, originally | 3 | * Termios command line History and Editting. |
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 | * | 4 | * |
11 | * You may use this code as you wish, so long as the original author(s) | 5 | * Copyright (c) 1986-2001 may safely be consumed by a BSD or GPL license. |
12 | * are attributed in any redistributions of the source code. | 6 | * Written by: Vladimir Oleynik <vodz@usa.net> |
13 | * This code is 'as is' with no warranty. | 7 | * |
14 | * This code may safely be consumed by a BSD or GPL license. | 8 | * Used ideas: |
9 | * Adam Rogoyski <rogoyski@cs.utexas.edu> | ||
10 | * Dave Cinege <dcinege@psychosis.com> | ||
11 | * Jakub Jelinek (c) 1995 | ||
12 | * Erik Andersen <andersee@debian.org> (Majorly adjusted for busybox) | ||
15 | * | 13 | * |
16 | * v 0.5 19990328 Initial release | 14 | * This code is 'as is' with no warranty. |
17 | * | 15 | * |
18 | * Future plans: Simple file and path name completion. (like BASH) | ||
19 | * | 16 | * |
20 | */ | 17 | */ |
21 | 18 | ||
@@ -27,20 +24,46 @@ | |||
27 | to work in an Xterm and console. Ctrl-A also works as Home. | 24 | to work in an Xterm and console. Ctrl-A also works as Home. |
28 | Ctrl-E also works as End. | 25 | Ctrl-E also works as End. |
29 | 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 | */ | ||
30 | 31 | ||
31 | Editor with vertical scrolling and completion by | ||
32 | Vladimir Oleynik. vodz@usa.net (c) 2001 | ||
33 | 32 | ||
34 | Small bug: not true work if terminal size (x*y symbols) less | 33 | #define TEST |
35 | size (prompt + editor`s line + 2 symbols) | ||
36 | */ | ||
37 | 34 | ||
38 | 35 | ||
36 | #ifndef TEST | ||
39 | 37 | ||
40 | #include "busybox.h" | 38 | #include "busybox.h" |
41 | 39 | ||
40 | #define D(x) | ||
41 | |||
42 | #else | ||
43 | |||
44 | #define BB_FEATURE_SH_COMMAND_EDITING | ||
45 | #define BB_FEATURE_SH_TAB_COMPLETION | ||
46 | #define BB_FEATURE_USERNAME_COMPLETION | ||
47 | #define BB_FEATURE_NONPRINTABLE_INVERSE_PUT | ||
48 | #define BB_FEATURE_BASH_STYLE_PROMT | ||
49 | #define BB_FEATURE_CLEAN_UP | ||
50 | |||
51 | #define TRUE 1 | ||
52 | #define FALSE 0 | ||
53 | #define D(x) x | ||
54 | |||
55 | #endif /* TEST */ | ||
56 | |||
42 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | 57 | #ifdef BB_FEATURE_SH_COMMAND_EDITING |
43 | 58 | ||
59 | #ifndef BB_FEATURE_SH_TAB_COMPLETION | ||
60 | #undef BB_FEATURE_USERNAME_COMPLETION | ||
61 | #endif | ||
62 | |||
63 | #if defined(BB_FEATURE_USERNAME_COMPLETION) || defined(BB_FEATURE_BASH_STYLE_PROMT) | ||
64 | #define BB_FEATURE_GETUSERNAME_AND_HOMEDIR | ||
65 | #endif | ||
66 | |||
44 | #include <stdio.h> | 67 | #include <stdio.h> |
45 | #include <errno.h> | 68 | #include <errno.h> |
46 | #include <unistd.h> | 69 | #include <unistd.h> |
@@ -49,26 +72,61 @@ | |||
49 | #include <sys/ioctl.h> | 72 | #include <sys/ioctl.h> |
50 | #include <ctype.h> | 73 | #include <ctype.h> |
51 | #include <signal.h> | 74 | #include <signal.h> |
75 | #include <limits.h> | ||
52 | 76 | ||
53 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | 77 | #ifdef BB_FEATURE_SH_TAB_COMPLETION |
78 | #include <dirent.h> | ||
54 | #include <sys/stat.h> | 79 | #include <sys/stat.h> |
55 | #endif | 80 | #endif |
56 | 81 | ||
82 | #ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR | ||
83 | #ifndef TEST | ||
57 | #include "pwd_grp/pwd.h" | 84 | #include "pwd_grp/pwd.h" |
85 | #else | ||
86 | #include <pwd.h> | ||
87 | #endif /* TEST */ | ||
88 | #endif /* advanced FEATURES */ | ||
58 | 89 | ||
59 | 90 | ||
60 | static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */ | 91 | #ifdef TEST |
92 | void *xrealloc(void *old, size_t size) | ||
93 | { | ||
94 | return realloc(old, size); | ||
95 | } | ||
61 | 96 | ||
62 | enum { | 97 | void *xmalloc(size_t size) |
63 | ESC = 27, | 98 | { |
64 | DEL = 127, | 99 | return malloc(size); |
100 | } | ||
101 | char *xstrdup(const char *s) | ||
102 | { | ||
103 | return strdup(s); | ||
104 | } | ||
105 | |||
106 | void *xcalloc(size_t size, size_t se) | ||
107 | { | ||
108 | return calloc(size, se); | ||
109 | } | ||
110 | |||
111 | #define error_msg(s, d) fprintf(stderr, s, d) | ||
112 | #endif | ||
113 | |||
114 | |||
115 | struct history { | ||
116 | char *s; | ||
117 | struct history *p; | ||
118 | struct history *n; | ||
65 | }; | 119 | }; |
66 | 120 | ||
67 | #define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0) | 121 | /* Maximum length of the linked list for the command line history */ |
68 | #define whitespace(c) (((c) == ' ') || ((c) == '\t')) | 122 | static const int MAX_HISTORY = 15; |
123 | |||
124 | /* First element in command line list */ | ||
125 | static struct history *his_front = NULL; | ||
126 | |||
127 | /* Last element in command line list */ | ||
128 | static struct history *his_end = NULL; | ||
69 | 129 | ||
70 | static struct history *his_front = NULL; /* First element in command line list */ | ||
71 | static struct history *his_end = NULL; /* Last element in command line list */ | ||
72 | 130 | ||
73 | /* ED: sparc termios is broken: revert back to old termio handling. */ | 131 | /* ED: sparc termios is broken: revert back to old termio handling. */ |
74 | 132 | ||
@@ -87,87 +145,100 @@ static struct history *his_end = NULL; /* Last element in command line list */ | |||
87 | static struct termios initial_settings, new_settings; | 145 | static struct termios initial_settings, new_settings; |
88 | 146 | ||
89 | 147 | ||
90 | #ifndef _POSIX_VDISABLE | 148 | #ifndef _POSIX_VDISABLE |
91 | #define _POSIX_VDISABLE '\0' | 149 | #define _POSIX_VDISABLE '\0' |
92 | #endif | 150 | #endif |
93 | 151 | ||
94 | 152 | ||
95 | static | 153 | static |
96 | volatile int cmdedit_termw; /* actual terminal width */ | 154 | volatile int cmdedit_termw = 80; /* actual terminal width */ |
97 | static int history_counter = 0; /* Number of commands in history list */ | 155 | static int history_counter = 0; /* Number of commands in history list */ |
98 | |||
99 | static | 156 | static |
100 | volatile int handlers_sets = 0; /* Set next bites | 157 | volatile int handlers_sets = 0; /* Set next bites: */ |
101 | when atexit() has been called | 158 | |
102 | and set many "terminates" signal handlers | ||
103 | and winchg signal handler | ||
104 | and if the terminal needs to be reset upon exit | ||
105 | */ | ||
106 | enum { | 159 | enum { |
107 | SET_ATEXIT = 1, | 160 | SET_ATEXIT = 1, /* when atexit() has been called and |
108 | SET_TERM_HANDLERS = 2, | 161 | get euid,uid,gid to fast compare */ |
109 | SET_WCHG_HANDLERS = 4, | 162 | SET_TERM_HANDLERS = 2, /* set many terminates signal handlers */ |
110 | SET_RESET_TERM = 8, | 163 | SET_WCHG_HANDLERS = 4, /* winchg signal handler */ |
164 | SET_RESET_TERM = 8, /* if the terminal needs to be reset upon exit */ | ||
111 | }; | 165 | }; |
112 | 166 | ||
113 | |||
114 | static int cmdedit_x; /* real x terminal position, | ||
115 | require put prompt in start x position */ | ||
116 | static int cmdedit_y; /* pseudoreal y terminal position */ | ||
117 | static int cmdedit_prmt_len; /* for fast running, without duplicate calculate */ | ||
118 | 167 | ||
119 | static int cursor; /* required global for signal handler */ | 168 | static int cmdedit_x; /* real x terminal position */ |
120 | static int len; /* --- "" - - "" - -"- --""-- --""--- */ | 169 | static int cmdedit_y; /* pseudoreal y terminal position */ |
121 | static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */ | 170 | static int cmdedit_prmt_len; /* lenght prompt without colores string */ |
122 | static const char *cmdedit_prompt;/* --- "" - - "" - -"- --""-- --""--- */ | ||
123 | 171 | ||
124 | /* Link into lash to reset context to 0 | 172 | static int cursor; /* required global for signal handler */ |
125 | * on ^C and such */ | 173 | static int len; /* --- "" - - "" - -"- --""-- --""--- */ |
174 | static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */ | ||
175 | static | ||
176 | #ifndef BB_FEATURE_BASH_STYLE_PROMT | ||
177 | const | ||
178 | #endif | ||
179 | char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */ | ||
180 | |||
181 | /* Link into lash to reset context to 0 on ^C and such */ | ||
126 | extern unsigned int shell_context; | 182 | extern unsigned int shell_context; |
127 | 183 | ||
128 | 184 | ||
129 | struct history { | 185 | #ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR |
130 | char *s; | 186 | static char *user_buf = ""; |
131 | struct history *p; | 187 | static char *home_pwd_buf = ""; |
132 | struct history *n; | 188 | static int my_euid; |
133 | }; | 189 | #endif |
190 | |||
191 | #ifdef BB_FEATURE_BASH_STYLE_PROMT | ||
192 | static char *hostname_buf = ""; | ||
193 | static int num_ok_lines = 1; | ||
194 | #endif | ||
195 | |||
196 | |||
197 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | ||
198 | |||
199 | #ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIR | ||
200 | static int my_euid; | ||
201 | #endif | ||
202 | |||
203 | static int my_uid; | ||
204 | static int my_gid; | ||
205 | |||
206 | #endif /* BB_FEATURE_SH_TAB_COMPLETION */ | ||
207 | |||
134 | 208 | ||
135 | static void cmdedit_setwidth(int w, int redraw_flg); | 209 | static void cmdedit_setwidth(int w, int redraw_flg); |
136 | 210 | ||
137 | static void win_changed(int nsig) | 211 | static void win_changed(int nsig) |
138 | { | 212 | { |
139 | struct winsize win = { 0, 0, 0, 0 }; | 213 | struct winsize win = { 0, 0, 0, 0 }; |
140 | static __sighandler_t previous_SIGWINCH_handler; /* for reset */ | 214 | static __sighandler_t previous_SIGWINCH_handler; /* for reset */ |
141 | 215 | ||
142 | /* emulate signal call if not called as a sig handler */ | 216 | /* emulate || signal call */ |
143 | if(nsig == -SIGWINCH || nsig == SIGWINCH) { | 217 | if (nsig == -SIGWINCH || nsig == SIGWINCH) { |
144 | ioctl(0, TIOCGWINSZ, &win); | 218 | ioctl(0, TIOCGWINSZ, &win); |
145 | if (win.ws_col > 0) { | 219 | if (win.ws_col > 0) { |
146 | cmdedit_setwidth( win.ws_col, nsig == SIGWINCH ); | 220 | cmdedit_setwidth(win.ws_col, nsig == SIGWINCH); |
147 | } else { | 221 | } |
148 | /* Default to 79 if their console doesn't want to share */ | ||
149 | cmdedit_setwidth( 79, nsig == SIGWINCH ); | ||
150 | } | ||
151 | } | 222 | } |
152 | |||
153 | /* Unix not all standart in recall signal */ | 223 | /* Unix not all standart in recall signal */ |
154 | 224 | ||
155 | if(nsig == -SIGWINCH) /* save previous handler */ | 225 | if (nsig == -SIGWINCH) /* save previous handler */ |
156 | previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); | 226 | previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); |
157 | else if(nsig == SIGWINCH) /* signaled called handler */ | 227 | else if (nsig == SIGWINCH) /* signaled called handler */ |
158 | signal(SIGWINCH, win_changed); /* set for next call */ | 228 | signal(SIGWINCH, win_changed); /* set for next call */ |
159 | else /* set previous handler */ | 229 | else /* nsig == 0 */ |
160 | signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */ | 230 | /* set previous handler */ |
231 | signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */ | ||
161 | } | 232 | } |
162 | 233 | ||
163 | static void cmdedit_reset_term(void) | 234 | static void cmdedit_reset_term(void) |
164 | { | 235 | { |
165 | if((handlers_sets & SET_RESET_TERM)!=0) { | 236 | if ((handlers_sets & SET_RESET_TERM) != 0) { |
166 | /* sparc and other have broken termios support: use old termio handling. */ | 237 | /* sparc and other have broken termios support: use old termio handling. */ |
167 | setTermSettings(fileno(stdin), (void*) &initial_settings); | 238 | setTermSettings(fileno(stdin), (void *) &initial_settings); |
168 | handlers_sets &= ~SET_RESET_TERM; | 239 | handlers_sets &= ~SET_RESET_TERM; |
169 | } | 240 | } |
170 | if((handlers_sets & SET_WCHG_HANDLERS)!=0) { | 241 | if ((handlers_sets & SET_WCHG_HANDLERS) != 0) { |
171 | /* reset SIGWINCH handler to previous (default) */ | 242 | /* reset SIGWINCH handler to previous (default) */ |
172 | win_changed(0); | 243 | win_changed(0); |
173 | handlers_sets &= ~SET_WCHG_HANDLERS; | 244 | handlers_sets &= ~SET_WCHG_HANDLERS; |
@@ -176,28 +247,45 @@ static void cmdedit_reset_term(void) | |||
176 | #ifdef BB_FEATURE_CLEAN_UP | 247 | #ifdef BB_FEATURE_CLEAN_UP |
177 | if (his_front) { | 248 | if (his_front) { |
178 | struct history *n; | 249 | struct history *n; |
250 | |||
179 | //while(his_front!=his_end) { | 251 | //while(his_front!=his_end) { |
180 | while(his_front!=his_end) { | 252 | while (his_front != his_end) { |
181 | n = his_front->n; | 253 | n = his_front->n; |
182 | free(his_front->s); | 254 | free(his_front->s); |
183 | free(his_front); | 255 | free(his_front); |
184 | his_front=n; | 256 | his_front = n; |
185 | } | 257 | } |
186 | } | 258 | } |
187 | #endif | 259 | #endif |
188 | } | 260 | } |
189 | 261 | ||
190 | 262 | ||
191 | |||
192 | /* special for recount position for scroll and remove terminal margin effect */ | 263 | /* special for recount position for scroll and remove terminal margin effect */ |
193 | static void cmdedit_set_out_char(int c, int next_char) { | 264 | static void cmdedit_set_out_char(int next_char) |
194 | putchar(c); | 265 | { |
195 | if(++cmdedit_x>=cmdedit_termw) { | 266 | |
267 | int c = command_ps[cursor]; | ||
268 | |||
269 | if (c == 0) | ||
270 | c = ' '; /* destroy end char? */ | ||
271 | #ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT | ||
272 | if (!isprint(c)) { /* Inverse put non-printable characters */ | ||
273 | if (((unsigned char) c) >= 128) | ||
274 | c -= 128; | ||
275 | if (((unsigned char) c) < ' ') | ||
276 | c += '@'; | ||
277 | if (c == 127) | ||
278 | c = '?'; | ||
279 | printf("\033[7m%c\033[0m", c); | ||
280 | } else | ||
281 | #endif | ||
282 | putchar(c); | ||
283 | if (++cmdedit_x >= cmdedit_termw) { | ||
196 | /* terminal is scrolled down */ | 284 | /* terminal is scrolled down */ |
197 | cmdedit_y++; | 285 | cmdedit_y++; |
198 | cmdedit_x=0; | 286 | cmdedit_x = 0; |
199 | 287 | ||
200 | if(!next_char) | 288 | if (!next_char) |
201 | next_char = ' '; | 289 | next_char = ' '; |
202 | /* destroy "(auto)margin" */ | 290 | /* destroy "(auto)margin" */ |
203 | putchar(next_char); | 291 | putchar(next_char); |
@@ -206,56 +294,221 @@ static void cmdedit_set_out_char(int c, int next_char) { | |||
206 | cursor++; | 294 | cursor++; |
207 | } | 295 | } |
208 | 296 | ||
209 | /* Move to end line. Bonus: rewrite line from cursor without use | 297 | /* Move to end line. Bonus: rewrite line from cursor */ |
210 | special control terminal strings, also saved size and speed! */ | 298 | static void input_end(void) |
211 | static void input_end (void) { | 299 | { |
212 | while(cursor < len) | 300 | while (cursor < len) |
213 | cmdedit_set_out_char(command_ps[cursor], 0); | 301 | cmdedit_set_out_char(0); |
214 | } | 302 | } |
215 | 303 | ||
216 | /* Go to the next line */ | 304 | /* Go to the next line */ |
217 | static void goto_new_line(void) { | 305 | static void goto_new_line(void) |
306 | { | ||
218 | input_end(); | 307 | input_end(); |
219 | cmdedit_set_out_char('\n', 0); | 308 | if (cmdedit_x) |
309 | putchar('\n'); | ||
220 | } | 310 | } |
221 | 311 | ||
222 | 312 | ||
223 | static inline void out1str(const char *s) { fputs (s, stdout); } | 313 | static inline void out1str(const char *s) |
224 | static inline void beep (void) { putchar('\007'); } | 314 | { |
315 | fputs(s, stdout); | ||
316 | } | ||
317 | static inline void beep(void) | ||
318 | { | ||
319 | putchar('\007'); | ||
320 | } | ||
225 | 321 | ||
226 | /* Go to HOME position */ | 322 | /* Move back one charactor */ |
227 | static void input_home(void) | 323 | /* special for slow terminal */ |
324 | static void input_backward(int num) | ||
228 | { | 325 | { |
229 | while(cmdedit_y>0) { /* up to start y */ | 326 | if (num > cursor) |
230 | out1str("\033[A"); | 327 | num = cursor; |
231 | cmdedit_y--; | 328 | cursor -= num; /* new cursor (in command, not terminal) */ |
329 | |||
330 | if (cmdedit_x >= num) { /* no to up line */ | ||
331 | cmdedit_x -= num; | ||
332 | if (num < 4) | ||
333 | while (num-- > 0) | ||
334 | putchar('\b'); | ||
335 | |||
336 | else | ||
337 | printf("\033[%dD", num); | ||
338 | } else { | ||
339 | int count_y; | ||
340 | |||
341 | if (cmdedit_x) { | ||
342 | putchar('\r'); /* back to first terminal pos. */ | ||
343 | num -= cmdedit_x; /* set previous backward */ | ||
344 | } | ||
345 | count_y = 1 + num / cmdedit_termw; | ||
346 | printf("\033[%dA", count_y); | ||
347 | cmdedit_y -= count_y; | ||
348 | /* require forward after uping */ | ||
349 | cmdedit_x = cmdedit_termw * count_y - num; | ||
350 | printf("\033[%dC", cmdedit_x); /* set term cursor */ | ||
232 | } | 351 | } |
233 | putchar('\r'); | 352 | } |
234 | cursor = 0; | 353 | |
354 | static void put_prompt(void) | ||
355 | { | ||
235 | out1str(cmdedit_prompt); | 356 | out1str(cmdedit_prompt); |
236 | cmdedit_x = cmdedit_prmt_len; | 357 | cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */ |
358 | cursor = 0; | ||
359 | } | ||
237 | 360 | ||
361 | #ifdef BB_FEATURE_BASH_STYLE_PROMT | ||
362 | static void | ||
363 | add_to_prompt(char **prmt_mem_ptr, int *alm, int *prmt_len, | ||
364 | const char *addb) | ||
365 | { | ||
366 | int l = strlen(addb); | ||
367 | |||
368 | *prmt_len += l; | ||
369 | if (*alm < (*prmt_len) + 1) { | ||
370 | *alm = (*prmt_len) + 1; | ||
371 | *prmt_mem_ptr = xrealloc(*prmt_mem_ptr, *alm); | ||
372 | } | ||
373 | strcat(*prmt_mem_ptr, addb); | ||
238 | } | 374 | } |
375 | #endif | ||
239 | 376 | ||
240 | /* Move back one charactor */ | 377 | static void parse_prompt(const char *prmt_ptr) |
241 | static void input_backward(void) { | 378 | { |
242 | if (cursor > 0) { | 379 | #ifdef BB_FEATURE_BASH_STYLE_PROMT |
243 | cursor--; | 380 | int alm = strlen(prmt_ptr) + 1; /* supposedly require memory */ |
244 | if(cmdedit_x!=0) { /* no first position in terminal line */ | 381 | int prmt_len = 0; |
245 | putchar('\b'); | 382 | int sub_len = 0; |
246 | cmdedit_x--; | 383 | int flg_not_length = '['; |
384 | char *prmt_mem_ptr = xstrdup(prmt_ptr); | ||
385 | char pwd_buf[PATH_MAX + 1]; | ||
386 | char buf[16]; | ||
387 | int c; | ||
388 | |||
389 | pwd_buf[0] = 0; | ||
390 | *prmt_mem_ptr = 0; | ||
391 | |||
392 | while (*prmt_ptr) { | ||
393 | c = *prmt_ptr++; | ||
394 | if (c == '\\') { | ||
395 | c = *prmt_ptr; | ||
396 | if (c == 0) | ||
397 | break; | ||
398 | prmt_ptr++; | ||
399 | switch (c) { | ||
400 | case 'u': | ||
401 | add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, user_buf); | ||
402 | continue; | ||
403 | case 'h': | ||
404 | if (hostname_buf[0] == 0) { | ||
405 | hostname_buf = xcalloc(256, 1); | ||
406 | if (gethostname(hostname_buf, 255) < 0) { | ||
407 | strcpy(hostname_buf, "?"); | ||
408 | } else { | ||
409 | char *s = strchr(hostname_buf, '.'); | ||
410 | |||
411 | if (s) | ||
412 | *s = 0; | ||
413 | } | ||
414 | } | ||
415 | add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, | ||
416 | hostname_buf); | ||
417 | continue; | ||
418 | case '$': | ||
419 | c = my_euid == 0 ? '#' : '$'; | ||
420 | break; | ||
421 | case 'w': | ||
422 | if (pwd_buf[0] == 0) { | ||
423 | int l; | ||
424 | |||
425 | getcwd(pwd_buf, PATH_MAX); | ||
426 | l = strlen(home_pwd_buf); | ||
427 | if (home_pwd_buf[0] != 0 && | ||
428 | strncmp(home_pwd_buf, pwd_buf, l) == 0) { | ||
429 | strcpy(pwd_buf + 1, pwd_buf + l); | ||
430 | pwd_buf[0] = '~'; | ||
431 | } | ||
432 | } | ||
433 | add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, pwd_buf); | ||
434 | continue; | ||
435 | case '!': | ||
436 | snprintf(buf, sizeof(buf), "%d", num_ok_lines); | ||
437 | add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, buf); | ||
438 | continue; | ||
439 | case 'e': | ||
440 | case 'E': /* \e \E = \033 */ | ||
441 | c = '\033'; | ||
442 | break; | ||
443 | case 'x': | ||
444 | case 'X': | ||
445 | case '0': | ||
446 | case '1': | ||
447 | case '2': | ||
448 | case '3': | ||
449 | case '4': | ||
450 | case '5': | ||
451 | case '6': | ||
452 | case '7':{ | ||
453 | int l; | ||
454 | int ho = 0; | ||
455 | char *eho; | ||
456 | |||
457 | if (c == 'X') | ||
458 | c = 'x'; | ||
459 | |||
460 | for (l = 0; l < 3;) { | ||
461 | |||
462 | buf[l++] = *prmt_ptr; | ||
463 | buf[l] = 0; | ||
464 | ho = strtol(buf, &eho, c == 'x' ? 16 : 8); | ||
465 | if (ho > UCHAR_MAX || (eho - buf) < l) { | ||
466 | l--; | ||
467 | break; | ||
468 | } | ||
469 | prmt_ptr++; | ||
470 | } | ||
471 | buf[l] = 0; | ||
472 | ho = strtol(buf, 0, c == 'x' ? 16 : 8); | ||
473 | c = ho == 0 ? '?' : (char) ho; | ||
474 | break; | ||
247 | } | 475 | } |
248 | else { | 476 | case '[': |
249 | out1str("\033[A"); /* up */ | 477 | case ']': |
250 | cmdedit_y--; | 478 | if (c == flg_not_length) { |
251 | 479 | flg_not_length = flg_not_length == '[' ? ']' : '['; | |
252 | /* to end in current terminal line */ | 480 | continue; |
253 | while(cmdedit_x<(cmdedit_termw-1)) { | ||
254 | out1str("\033[C"); | ||
255 | cmdedit_x++; | ||
256 | } | 481 | } |
482 | break; | ||
257 | } | 483 | } |
484 | } | ||
485 | buf[0] = c; | ||
486 | buf[1] = 0; | ||
487 | add_to_prompt(&prmt_mem_ptr, &alm, &prmt_len, buf); | ||
488 | if (flg_not_length == ']') | ||
489 | sub_len++; | ||
258 | } | 490 | } |
491 | cmdedit_prmt_len = prmt_len - sub_len; | ||
492 | cmdedit_prompt = prmt_mem_ptr; | ||
493 | #else | ||
494 | cmdedit_prompt = prmt_ptr; | ||
495 | cmdedit_prmt_len = strlen(prmt_ptr); | ||
496 | #endif | ||
497 | put_prompt(); | ||
498 | } | ||
499 | |||
500 | |||
501 | /* draw promt, editor line, and clear tail */ | ||
502 | static void redraw(int y, int back_cursor) | ||
503 | { | ||
504 | if (y > 0) /* up to start y */ | ||
505 | printf("\033[%dA", y); | ||
506 | cmdedit_y = 0; /* new quasireal y */ | ||
507 | putchar('\r'); | ||
508 | put_prompt(); | ||
509 | input_end(); /* rewrite */ | ||
510 | printf("\033[J"); /* destroy tail after cursor */ | ||
511 | input_backward(back_cursor); | ||
259 | } | 512 | } |
260 | 513 | ||
261 | /* Delete the char in front of the cursor */ | 514 | /* Delete the char in front of the cursor */ |
@@ -265,21 +518,20 @@ static void input_delete(void) | |||
265 | 518 | ||
266 | if (j == len) | 519 | if (j == len) |
267 | return; | 520 | return; |
268 | 521 | ||
269 | memmove (command_ps + j, command_ps + j + 1, BUFSIZ - j - 1); | 522 | strcpy(command_ps + j, command_ps + j + 1); |
270 | len--; | 523 | len--; |
271 | input_end(); /* rewtite new line */ | 524 | input_end(); /* rewtite new line */ |
272 | cmdedit_set_out_char(' ', 0); /* destroy end char */ | 525 | cmdedit_set_out_char(0); /* destroy end char */ |
273 | while (j < cursor) | 526 | input_backward(cursor - j); /* back to old pos cursor */ |
274 | input_backward(); /* back to old pos cursor */ | ||
275 | } | 527 | } |
276 | 528 | ||
277 | /* Delete the char in back of the cursor */ | 529 | /* Delete the char in back of the cursor */ |
278 | static void input_backspace(void) | 530 | static void input_backspace(void) |
279 | { | 531 | { |
280 | if (cursor > 0) { | 532 | if (cursor > 0) { |
281 | input_backward(); | 533 | input_backward(1); |
282 | input_delete (); | 534 | input_delete(); |
283 | } | 535 | } |
284 | } | 536 | } |
285 | 537 | ||
@@ -287,382 +539,653 @@ static void input_backspace(void) | |||
287 | /* Move forward one charactor */ | 539 | /* Move forward one charactor */ |
288 | static void input_forward(void) | 540 | static void input_forward(void) |
289 | { | 541 | { |
290 | if (cursor < len) | 542 | if (cursor < len) |
291 | cmdedit_set_out_char(command_ps[cursor], command_ps[cursor + 1]); | 543 | cmdedit_set_out_char(command_ps[cursor + 1]); |
292 | } | 544 | } |
293 | 545 | ||
294 | 546 | ||
295 | static void clean_up_and_die(int sig) | 547 | static void clean_up_and_die(int sig) |
296 | { | 548 | { |
297 | goto_new_line(); | 549 | goto_new_line(); |
298 | if (sig!=SIGINT) | 550 | if (sig != SIGINT) |
299 | exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */ | 551 | exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */ |
300 | cmdedit_reset_term(); | 552 | cmdedit_reset_term(); |
301 | } | 553 | } |
302 | 554 | ||
303 | static void cmdedit_setwidth(int w, int redraw_flg) | 555 | static void cmdedit_setwidth(int w, int redraw_flg) |
304 | { | 556 | { |
305 | cmdedit_termw = cmdedit_prmt_len+2; | 557 | cmdedit_termw = cmdedit_prmt_len + 2; |
306 | if (w > cmdedit_termw) { | 558 | if (w > cmdedit_termw) { |
307 | 559 | ||
308 | cmdedit_termw = w; | 560 | cmdedit_termw = w; |
309 | 561 | ||
310 | if(redraw_flg) { | 562 | if (redraw_flg) { |
311 | int sav_cursor = cursor; | 563 | /* new y for current cursor */ |
312 | 564 | int new_y = (cursor + cmdedit_prmt_len) / w; | |
313 | /* set variables for new terminal size */ | ||
314 | cmdedit_y = sav_cursor/w; | ||
315 | cmdedit_x = sav_cursor-cmdedit_y*w; | ||
316 | 565 | ||
317 | /* redraw */ | 566 | /* redraw */ |
318 | input_home(); | 567 | redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor); |
319 | input_end(); | 568 | fflush(stdout); |
320 | while(sav_cursor<cursor) | ||
321 | input_backward(); | ||
322 | } | 569 | } |
323 | } else { | 570 | } else { |
324 | error_msg("\n*** Error: minimum screen width is %d", cmdedit_termw); | 571 | error_msg("\n*** Error: minimum screen width is %d", |
572 | cmdedit_termw); | ||
325 | } | 573 | } |
326 | } | 574 | } |
327 | 575 | ||
328 | extern void cmdedit_init(void) | 576 | extern void cmdedit_init(void) |
329 | { | 577 | { |
330 | if((handlers_sets & SET_WCHG_HANDLERS)==0) { | 578 | if ((handlers_sets & SET_WCHG_HANDLERS) == 0) { |
331 | /* pretend we received a signal in order to set term size and sig handling */ | 579 | /* emulate usage handler to set handler and call yours work */ |
332 | win_changed(-SIGWINCH); | 580 | win_changed(-SIGWINCH); |
333 | handlers_sets |= SET_WCHG_HANDLERS; | 581 | handlers_sets |= SET_WCHG_HANDLERS; |
334 | } | 582 | } |
335 | 583 | ||
336 | if((handlers_sets & SET_ATEXIT)==0) { | 584 | if ((handlers_sets & SET_ATEXIT) == 0) { |
337 | atexit(cmdedit_reset_term); /* be sure to do this only once */ | 585 | #ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR |
586 | struct passwd *entry; | ||
587 | |||
588 | my_euid = geteuid(); | ||
589 | entry = getpwuid(my_euid); | ||
590 | if (entry) { | ||
591 | user_buf = xstrdup(entry->pw_name); | ||
592 | home_pwd_buf = xstrdup(entry->pw_dir); | ||
593 | } | ||
594 | #endif | ||
595 | |||
596 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | ||
597 | |||
598 | #ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIR | ||
599 | my_euid = geteuid(); | ||
600 | #endif | ||
601 | my_uid = getuid(); | ||
602 | my_gid = getgid(); | ||
603 | #endif /* BB_FEATURE_SH_TAB_COMPLETION */ | ||
338 | handlers_sets |= SET_ATEXIT; | 604 | handlers_sets |= SET_ATEXIT; |
605 | atexit(cmdedit_reset_term); /* be sure to do this only once */ | ||
339 | } | 606 | } |
340 | if((handlers_sets & SET_TERM_HANDLERS)==0) { | 607 | |
608 | if ((handlers_sets & SET_TERM_HANDLERS) == 0) { | ||
341 | signal(SIGKILL, clean_up_and_die); | 609 | signal(SIGKILL, clean_up_and_die); |
342 | signal(SIGINT, clean_up_and_die); | 610 | signal(SIGINT, clean_up_and_die); |
343 | signal(SIGQUIT, clean_up_and_die); | 611 | signal(SIGQUIT, clean_up_and_die); |
344 | signal(SIGTERM, clean_up_and_die); | 612 | signal(SIGTERM, clean_up_and_die); |
345 | handlers_sets |= SET_TERM_HANDLERS; | 613 | handlers_sets |= SET_TERM_HANDLERS; |
346 | } | 614 | } |
615 | |||
347 | } | 616 | } |
348 | 617 | ||
349 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | 618 | #ifdef BB_FEATURE_SH_TAB_COMPLETION |
350 | 619 | ||
620 | static int is_execute(const struct stat *st) | ||
621 | { | ||
622 | if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) || | ||
623 | (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) || | ||
624 | (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) || | ||
625 | (st->st_mode & S_IXOTH)) return TRUE; | ||
626 | return FALSE; | ||
627 | } | ||
628 | |||
351 | #ifdef BB_FEATURE_USERNAME_COMPLETION | 629 | #ifdef BB_FEATURE_USERNAME_COMPLETION |
352 | static char** username_tab_completion(char *ud, int *num_matches) | 630 | |
631 | static char **username_tab_completion(char *ud, int *num_matches) | ||
353 | { | 632 | { |
354 | static struct passwd *entry; | 633 | struct passwd *entry; |
355 | int userlen; | 634 | int userlen; |
356 | char **matches = (char **) NULL; | 635 | char *temp; |
357 | char *temp; | ||
358 | int nm = 0; | ||
359 | 636 | ||
360 | setpwent (); | ||
361 | userlen = strlen (ud + 1); | ||
362 | 637 | ||
363 | while ((entry = getpwent ()) != NULL) { | 638 | ud++; /* ~user/... to user/... */ |
364 | /* Null usernames should result in all users as possible completions. */ | 639 | userlen = strlen(ud); |
365 | if (!userlen || !strncmp (ud + 1, entry->pw_name, userlen)) { | ||
366 | 640 | ||
367 | temp = xmalloc (3 + strlen (entry->pw_name)); | 641 | if (num_matches == 0) { /* "~/..." or "~user/..." */ |
368 | sprintf(temp, "~%s/", entry->pw_name); | 642 | char *sav_ud = ud - 1; |
643 | char *home = 0; | ||
369 | 644 | ||
370 | matches = xrealloc(matches, (nm+1)*sizeof(char *)); | 645 | if (*ud == '/') { /* "~/..." */ |
371 | matches[nm++] = temp; | 646 | home = home_pwd_buf; |
372 | } | 647 | } else { |
373 | } | 648 | /* "~user/..." */ |
649 | temp = strchr(ud, '/'); | ||
650 | *temp = 0; /* ~user\0 */ | ||
651 | entry = getpwnam(ud); | ||
652 | *temp = '/'; /* restore ~user/... */ | ||
653 | ud = temp; | ||
654 | if (entry) | ||
655 | home = entry->pw_dir; | ||
656 | } | ||
657 | if (home) { | ||
658 | if ((userlen + strlen(home) + 1) < BUFSIZ) { | ||
659 | char temp2[BUFSIZ]; /* argument size */ | ||
374 | 660 | ||
375 | endpwent (); | 661 | /* /home/user/... */ |
376 | (*num_matches) = nm; | 662 | sprintf(temp2, "%s%s", home, ud); |
377 | return (matches); | 663 | strcpy(sav_ud, temp2); |
664 | } | ||
665 | } | ||
666 | return 0; /* void, result save to argument :-) */ | ||
667 | } else { | ||
668 | /* "~[^/]*" */ | ||
669 | char **matches = (char **) NULL; | ||
670 | int nm = 0; | ||
671 | |||
672 | setpwent(); | ||
673 | |||
674 | while ((entry = getpwent()) != NULL) { | ||
675 | /* Null usernames should result in all users as possible completions. */ | ||
676 | if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) { | ||
677 | |||
678 | temp = xmalloc(3 + strlen(entry->pw_name)); | ||
679 | sprintf(temp, "~%s/", entry->pw_name); | ||
680 | matches = xrealloc(matches, (nm + 1) * sizeof(char *)); | ||
681 | |||
682 | matches[nm++] = temp; | ||
683 | } | ||
684 | } | ||
685 | |||
686 | endpwent(); | ||
687 | (*num_matches) = nm; | ||
688 | return (matches); | ||
689 | } | ||
378 | } | 690 | } |
379 | #endif | 691 | #endif /* BB_FEATURE_USERNAME_COMPLETION */ |
380 | 692 | ||
381 | enum { | 693 | enum { |
382 | FIND_EXE_ONLY = 0, | 694 | FIND_EXE_ONLY = 0, |
383 | FIND_DIR_ONLY = 1, | 695 | FIND_DIR_ONLY = 1, |
384 | FIND_FILE_ONLY = 2, | 696 | FIND_FILE_ONLY = 2, |
385 | }; | 697 | }; |
386 | 698 | ||
387 | #include <dirent.h> | ||
388 | |||
389 | static int path_parse(char ***p, int flags) | 699 | static int path_parse(char ***p, int flags) |
390 | { | 700 | { |
391 | int npth; | 701 | int npth; |
392 | char *tmp; | 702 | char *tmp; |
393 | char *pth; | 703 | char *pth; |
394 | 704 | ||
395 | if(flags!=FIND_EXE_ONLY || (pth=getenv("PATH"))==0) { | ||
396 | /* if not setenv PATH variable, to search cur dir "." */ | 705 | /* if not setenv PATH variable, to search cur dir "." */ |
397 | (*p) = xmalloc(sizeof(char *)); | 706 | if (flags != FIND_EXE_ONLY || (pth = getenv("PATH")) == 0 || |
398 | (*p)[0] = xstrdup("."); | 707 | /* PATH=<empty> or PATH=:<empty> */ |
708 | *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) { | ||
399 | return 1; | 709 | return 1; |
400 | } | 710 | } |
401 | 711 | ||
402 | tmp = pth; | 712 | tmp = pth; |
403 | npth=0; | 713 | npth = 0; |
404 | 714 | ||
405 | for(;;) { | 715 | for (;;) { |
406 | npth++; /* count words is + 1 count ':' */ | 716 | npth++; /* count words is + 1 count ':' */ |
407 | tmp = strchr(tmp, ':'); | 717 | tmp = strchr(tmp, ':'); |
408 | if(tmp) | 718 | if (tmp) { |
409 | tmp++; | 719 | if (*++tmp == 0) |
410 | else | 720 | break; /* :<empty> */ |
721 | } else | ||
411 | break; | 722 | break; |
412 | } | 723 | } |
413 | 724 | ||
414 | *p = xmalloc(npth*sizeof(char *)); | 725 | *p = xmalloc(npth * sizeof(char *)); |
415 | 726 | ||
416 | tmp = pth; | 727 | tmp = pth; |
417 | (*p)[0] = xstrdup(tmp); | 728 | (*p)[0] = xstrdup(tmp); |
418 | npth=1; /* count words is + 1 count ':' */ | 729 | npth = 1; /* count words is + 1 count ':' */ |
419 | 730 | ||
420 | for(;;) { | 731 | for (;;) { |
421 | tmp = strchr(tmp, ':'); | 732 | tmp = strchr(tmp, ':'); |
422 | if(tmp) { | 733 | if (tmp) { |
423 | (*p)[0][(tmp-pth)]=0; /* ':' -> '\0'*/ | 734 | (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */ |
424 | tmp++; | 735 | if (*++tmp == 0) |
736 | break; /* :<empty> */ | ||
425 | } else | 737 | } else |
426 | break; | 738 | break; |
427 | (*p)[npth++] = &(*p)[0][(tmp-pth)]; /* p[next]=p[0][&'\0'+1] */ | 739 | (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */ |
428 | } | 740 | } |
429 | 741 | ||
430 | return npth; | 742 | return npth; |
431 | } | 743 | } |
432 | 744 | ||
433 | static char** exe_n_cwd_tab_completion(char* command, int *num_matches, int type) | 745 | static char *add_quote_for_spec_chars(char *found) |
434 | { | 746 | { |
435 | char *dirName; | 747 | int l = 0; |
436 | char **matches = 0; | 748 | char *s = xmalloc((strlen(found) + 1) * 2); |
437 | DIR *dir; | ||
438 | struct dirent *next; | ||
439 | char cmd [BUFSIZ+4]; | ||
440 | char *dirbuf; | ||
441 | char found [BUFSIZ+4]; | ||
442 | int nm = *num_matches; | ||
443 | struct stat st; | ||
444 | char **paths; | ||
445 | int npaths; | ||
446 | int i; | ||
447 | char full_pth[BUFSIZ+4+PATH_MAX]; | ||
448 | 749 | ||
750 | while (*found) { | ||
751 | if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found)) | ||
752 | s[l++] = '\\'; | ||
753 | s[l++] = *found++; | ||
754 | } | ||
755 | s[l] = 0; | ||
756 | return s; | ||
757 | } | ||
449 | 758 | ||
450 | strcpy(cmd, command); /* save for change (last '/' to '\0') */ | 759 | static char **exe_n_cwd_tab_completion(char *command, int *num_matches, |
760 | int type) | ||
761 | { | ||
451 | 762 | ||
452 | dirName = strrchr(cmd, '/'); | 763 | char **matches = 0; |
453 | if(dirName==NULL) { | 764 | DIR *dir; |
765 | struct dirent *next; | ||
766 | char dirbuf[BUFSIZ]; | ||
767 | int nm = *num_matches; | ||
768 | struct stat st; | ||
769 | char *path1[1]; | ||
770 | char **paths = path1; | ||
771 | int npaths; | ||
772 | int i; | ||
773 | char found[BUFSIZ + 4 + PATH_MAX]; | ||
774 | char *pfind = strrchr(command, '/'); | ||
775 | |||
776 | path1[0] = "."; | ||
777 | |||
778 | if (pfind == NULL) { | ||
454 | /* no dir, if flags==EXE_ONLY - get paths, else "." */ | 779 | /* no dir, if flags==EXE_ONLY - get paths, else "." */ |
455 | npaths = path_parse(&paths, type); | 780 | npaths = path_parse(&paths, type); |
456 | if(npaths==0) | 781 | pfind = command; |
457 | return 0; | ||
458 | } else { | 782 | } else { |
459 | /* with dir */ | 783 | /* with dir */ |
784 | /* save for change */ | ||
785 | strcpy(dirbuf, command); | ||
786 | /* set dir only */ | ||
787 | dirbuf[(pfind - command) + 1] = 0; | ||
788 | #ifdef BB_FEATURE_USERNAME_COMPLETION | ||
789 | if (dirbuf[0] == '~') /* ~/... or ~user/... */ | ||
790 | username_tab_completion(dirbuf, 0); | ||
791 | #endif | ||
792 | /* "strip" dirname in command */ | ||
793 | pfind++; | ||
460 | 794 | ||
461 | /* save dir */ | ||
462 | dirbuf = xstrdup(cmd); | ||
463 | /* set only dirname */ | ||
464 | dirbuf[(dirName-cmd)+1]=0; | ||
465 | |||
466 | /* strip dirname in cmd */ | ||
467 | strcpy(cmd, dirName+1); | ||
468 | |||
469 | paths = xmalloc(sizeof(char*)); | ||
470 | paths[0] = dirbuf; | 795 | paths[0] = dirbuf; |
471 | npaths = 1; /* only 1 dir */ | 796 | npaths = 1; /* only 1 dir */ |
472 | } | 797 | } |
473 | 798 | ||
474 | for(i=0; i < npaths; i++) { | 799 | for (i = 0; i < npaths; i++) { |
475 | 800 | ||
476 | dir = opendir(paths[i]); | 801 | dir = opendir(paths[i]); |
477 | if (!dir) { | 802 | if (!dir) /* Don't print an error */ |
478 | /* Don't print an error, just shut up and return */ | 803 | continue; |
479 | return (matches); | 804 | |
480 | } | 805 | while ((next = readdir(dir)) != NULL) { |
481 | while ((next = readdir(dir)) != NULL) { | 806 | const char *str_merge = "%s/%s"; |
807 | char *str_found = next->d_name; | ||
808 | |||
482 | /* matched ? */ | 809 | /* matched ? */ |
483 | if(strncmp(next->d_name, cmd, strlen(cmd))) | 810 | if (strncmp(str_found, pfind, strlen(pfind))) |
484 | continue; | 811 | continue; |
485 | /* not see .name without .match */ | 812 | /* not see .name without .match */ |
486 | if(*next->d_name == '.' && *cmd != '.') | 813 | if (*str_found == '.' && *pfind == 0) { |
487 | continue; | 814 | if (*paths[i] == '/' && paths[i][1] == 0 |
488 | sprintf(full_pth, "%s/%s", paths[i], next->d_name); | 815 | && str_found[1] == 0) str_found = ""; /* only "/" */ |
489 | /* hmm, remover in progress? */ | 816 | else |
490 | if(stat(full_pth, &st)<0) | ||
491 | continue; | 817 | continue; |
492 | /* Cool, found a match. */ | 818 | } |
819 | if (paths[i][strlen(paths[i]) - 1] == '/') | ||
820 | str_merge = "%s%s"; | ||
821 | sprintf(found, str_merge, paths[i], str_found); | ||
822 | /* hmm, remover in progress? */ | ||
823 | if (stat(found, &st) < 0) | ||
824 | continue; | ||
825 | /* find with dirs ? */ | ||
826 | if (paths[i] != dirbuf) | ||
827 | strcpy(found, next->d_name); /* only name */ | ||
493 | if (S_ISDIR(st.st_mode)) { | 828 | if (S_ISDIR(st.st_mode)) { |
494 | /* name is directory */ | 829 | /* name is directory */ |
495 | strcpy(found, next->d_name); | 830 | /* algorithmic only "/" ? */ |
496 | strcat(found, "/"); | 831 | if (*str_found) |
497 | if(type==FIND_DIR_ONLY) | 832 | strcat(found, "/"); |
498 | strcat(found, " "); | 833 | str_found = add_quote_for_spec_chars(found); |
499 | } else { | 834 | } else { |
500 | /* not put found file if search only dirs for cd */ | 835 | /* not put found file if search only dirs for cd */ |
501 | if(type==FIND_DIR_ONLY) | 836 | if (type == FIND_DIR_ONLY) |
502 | continue; | 837 | continue; |
503 | strcpy(found, next->d_name); | 838 | str_found = add_quote_for_spec_chars(found); |
504 | strcat(found, " "); | 839 | if (type == FIND_FILE_ONLY || |
505 | } | 840 | (type == FIND_EXE_ONLY && is_execute(&st) == TRUE)) |
841 | strcat(str_found, " "); | ||
842 | } | ||
506 | /* Add it to the list */ | 843 | /* Add it to the list */ |
507 | matches = xrealloc(matches, (nm+1)*sizeof(char *)); | 844 | matches = xrealloc(matches, (nm + 1) * sizeof(char *)); |
508 | matches[nm++] = xstrdup(found); | 845 | |
846 | matches[nm++] = str_found; | ||
509 | } | 847 | } |
848 | closedir(dir); | ||
849 | } | ||
850 | if (paths != path1) { | ||
851 | free(paths[0]); /* allocated memory only in first member */ | ||
852 | free(paths); | ||
510 | } | 853 | } |
511 | free(paths[0]); /* allocate memory only in first member */ | ||
512 | free(paths); | ||
513 | *num_matches = nm; | 854 | *num_matches = nm; |
514 | return (matches); | 855 | return (matches); |
515 | } | 856 | } |
516 | 857 | ||
517 | static void input_tab(int lastWasTab) | 858 | static int match_compare(const void *a, const void *b) |
518 | { | 859 | { |
519 | /* Do TAB completion */ | 860 | return strcmp(*(char **) a, *(char **) b); |
520 | static int num_matches; | 861 | } |
521 | static char **matches; | ||
522 | 862 | ||
523 | char matchBuf[BUFSIZ]; | ||
524 | 863 | ||
525 | int pos = cursor; | ||
526 | int find_type=FIND_FILE_ONLY; | ||
527 | 864 | ||
865 | #define QUOT (UCHAR_MAX+1) | ||
528 | 866 | ||
529 | if (lastWasTab == FALSE) { | 867 | #define collapse_pos(is, in) { \ |
530 | char *tmp, *tmp1; | 868 | memcpy(int_buf+is, int_buf+in, (BUFSIZ+1-is-in)*sizeof(int)); \ |
531 | int len_found; | 869 | memcpy(pos_buf+is, pos_buf+in, (BUFSIZ+1-is-in)*sizeof(int)); } |
532 | 870 | ||
533 | /* For now, we will not bother with trying to distinguish | 871 | static int find_match(char *matchBuf, int *len_with_quotes) |
534 | * whether the cursor is in/at a command extression -- we | 872 | { |
535 | * will always try all possible matches. If you don't like | 873 | int i, j; |
536 | * that then feel free to fix it. | 874 | int command_mode; |
537 | */ | 875 | int c, c2; |
538 | 876 | int int_buf[BUFSIZ + 1]; | |
539 | /* Make a local copy of the string -- up | 877 | int pos_buf[BUFSIZ + 1]; |
540 | * to the position of the cursor */ | 878 | |
541 | memset(matchBuf, 0, BUFSIZ); | 879 | /* set to integer dimension characters and own positions */ |
542 | tmp = strncpy(matchBuf, command_ps, cursor); | 880 | for (i = 0;; i++) { |
881 | int_buf[i] = (int) ((unsigned char) matchBuf[i]); | ||
882 | if (int_buf[i] == 0) { | ||
883 | pos_buf[i] = -1; /* indicator end line */ | ||
884 | break; | ||
885 | } else | ||
886 | pos_buf[i] = i; | ||
887 | } | ||
543 | 888 | ||
544 | /* skip past any command seperator tokens */ | 889 | /* mask \+symbol and convert '\t' to ' ' */ |
545 | while ( (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { | 890 | for (i = j = 0; matchBuf[i]; i++, j++) |
546 | tmp = ++tmp1; | 891 | if (matchBuf[i] == '\\') { |
892 | collapse_pos(j, j + 1); | ||
893 | int_buf[j] |= QUOT; | ||
894 | i++; | ||
895 | #ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT | ||
896 | if (matchBuf[i] == '\t') /* algorithm equivalent */ | ||
897 | int_buf[j] = ' ' | QUOT; | ||
898 | #endif | ||
547 | } | 899 | } |
900 | #ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT | ||
901 | else if (matchBuf[i] == '\t') | ||
902 | int_buf[j] = ' '; | ||
903 | #endif | ||
548 | 904 | ||
549 | /* skip any leading white space */ | 905 | /* mask "symbols" or 'symbols' */ |
550 | while (*tmp == ' ') | 906 | c2 = 0; |
551 | tmp++; | 907 | for (i = 0; int_buf[i]; i++) { |
908 | c = int_buf[i]; | ||
909 | if (c == '\'' || c == '"') { | ||
910 | if (c2 == 0) | ||
911 | c2 = c; | ||
912 | else { | ||
913 | if (c == c2) | ||
914 | c2 = 0; | ||
915 | else | ||
916 | int_buf[i] |= QUOT; | ||
917 | } | ||
918 | } else if (c2 != 0 && c != '$') | ||
919 | int_buf[i] |= QUOT; | ||
920 | } | ||
552 | 921 | ||
553 | if(strncmp(tmp, "cd ", 3)==0) | 922 | /* skip commands with arguments if line have commands delimiters */ |
554 | find_type = FIND_DIR_ONLY; | 923 | /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */ |
555 | else if(strchr(tmp, ' ')==NULL) | 924 | for (i = 0; int_buf[i]; i++) { |
556 | find_type = FIND_EXE_ONLY; | 925 | c = int_buf[i]; |
926 | c2 = int_buf[i + 1]; | ||
927 | j = i ? int_buf[i - 1] : -1; | ||
928 | command_mode = 0; | ||
929 | if (c == ';' || c == '&' || c == '|') { | ||
930 | command_mode = 1 + (c == c2); | ||
931 | if (c == '&') { | ||
932 | if (j == '>' || j == '<') | ||
933 | command_mode = 0; | ||
934 | } else if (c == '|' && j == '>') | ||
935 | command_mode = 0; | ||
936 | } | ||
937 | if (command_mode) { | ||
938 | collapse_pos(0, i + command_mode); | ||
939 | i = -1; /* hack incremet */ | ||
940 | } | ||
941 | } | ||
942 | /* collapse `command...` */ | ||
943 | for (i = 0; int_buf[i]; i++) | ||
944 | if (int_buf[i] == '`') { | ||
945 | for (j = i + 1; int_buf[j]; j++) | ||
946 | if (int_buf[j] == '`') { | ||
947 | collapse_pos(i, j + 1); | ||
948 | j = 0; | ||
949 | break; | ||
950 | } | ||
951 | if (j) { | ||
952 | /* not found close ` - command mode, collapse all previous */ | ||
953 | collapse_pos(0, i + 1); | ||
954 | break; | ||
955 | } else | ||
956 | i--; /* hack incremet */ | ||
957 | } | ||
557 | 958 | ||
558 | /* find begin curent word */ | 959 | /* collapse (command...(command...)...) or {command...{command...}...} */ |
559 | if( (tmp1=strrchr(tmp, ' ')) != NULL) { | 960 | c = 0; /* "recursive" level */ |
560 | tmp = ++tmp1; | 961 | c2 = 0; |
962 | for (i = 0; int_buf[i]; i++) | ||
963 | if (int_buf[i] == '(' || int_buf[i] == '{') { | ||
964 | if (int_buf[i] == '(') | ||
965 | c++; | ||
966 | else | ||
967 | c2++; | ||
968 | collapse_pos(0, i + 1); | ||
969 | i = -1; /* hack incremet */ | ||
970 | } | ||
971 | for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) | ||
972 | if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) { | ||
973 | if (int_buf[i] == ')') | ||
974 | c--; | ||
975 | else | ||
976 | c2--; | ||
977 | collapse_pos(0, i + 1); | ||
978 | i = -1; /* hack incremet */ | ||
561 | } | 979 | } |
562 | strcpy(matchBuf, tmp); | ||
563 | 980 | ||
564 | /* Free up any memory already allocated */ | 981 | /* skip first not quote space */ |
982 | for (i = 0; int_buf[i]; i++) | ||
983 | if (int_buf[i] != ' ') | ||
984 | break; | ||
985 | if (i) | ||
986 | collapse_pos(0, i); | ||
987 | |||
988 | /* set find mode for completion */ | ||
989 | command_mode = FIND_EXE_ONLY; | ||
990 | for (i = 0; int_buf[i]; i++) | ||
991 | if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { | ||
992 | if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY | ||
993 | && strncmp(&matchBuf[pos_buf[0]], "cd", 2) == 0) | ||
994 | command_mode = FIND_DIR_ONLY; | ||
995 | else { | ||
996 | command_mode = FIND_FILE_ONLY; | ||
997 | break; | ||
998 | } | ||
999 | } | ||
1000 | /* "strlen" */ | ||
1001 | for (i = 0; int_buf[i]; i++); | ||
1002 | /* find last word */ | ||
1003 | for (--i; i >= 0; i--) { | ||
1004 | c = int_buf[i]; | ||
1005 | if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') { | ||
1006 | collapse_pos(0, i + 1); | ||
1007 | break; | ||
1008 | } | ||
1009 | } | ||
1010 | /* skip first not quoted '\'' or '"' */ | ||
1011 | for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++); | ||
1012 | /* collapse quote or unquote // or /~ */ | ||
1013 | while ((int_buf[i] & ~QUOT) == '/' && ( | ||
1014 | (int_buf[i + 1] & ~QUOT) == '/' | ||
1015 | || (int_buf[i + 1] & ~QUOT) == | ||
1016 | '~')) i++; | ||
1017 | if (i) | ||
1018 | collapse_pos(0, i); | ||
1019 | |||
1020 | /* set only match and destroy quotes */ | ||
1021 | j = 0; | ||
1022 | for (i = 0; pos_buf[i] >= 0; i++) { | ||
1023 | matchBuf[i] = matchBuf[pos_buf[i]]; | ||
1024 | j = pos_buf[i] + 1; | ||
1025 | } | ||
1026 | matchBuf[i] = 0; | ||
1027 | /* old lenght matchBuf with quotes symbols */ | ||
1028 | *len_with_quotes = j ? j - pos_buf[0] : 0; | ||
1029 | |||
1030 | return command_mode; | ||
1031 | } | ||
1032 | |||
1033 | |||
1034 | static void input_tab(int *lastWasTab) | ||
1035 | { | ||
1036 | /* Do TAB completion */ | ||
1037 | static int num_matches; | ||
1038 | static char **matches; | ||
1039 | |||
1040 | if (lastWasTab == 0) { /* free all memory */ | ||
565 | if (matches) { | 1041 | if (matches) { |
566 | while(num_matches>0) | 1042 | while (num_matches > 0) |
567 | free(matches[--num_matches]); | 1043 | free(matches[--num_matches]); |
568 | free(matches); | 1044 | free(matches); |
569 | matches = (char **) NULL; | 1045 | matches = (char **) NULL; |
570 | } | 1046 | } |
1047 | return; | ||
1048 | } | ||
1049 | if (*lastWasTab == FALSE) { | ||
1050 | |||
1051 | char *tmp; | ||
1052 | int len_found; | ||
1053 | char matchBuf[BUFSIZ]; | ||
1054 | int find_type; | ||
1055 | int recalc_pos; | ||
1056 | |||
1057 | *lastWasTab = TRUE; /* flop trigger */ | ||
1058 | |||
1059 | /* Make a local copy of the string -- up | ||
1060 | * to the position of the cursor */ | ||
1061 | tmp = strncpy(matchBuf, command_ps, cursor); | ||
1062 | tmp[cursor] = 0; | ||
1063 | |||
1064 | find_type = find_match(matchBuf, &recalc_pos); | ||
1065 | |||
1066 | /* Free up any memory already allocated */ | ||
1067 | input_tab(0); | ||
571 | 1068 | ||
572 | #ifdef BB_FEATURE_USERNAME_COMPLETION | 1069 | #ifdef BB_FEATURE_USERNAME_COMPLETION |
573 | /* If the word starts with `~' and there is no slash in the word, | 1070 | /* If the word starts with `~' and there is no slash in the word, |
574 | * then try completing this word as a username. */ | 1071 | * then try completing this word as a username. */ |
575 | 1072 | ||
576 | if (matchBuf[0]=='~' && strchr(matchBuf, '/')==0) { | 1073 | if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0) |
577 | matches = username_tab_completion(matchBuf, &num_matches); | 1074 | matches = username_tab_completion(matchBuf, &num_matches); |
578 | } | ||
579 | #endif | 1075 | #endif |
580 | /* Try to match any executable in our path and everything | 1076 | /* Try to match any executable in our path and everything |
581 | * in the current working directory that matches. */ | 1077 | * in the current working directory that matches. */ |
582 | if (!matches) | 1078 | if (!matches) |
583 | matches = exe_n_cwd_tab_completion(matchBuf, &num_matches, find_type); | 1079 | matches = |
1080 | exe_n_cwd_tab_completion(matchBuf, &num_matches, | ||
1081 | find_type); | ||
584 | 1082 | ||
585 | /* Did we find exactly one match? */ | 1083 | /* Did we find exactly one match? */ |
586 | if(!matches || num_matches>1) { | 1084 | if (!matches || num_matches > 1) { |
1085 | char *tmp1; | ||
1086 | |||
587 | beep(); | 1087 | beep(); |
588 | return; | 1088 | if (!matches) |
1089 | return; /* not found */ | ||
1090 | /* sort */ | ||
1091 | qsort(matches, num_matches, sizeof(char *), match_compare); | ||
1092 | |||
1093 | /* find minimal match */ | ||
1094 | tmp = xstrdup(matches[0]); | ||
1095 | for (tmp1 = tmp; *tmp1; tmp1++) | ||
1096 | for (len_found = 1; len_found < num_matches; len_found++) | ||
1097 | if (matches[len_found][(tmp1 - tmp)] != *tmp1) { | ||
1098 | *tmp1 = 0; | ||
1099 | break; | ||
1100 | } | ||
1101 | if (*tmp == 0) { /* have unique */ | ||
1102 | free(tmp); | ||
1103 | return; | ||
1104 | } | ||
1105 | } else { /* one match */ | ||
1106 | tmp = matches[0]; | ||
1107 | /* for next completion current found */ | ||
1108 | *lastWasTab = FALSE; | ||
589 | } | 1109 | } |
590 | 1110 | ||
591 | len_found = strlen(matches[0]); | 1111 | len_found = strlen(tmp); |
592 | |||
593 | /* have space to placed match? */ | 1112 | /* have space to placed match? */ |
594 | if ( (len_found-strlen(matchBuf)+len) < BUFSIZ ) { | 1113 | if ((len_found - strlen(matchBuf) + len) < BUFSIZ) { |
595 | 1114 | ||
596 | int recalc_pos = len; | 1115 | /* before word for match */ |
597 | 1116 | command_ps[cursor - recalc_pos] = 0; | |
598 | /* before word for match */ | 1117 | /* save tail line */ |
599 | command_ps[pos-strlen(matchBuf)]=0; | 1118 | strcpy(matchBuf, command_ps + cursor); |
600 | 1119 | /* add match */ | |
601 | /* tail line */ | 1120 | strcat(command_ps, tmp); |
602 | strcpy(matchBuf, command_ps+pos); | 1121 | /* add tail */ |
603 | |||
604 | /* add match */ | ||
605 | strcat(command_ps, matches[0]); | ||
606 | /* add tail */ | ||
607 | strcat(command_ps, matchBuf); | 1122 | strcat(command_ps, matchBuf); |
608 | 1123 | /* back to begin word for match */ | |
609 | /* write out the matched command */ | 1124 | input_backward(recalc_pos); |
610 | len=strlen(command_ps); | 1125 | /* new pos */ |
611 | recalc_pos = len-recalc_pos+pos; | 1126 | recalc_pos = cursor + len_found; |
612 | input_end(); /* write */ | 1127 | /* new len */ |
613 | while(recalc_pos<cursor) | 1128 | len = strlen(command_ps); |
614 | input_backward(); | 1129 | /* write out the matched command */ |
615 | return; | 1130 | input_end(); |
1131 | input_backward(cursor - recalc_pos); | ||
616 | } | 1132 | } |
1133 | if (tmp != matches[0]) | ||
1134 | free(tmp); | ||
617 | } else { | 1135 | } else { |
618 | /* Ok -- the last char was a TAB. Since they | 1136 | /* Ok -- the last char was a TAB. Since they |
619 | * just hit TAB again, print a list of all the | 1137 | * just hit TAB again, print a list of all the |
620 | * available choices... */ | 1138 | * available choices... */ |
621 | if ( matches && num_matches>0 ) { | 1139 | if (matches && num_matches > 0) { |
622 | int i, col; | 1140 | int i, col, l; |
623 | int sav_cursor = cursor; | 1141 | int sav_cursor = cursor; /* change goto_new_line() */ |
624 | 1142 | ||
625 | /* Go to the next line */ | 1143 | /* Go to the next line */ |
626 | goto_new_line(); | 1144 | goto_new_line(); |
627 | for (i=0,col=0; i<num_matches; i++) { | 1145 | for (i = 0, col = 0; i < num_matches; i++) { |
628 | printf("%s ", matches[i]); | 1146 | l = strlen(matches[i]); |
629 | col += strlen(matches[i])+2; | 1147 | if (l < 14) |
630 | col -= (col/cmdedit_termw)*cmdedit_termw; | 1148 | l = 14; |
631 | if (col > 60 && matches[i+1] != NULL) { | 1149 | printf("%-14s ", matches[i]); |
1150 | if ((l += 2) > 16) | ||
1151 | while (l % 16) { | ||
1152 | putchar(' '); | ||
1153 | l++; | ||
1154 | } | ||
1155 | col += l; | ||
1156 | col -= (col / cmdedit_termw) * cmdedit_termw; | ||
1157 | if (col > 60 && matches[i + 1] != NULL) { | ||
632 | putchar('\n'); | 1158 | putchar('\n'); |
633 | col = 0; | 1159 | col = 0; |
634 | } | 1160 | } |
635 | } | 1161 | } |
636 | /* Go to the next line and rewrite the prompt */ | 1162 | /* Go to the next line and rewrite */ |
637 | printf("\n%s", cmdedit_prompt); | 1163 | putchar('\n'); |
638 | cmdedit_x = cmdedit_prmt_len; | 1164 | redraw(0, len - sav_cursor); |
639 | cmdedit_y = 0; | ||
640 | cursor = 0; | ||
641 | input_end(); /* Rewrite the command */ | ||
642 | /* Put the cursor back to where it used to be */ | ||
643 | while (sav_cursor < cursor) | ||
644 | input_backward(); | ||
645 | } | 1165 | } |
646 | } | 1166 | } |
647 | } | 1167 | } |
648 | #endif | 1168 | #endif /* BB_FEATURE_SH_TAB_COMPLETION */ |
649 | 1169 | ||
650 | static void get_previous_history(struct history **hp, char* command) | 1170 | static void get_previous_history(struct history **hp, struct history *p) |
651 | { | 1171 | { |
652 | if ((*hp)->s) | 1172 | if ((*hp)->s) |
653 | free((*hp)->s); | 1173 | free((*hp)->s); |
654 | (*hp)->s = strdup(command); | 1174 | (*hp)->s = xstrdup(command_ps); |
655 | *hp = (*hp)->p; | 1175 | *hp = p; |
656 | } | 1176 | } |
657 | 1177 | ||
658 | static void get_next_history(struct history **hp, char* command) | 1178 | static inline void get_next_history(struct history **hp) |
659 | { | 1179 | { |
660 | if ((*hp)->s) | 1180 | get_previous_history(hp, (*hp)->n); |
661 | free((*hp)->s); | ||
662 | (*hp)->s = strdup(command); | ||
663 | *hp = (*hp)->n; | ||
664 | } | 1181 | } |
665 | 1182 | ||
1183 | enum { | ||
1184 | ESC = 27, | ||
1185 | DEL = 127, | ||
1186 | }; | ||
1187 | |||
1188 | |||
666 | /* | 1189 | /* |
667 | * This function is used to grab a character buffer | 1190 | * This function is used to grab a character buffer |
668 | * from the input file descriptor and allows you to | 1191 | * from the input file descriptor and allows you to |
@@ -678,95 +1201,78 @@ static void get_next_history(struct history **hp, char* command) | |||
678 | * | 1201 | * |
679 | * Furthermore, the "vi" command editing keys are not implemented. | 1202 | * Furthermore, the "vi" command editing keys are not implemented. |
680 | * | 1203 | * |
681 | * TODO: implement TAB command completion. :) | ||
682 | */ | 1204 | */ |
683 | extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | 1205 | extern void cmdedit_read_input(char *prompt, char command[BUFSIZ]) |
684 | { | 1206 | { |
685 | 1207 | ||
686 | int inputFd=fileno(stdin); | 1208 | int inputFd = fileno(stdin); |
687 | 1209 | ||
688 | int j = 0; | ||
689 | int break_out = 0; | 1210 | int break_out = 0; |
690 | int ret = 0; | ||
691 | int lastWasTab = FALSE; | 1211 | int lastWasTab = FALSE; |
692 | char c = 0; | 1212 | char c = 0; |
693 | struct history *hp = his_end; | 1213 | struct history *hp = his_end; |
694 | 1214 | ||
1215 | /* prepare before init handlers */ | ||
1216 | cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */ | ||
695 | len = 0; | 1217 | len = 0; |
696 | cursor = 0; | ||
697 | command_ps = command; | 1218 | command_ps = command; |
698 | 1219 | ||
699 | if (new_settings.c_cc[VMIN]==0) { | 1220 | if (new_settings.c_cc[VMIN] == 0) { /* first call */ |
700 | 1221 | ||
701 | getTermSettings(inputFd, (void*) &initial_settings); | 1222 | getTermSettings(inputFd, (void *) &initial_settings); |
702 | memcpy(&new_settings, &initial_settings, sizeof(struct termios)); | 1223 | memcpy(&new_settings, &initial_settings, sizeof(struct termios)); |
1224 | |||
703 | new_settings.c_cc[VMIN] = 1; | 1225 | new_settings.c_cc[VMIN] = 1; |
704 | new_settings.c_cc[VTIME] = 0; | 1226 | new_settings.c_cc[VTIME] = 0; |
705 | new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */ | 1227 | new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */ |
706 | new_settings.c_lflag &= ~ICANON; /* unbuffered input */ | 1228 | new_settings.c_lflag &= ~ICANON; /* unbuffered input */ |
707 | new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */ | 1229 | new_settings.c_lflag &= ~(ECHO | ECHOCTL | ECHONL); /* Turn off echoing */ |
708 | } | 1230 | } |
709 | setTermSettings(inputFd, (void*) &new_settings); | ||
710 | handlers_sets |= SET_RESET_TERM; | ||
711 | 1231 | ||
712 | memset(command, 0, BUFSIZ); | 1232 | command[0] = 0; |
713 | 1233 | ||
714 | cmdedit_init(); | 1234 | setTermSettings(inputFd, (void *) &new_settings); |
1235 | handlers_sets |= SET_RESET_TERM; | ||
715 | 1236 | ||
1237 | cmdedit_init(); | ||
716 | /* Print out the command prompt */ | 1238 | /* Print out the command prompt */ |
717 | cmdedit_prompt = prompt; | 1239 | parse_prompt(prompt); |
718 | cmdedit_prmt_len = strlen(prompt); | ||
719 | printf("%s", prompt); | ||
720 | cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */ | ||
721 | cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */ | ||
722 | |||
723 | 1240 | ||
724 | while (1) { | 1241 | while (1) { |
725 | 1242 | ||
726 | fflush(stdout); /* buffered out to fast */ | 1243 | fflush(stdout); /* buffered out to fast */ |
727 | 1244 | ||
728 | if ((ret = read(inputFd, &c, 1)) < 1) | 1245 | if (read(inputFd, &c, 1) < 1) |
729 | return; | 1246 | return; |
730 | //fprintf(stderr, "got a '%c' (%d)\n", c, c); | ||
731 | 1247 | ||
732 | switch (c) { | 1248 | switch (c) { |
733 | case '\n': | 1249 | case '\n': |
734 | case '\r': | 1250 | case '\r': |
735 | /* Enter */ | 1251 | /* Enter */ |
736 | *(command + len) = c; | 1252 | goto_new_line(); |
737 | len++; | ||
738 | input_end (); | ||
739 | break_out = 1; | 1253 | break_out = 1; |
740 | break; | 1254 | break; |
741 | case 1: | 1255 | case 1: |
742 | /* Control-a -- Beginning of line */ | 1256 | /* Control-a -- Beginning of line */ |
743 | input_home(); | 1257 | input_backward(cursor); |
744 | break; | 1258 | break; |
745 | case 2: | 1259 | case 2: |
746 | /* Control-b -- Move back one character */ | 1260 | /* Control-b -- Move back one character */ |
747 | input_backward(); | 1261 | input_backward(1); |
748 | break; | 1262 | break; |
749 | case 3: | 1263 | case 3: |
750 | /* Control-c -- stop gathering input */ | 1264 | /* Control-c -- stop gathering input */ |
751 | 1265 | ||
752 | /* Link into lash to reset context to 0 on ^C and such */ | 1266 | /* Link into lash to reset context to 0 on ^C and such */ |
753 | shell_context = 0; | 1267 | shell_context = 0; |
754 | 1268 | ||
755 | /* Go to the next line */ | 1269 | /* Go to the next line */ |
756 | goto_new_line(); | 1270 | goto_new_line(); |
1271 | command[0] = 0; | ||
757 | 1272 | ||
758 | #if 0 | ||
759 | /* Rewrite the prompt */ | ||
760 | printf("%s", prompt); | ||
761 | |||
762 | /* Reset the command string */ | ||
763 | memset(command, 0, BUFSIZ); | ||
764 | len = cursor = 0; | ||
765 | #endif | ||
766 | return; | 1273 | return; |
767 | |||
768 | case 4: | 1274 | case 4: |
769 | /* Control-d -- Delete one character, or exit | 1275 | /* Control-d -- Delete one character, or exit |
770 | * if the len=0 and no chars to delete */ | 1276 | * if the len=0 and no chars to delete */ |
771 | if (len == 0) { | 1277 | if (len == 0) { |
772 | printf("exit"); | 1278 | printf("exit"); |
@@ -790,13 +1296,13 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
790 | break; | 1296 | break; |
791 | case '\t': | 1297 | case '\t': |
792 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | 1298 | #ifdef BB_FEATURE_SH_TAB_COMPLETION |
793 | input_tab(lastWasTab); | 1299 | input_tab(&lastWasTab); |
794 | #endif | 1300 | #endif |
795 | break; | 1301 | break; |
796 | case 14: | 1302 | case 14: |
797 | /* Control-n -- Get next command in history */ | 1303 | /* Control-n -- Get next command in history */ |
798 | if (hp && hp->n && hp->n->s) { | 1304 | if (hp && hp->n && hp->n->s) { |
799 | get_next_history(&hp, command); | 1305 | get_next_history(&hp); |
800 | goto rewrite_line; | 1306 | goto rewrite_line; |
801 | } else { | 1307 | } else { |
802 | beep(); | 1308 | beep(); |
@@ -805,114 +1311,111 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
805 | case 16: | 1311 | case 16: |
806 | /* Control-p -- Get previous command from history */ | 1312 | /* Control-p -- Get previous command from history */ |
807 | if (hp && hp->p) { | 1313 | if (hp && hp->p) { |
808 | get_previous_history(&hp, command); | 1314 | get_previous_history(&hp, hp->p); |
809 | goto rewrite_line; | 1315 | goto rewrite_line; |
810 | } else { | 1316 | } else { |
811 | beep(); | 1317 | beep(); |
812 | } | 1318 | } |
813 | break; | 1319 | break; |
1320 | case 21: | ||
1321 | /* Control-U -- Clear line before cursor */ | ||
1322 | if (cursor) { | ||
1323 | strcpy(command, command + cursor); | ||
1324 | redraw(cmdedit_y, len -= cursor); | ||
1325 | } | ||
1326 | break; | ||
1327 | |||
814 | case ESC:{ | 1328 | case ESC:{ |
815 | /* escape sequence follows */ | 1329 | /* escape sequence follows */ |
816 | if ((ret = read(inputFd, &c, 1)) < 1) | 1330 | if (read(inputFd, &c, 1) < 1) |
1331 | return; | ||
1332 | /* different vt100 emulations */ | ||
1333 | if (c == '[' || c == 'O') { | ||
1334 | if (read(inputFd, &c, 1) < 1) | ||
817 | return; | 1335 | return; |
1336 | } | ||
1337 | switch (c) { | ||
1338 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | ||
1339 | case '\t': /* Alt-Tab */ | ||
818 | 1340 | ||
819 | if (c == '[') { /* 91 */ | 1341 | input_tab(&lastWasTab); |
820 | if ((ret = read(inputFd, &c, 1)) < 1) | 1342 | break; |
821 | return; | 1343 | #endif |
822 | 1344 | case 'A': | |
823 | switch (c) { | 1345 | /* Up Arrow -- Get previous command from history */ |
824 | case 'A': | 1346 | if (hp && hp->p) { |
825 | /* Up Arrow -- Get previous command from history */ | 1347 | get_previous_history(&hp, hp->p); |
826 | if (hp && hp->p) { | 1348 | goto rewrite_line; |
827 | get_previous_history(&hp, command); | 1349 | } else { |
828 | goto rewrite_line; | 1350 | beep(); |
829 | } else { | ||
830 | beep(); | ||
831 | } | ||
832 | break; | ||
833 | case 'B': | ||
834 | /* Down Arrow -- Get next command in history */ | ||
835 | if (hp && hp->n && hp->n->s) { | ||
836 | get_next_history(&hp, command); | ||
837 | goto rewrite_line; | ||
838 | } else { | ||
839 | beep(); | ||
840 | } | ||
841 | break; | ||
842 | |||
843 | /* Rewrite the line with the selected history item */ | ||
844 | rewrite_line: | ||
845 | /* return to begin of line */ | ||
846 | input_home (); | ||
847 | /* for next memmoves without set '\0' */ | ||
848 | memset (command, 0, BUFSIZ); | ||
849 | /* change command */ | ||
850 | strcpy (command, hp->s); | ||
851 | /* write new command */ | ||
852 | for (j=0; command[j]; j++) | ||
853 | cmdedit_set_out_char(command[j], 0); | ||
854 | ret = cursor; | ||
855 | /* erase tail if required */ | ||
856 | for (j = ret; j < len; j++) | ||
857 | cmdedit_set_out_char(' ', 0); | ||
858 | /* and backward cursor */ | ||
859 | for (j = ret; j < len; j++) | ||
860 | input_backward(); | ||
861 | len = cursor; /* set new len */ | ||
862 | break; | ||
863 | case 'C': | ||
864 | /* Right Arrow -- Move forward one character */ | ||
865 | input_forward(); | ||
866 | break; | ||
867 | case 'D': | ||
868 | /* Left Arrow -- Move back one character */ | ||
869 | input_backward(); | ||
870 | break; | ||
871 | case '3': | ||
872 | /* Delete */ | ||
873 | input_delete(); | ||
874 | break; | ||
875 | case '1': | ||
876 | /* Home (Ctrl-A) */ | ||
877 | input_home(); | ||
878 | break; | ||
879 | case '4': | ||
880 | /* End (Ctrl-E) */ | ||
881 | input_end(); | ||
882 | break; | ||
883 | default: | ||
884 | beep(); | ||
885 | } | ||
886 | if (c == '1' || c == '3' || c == '4') | ||
887 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
888 | return; /* read 126 (~) */ | ||
889 | } | 1351 | } |
890 | if (c == 'O') { | 1352 | break; |
891 | /* 79 */ | 1353 | case 'B': |
892 | if ((ret = read(inputFd, &c, 1)) < 1) | 1354 | /* Down Arrow -- Get next command in history */ |
893 | return; | 1355 | if (hp && hp->n && hp->n->s) { |
894 | switch (c) { | 1356 | get_next_history(&hp); |
895 | case 'H': | 1357 | goto rewrite_line; |
896 | /* Home (xterm) */ | 1358 | } else { |
897 | input_home(); | 1359 | beep(); |
898 | break; | ||
899 | case 'F': | ||
900 | /* End (xterm) */ | ||
901 | input_end(); | ||
902 | break; | ||
903 | default: | ||
904 | beep(); | ||
905 | } | ||
906 | } | 1360 | } |
907 | c = 0; | ||
908 | break; | 1361 | break; |
1362 | |||
1363 | /* Rewrite the line with the selected history item */ | ||
1364 | rewrite_line: | ||
1365 | /* change command */ | ||
1366 | len = strlen(strcpy(command, hp->s)); | ||
1367 | /* redraw and go to end line */ | ||
1368 | redraw(cmdedit_y, 0); | ||
1369 | break; | ||
1370 | case 'C': | ||
1371 | /* Right Arrow -- Move forward one character */ | ||
1372 | input_forward(); | ||
1373 | break; | ||
1374 | case 'D': | ||
1375 | /* Left Arrow -- Move back one character */ | ||
1376 | input_backward(1); | ||
1377 | break; | ||
1378 | case '3': | ||
1379 | /* Delete */ | ||
1380 | input_delete(); | ||
1381 | break; | ||
1382 | case '1': | ||
1383 | case 'H': | ||
1384 | /* Home (Ctrl-A) */ | ||
1385 | input_backward(cursor); | ||
1386 | break; | ||
1387 | case '4': | ||
1388 | case 'F': | ||
1389 | /* End (Ctrl-E) */ | ||
1390 | input_end(); | ||
1391 | break; | ||
1392 | default: | ||
1393 | if (!(c >= '1' && c <= '9')) | ||
1394 | c = 0; | ||
1395 | beep(); | ||
909 | } | 1396 | } |
1397 | if (c >= '1' && c <= '9') | ||
1398 | do | ||
1399 | if (read(inputFd, &c, 1) < 1) | ||
1400 | return; | ||
1401 | while (c != '~'); | ||
1402 | break; | ||
1403 | } | ||
910 | 1404 | ||
911 | default: /* If it's regular input, do the normal thing */ | 1405 | default: /* If it's regular input, do the normal thing */ |
912 | 1406 | #ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT | |
913 | if (!isprint(c)) { /* Skip non-printable characters */ | 1407 | /* Control-V -- Add non-printable symbol */ |
1408 | if (c == 22) { | ||
1409 | if (read(inputFd, &c, 1) < 1) | ||
1410 | return; | ||
1411 | if (c == 0) { | ||
1412 | beep(); | ||
1413 | break; | ||
1414 | } | ||
1415 | } else | ||
1416 | #endif | ||
1417 | if (!isprint(c)) /* Skip non-printable characters */ | ||
914 | break; | 1418 | break; |
915 | } | ||
916 | 1419 | ||
917 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ | 1420 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ |
918 | break; | 1421 | break; |
@@ -921,45 +1424,41 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
921 | 1424 | ||
922 | if (cursor == (len - 1)) { /* Append if at the end of the line */ | 1425 | if (cursor == (len - 1)) { /* Append if at the end of the line */ |
923 | *(command + cursor) = c; | 1426 | *(command + cursor) = c; |
924 | cmdedit_set_out_char(c, command[cursor+1]); | 1427 | *(command + cursor + 1) = 0; |
1428 | cmdedit_set_out_char(0); | ||
925 | } else { /* Insert otherwise */ | 1429 | } else { /* Insert otherwise */ |
926 | memmove(command + cursor + 1, command + cursor, | 1430 | int sc = cursor; |
927 | len - cursor - 1); | ||
928 | 1431 | ||
929 | *(command + cursor) = c; | 1432 | memmove(command + sc + 1, command + sc, len - sc); |
930 | j = cursor+1; | 1433 | *(command + sc) = c; |
1434 | sc++; | ||
931 | /* rewrite from cursor */ | 1435 | /* rewrite from cursor */ |
932 | input_end (); | 1436 | input_end(); |
933 | /* to prev x pos + 1 */ | 1437 | /* to prev x pos + 1 */ |
934 | while(cursor > j) | 1438 | input_backward(cursor - sc); |
935 | input_backward(); | ||
936 | } | 1439 | } |
937 | 1440 | ||
938 | break; | 1441 | break; |
939 | } | 1442 | } |
940 | if (c == '\t') | ||
941 | lastWasTab = TRUE; | ||
942 | else | ||
943 | lastWasTab = FALSE; | ||
944 | |||
945 | if (break_out) /* Enter is the command terminator, no more input. */ | 1443 | if (break_out) /* Enter is the command terminator, no more input. */ |
946 | break; | 1444 | break; |
1445 | |||
1446 | if (c != '\t') | ||
1447 | lastWasTab = FALSE; | ||
947 | } | 1448 | } |
948 | 1449 | ||
949 | setTermSettings (inputFd, (void *) &initial_settings); | 1450 | setTermSettings(inputFd, (void *) &initial_settings); |
950 | handlers_sets &= ~SET_RESET_TERM; | 1451 | handlers_sets &= ~SET_RESET_TERM; |
951 | 1452 | ||
952 | /* Handle command history log */ | 1453 | /* Handle command history log */ |
953 | if (len>1) { /* no put empty line (only '\n') */ | 1454 | if (len) { /* no put empty line */ |
954 | 1455 | ||
955 | struct history *h = his_end; | 1456 | struct history *h = his_end; |
956 | char *ss; | 1457 | char *ss; |
957 | 1458 | ||
958 | command[len-1] = 0; /* destroy end '\n' */ | 1459 | ss = xstrdup(command); /* duplicate */ |
959 | ss = strdup(command); /* duplicate without '\n' */ | ||
960 | command[len-1] = '\n'; /* restore '\n' */ | ||
961 | 1460 | ||
962 | if (!h) { | 1461 | if (h == 0) { |
963 | /* No previous history -- this memory is never freed */ | 1462 | /* No previous history -- this memory is never freed */ |
964 | h = his_front = xmalloc(sizeof(struct history)); | 1463 | h = his_front = xmalloc(sizeof(struct history)); |
965 | h->n = xmalloc(sizeof(struct history)); | 1464 | h->n = xmalloc(sizeof(struct history)); |
@@ -994,8 +1493,18 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
994 | history_counter++; | 1493 | history_counter++; |
995 | } | 1494 | } |
996 | } | 1495 | } |
1496 | #if defined(BB_FEATURE_BASH_STYLE_PROMT) | ||
1497 | num_ok_lines++; | ||
1498 | #endif | ||
997 | } | 1499 | } |
998 | 1500 | command[len++] = '\n'; /* set '\n' */ | |
1501 | command[len] = 0; | ||
1502 | #if defined(BB_FEATURE_CLEAN_UP) && defined(BB_FEATURE_SH_TAB_COMPLETION) | ||
1503 | input_tab(0); /* strong free */ | ||
1504 | #endif | ||
1505 | #if defined(BB_FEATURE_BASH_STYLE_PROMT) | ||
1506 | free(cmdedit_prompt); | ||
1507 | #endif | ||
999 | return; | 1508 | return; |
1000 | } | 1509 | } |
1001 | 1510 | ||
@@ -1004,7 +1513,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
1004 | extern void cmdedit_terminate(void) | 1513 | extern void cmdedit_terminate(void) |
1005 | { | 1514 | { |
1006 | cmdedit_reset_term(); | 1515 | cmdedit_reset_term(); |
1007 | if((handlers_sets & SET_TERM_HANDLERS)!=0) { | 1516 | if ((handlers_sets & SET_TERM_HANDLERS) != 0) { |
1008 | signal(SIGKILL, SIG_DFL); | 1517 | signal(SIGKILL, SIG_DFL); |
1009 | signal(SIGINT, SIG_DFL); | 1518 | signal(SIGINT, SIG_DFL); |
1010 | signal(SIGQUIT, SIG_DFL); | 1519 | signal(SIGQUIT, SIG_DFL); |
@@ -1014,6 +1523,32 @@ extern void cmdedit_terminate(void) | |||
1014 | } | 1523 | } |
1015 | } | 1524 | } |
1016 | 1525 | ||
1526 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ | ||
1527 | |||
1528 | |||
1529 | #ifdef TEST | ||
1530 | |||
1531 | unsigned int shell_context; | ||
1017 | 1532 | ||
1533 | int main(int argc, char **argv) | ||
1534 | { | ||
1535 | char buff[BUFSIZ]; | ||
1536 | char *prompt = | ||
1537 | #if defined(BB_FEATURE_BASH_STYLE_PROMT) | ||
1538 | "\\[\\033[32;1m\\]\\u@\\[\\033[33;1m\\]\\h:\ | ||
1539 | \\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \ | ||
1540 | \\!\\[\\033[36;1m\\]\\$ \\[\\033[0m\\]"; | ||
1541 | #else | ||
1542 | "% "; | ||
1543 | #endif | ||
1544 | |||
1545 | shell_context = 1; | ||
1546 | do { | ||
1547 | cmdedit_read_input(prompt, buff); | ||
1548 | printf("*** cmdedit_read_input() returned line =%s=\n", buff); | ||
1549 | } while (shell_context); | ||
1550 | printf("*** cmdedit_read_input() detect ^C\n"); | ||
1551 | return 0; | ||
1552 | } | ||
1018 | 1553 | ||
1019 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ | 1554 | #endif /* TEST */ |