diff options
author | Mark Whitley <markw@lineo.com> | 2001-01-26 20:42:23 +0000 |
---|---|---|
committer | Mark Whitley <markw@lineo.com> | 2001-01-26 20:42:23 +0000 |
commit | 4e33875759b3c13118ecf349bfe302e0d8374b78 (patch) | |
tree | b0976625da2bd861146fe16dff9a9dfbf50f8a83 | |
parent | a68b21a28e62a86afdbbc57b5a4694a17d7dfd1e (diff) | |
download | busybox-w32-4e33875759b3c13118ecf349bfe302e0d8374b78.tar.gz busybox-w32-4e33875759b3c13118ecf349bfe302e0d8374b78.tar.bz2 busybox-w32-4e33875759b3c13118ecf349bfe302e0d8374b78.zip |
Applied patch from Vladimir N. Oleynik <dzo@simtreas.ru> to do tab-completion
in Busybox shell. (Thanks, Vlad.)
-rw-r--r-- | Config.h | 6 | ||||
-rw-r--r-- | cmdedit.c | 756 | ||||
-rw-r--r-- | shell/cmdedit.c | 756 |
3 files changed, 1007 insertions, 511 deletions
@@ -260,7 +260,11 @@ | |||
260 | // Enable tab completion in the shell (not yet | 260 | // Enable tab completion in the shell (not yet |
261 | // working very well -- so don't turn this on) | 261 | // working very well -- so don't turn this on) |
262 | // Only relevant if BB_SH is enabled. | 262 | // Only relevant if BB_SH is enabled. |
263 | //#define BB_FEATURE_SH_TAB_COMPLETION | 263 | #define BB_FEATURE_SH_TAB_COMPLETION |
264 | // | ||
265 | // Attempts to match usernames in a ~-prefixed path | ||
266 | // XXX: Doesn't work without NSS, off by default | ||
267 | //#define BB_FEATURE_USERNAME_COMPLETION /* require NSS */ | ||
264 | // | 268 | // |
265 | //Turn on extra fbset options | 269 | //Turn on extra fbset options |
266 | //#define BB_FEATURE_FBSET_FANCY | 270 | //#define BB_FEATURE_FBSET_FANCY |
@@ -25,13 +25,20 @@ | |||
25 | need to be added. This version was created on Debian GNU/Linux 2.x. | 25 | need to be added. This version was created on Debian GNU/Linux 2.x. |
26 | Delete, Backspace, Home, End, and the arrow keys were tested | 26 | Delete, Backspace, Home, End, and the arrow keys were tested |
27 | to work in an Xterm and console. Ctrl-A also works as Home. | 27 | to work in an Xterm and console. Ctrl-A also works as Home. |
28 | Ctrl-E also works as End. The binary size increase is <3K. | 28 | Ctrl-E also works as End. |
29 | 29 | ||
30 | Editting will not display correctly for lines greater then the | 30 | |
31 | terminal width. (more then one line.) However, history will. | 31 | Editor with vertical scrolling and completion by |
32 | Vladimir Oleynik. vodz@usa.net (c) 2001 | ||
33 | |||
34 | Small bug: not true work if terminal size (x*y symbols) less | ||
35 | size (prompt + editor`s line + 2 symbols) | ||
32 | */ | 36 | */ |
33 | 37 | ||
38 | |||
39 | |||
34 | #include "busybox.h" | 40 | #include "busybox.h" |
41 | |||
35 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | 42 | #ifdef BB_FEATURE_SH_COMMAND_EDITING |
36 | 43 | ||
37 | #include <stdio.h> | 44 | #include <stdio.h> |
@@ -43,6 +50,13 @@ | |||
43 | #include <ctype.h> | 50 | #include <ctype.h> |
44 | #include <signal.h> | 51 | #include <signal.h> |
45 | 52 | ||
53 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | ||
54 | #include <sys/stat.h> | ||
55 | #endif | ||
56 | |||
57 | #ifdef BB_FEATURE_USERNAME_COMPLETION | ||
58 | #include <pwd.h> | ||
59 | #endif | ||
46 | 60 | ||
47 | static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */ | 61 | static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */ |
48 | 62 | ||
@@ -58,7 +72,6 @@ static struct history *his_front = NULL; /* First element in command line list * | |||
58 | static struct history *his_end = NULL; /* Last element in command line list */ | 72 | static struct history *his_end = NULL; /* Last element in command line list */ |
59 | 73 | ||
60 | /* ED: sparc termios is broken: revert back to old termio handling. */ | 74 | /* ED: sparc termios is broken: revert back to old termio handling. */ |
61 | #ifdef BB_FEATURE_USE_TERMIOS | ||
62 | 75 | ||
63 | #if #cpu(sparc) | 76 | #if #cpu(sparc) |
64 | # include <termio.h> | 77 | # include <termio.h> |
@@ -79,16 +92,35 @@ static struct termios initial_settings, new_settings; | |||
79 | #define _POSIX_VDISABLE '\0' | 92 | #define _POSIX_VDISABLE '\0' |
80 | #endif | 93 | #endif |
81 | 94 | ||
82 | #endif | ||
83 | 95 | ||
96 | static | ||
97 | volatile int cmdedit_termw; /* actual terminal width */ | ||
98 | static int history_counter = 0; /* Number of commands in history list */ | ||
84 | 99 | ||
100 | static | ||
101 | volatile int handlers_sets = 0; /* Set next bites | ||
102 | when atexit() has been called | ||
103 | and set many "terminates" signal handlers | ||
104 | and winchg signal handler | ||
105 | and if the terminal needs to be reset upon exit | ||
106 | */ | ||
107 | enum { | ||
108 | SET_ATEXIT = 1, | ||
109 | SET_TERM_HANDLERS = 2, | ||
110 | SET_WCHG_HANDLERS = 4, | ||
111 | SET_RESET_TERM = 8, | ||
112 | }; | ||
85 | 113 | ||
86 | static int cmdedit_termw = 80; /* actual terminal width */ | ||
87 | static int cmdedit_scroll = 27; /* width of EOL scrolling region */ | ||
88 | static int history_counter = 0; /* Number of commands in history list */ | ||
89 | static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ | ||
90 | static int exithandler_set = 0; /* Set to true when atexit() has been called */ | ||
91 | 114 | ||
115 | static int cmdedit_x; /* real x terminal position, | ||
116 | require put prompt in start x position */ | ||
117 | static int cmdedit_y; /* pseudoreal y terminal position */ | ||
118 | static int cmdedit_prmt_len; /* for fast running, without duplicate calculate */ | ||
119 | |||
120 | static int cursor; /* required global for signal handler */ | ||
121 | static int len; /* --- "" - - "" - -"- --""-- --""--- */ | ||
122 | static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */ | ||
123 | static const char *cmdedit_prompt;/* --- "" - - "" - -"- --""-- --""--- */ | ||
92 | 124 | ||
93 | /* Link into lash to reset context to 0 | 125 | /* Link into lash to reset context to 0 |
94 | * on ^C and such */ | 126 | * on ^C and such */ |
@@ -101,38 +133,43 @@ struct history { | |||
101 | struct history *n; | 133 | struct history *n; |
102 | }; | 134 | }; |
103 | 135 | ||
104 | #define xwrite write | 136 | static void cmdedit_setwidth(int w, int redraw_flg); |
105 | 137 | ||
106 | /* | 138 | static void win_changed(int nsig) |
107 | * TODO: Someday we want to implement 'horizontal scrolling' of the | ||
108 | * command-line when the user has typed more than the current width. This | ||
109 | * would allow the user to see a 'window' of what he has typed. | ||
110 | */ | ||
111 | static void cmdedit_setwidth(int w) | ||
112 | { | ||
113 | if (w > 20) { | ||
114 | cmdedit_termw = w; | ||
115 | cmdedit_scroll = w / 3; | ||
116 | } else { | ||
117 | error_msg("\n*** Error: minimum screen width is 21\n"); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | static void win_changed(int junk) | ||
122 | { | 139 | { |
123 | struct winsize win = { 0, 0, 0, 0 }; | 140 | struct winsize win = { 0, 0, 0, 0 }; |
124 | ioctl(0, TIOCGWINSZ, &win); | 141 | static __sighandler_t previous_SIGWINCH_handler; /* for reset */ |
125 | if (win.ws_col > 0) { | 142 | |
126 | cmdedit_setwidth( win.ws_col - 1); | 143 | /* emulate || signal call */ |
144 | if(nsig == -SIGWINCH || nsig == SIGWINCH) { | ||
145 | ioctl(0, TIOCGWINSZ, &win); | ||
146 | if (win.ws_col > 0) { | ||
147 | cmdedit_setwidth( win.ws_col, nsig == SIGWINCH ); | ||
148 | } | ||
127 | } | 149 | } |
150 | /* Unix not all standart in recall signal */ | ||
151 | |||
152 | if(nsig == -SIGWINCH) /* save previous handler */ | ||
153 | previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); | ||
154 | else if(nsig == SIGWINCH) /* signaled called handler */ | ||
155 | signal(SIGWINCH, win_changed); /* set for next call */ | ||
156 | else /* set previous handler */ | ||
157 | signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */ | ||
128 | } | 158 | } |
129 | 159 | ||
130 | |||
131 | static void cmdedit_reset_term(void) | 160 | static void cmdedit_reset_term(void) |
132 | { | 161 | { |
133 | if (reset_term) | 162 | if((handlers_sets & SET_RESET_TERM)!=0) { |
134 | /* sparc and other have broken termios support: use old termio handling. */ | 163 | /* sparc and other have broken termios support: use old termio handling. */ |
135 | setTermSettings(fileno(stdin), (void*) &initial_settings); | 164 | setTermSettings(fileno(stdin), (void*) &initial_settings); |
165 | handlers_sets &= ~SET_RESET_TERM; | ||
166 | } | ||
167 | if((handlers_sets & SET_WCHG_HANDLERS)!=0) { | ||
168 | /* reset SIGWINCH handler to previous (default) */ | ||
169 | win_changed(0); | ||
170 | handlers_sets &= ~SET_WCHG_HANDLERS; | ||
171 | } | ||
172 | fflush(stdout); | ||
136 | #ifdef BB_FEATURE_CLEAN_UP | 173 | #ifdef BB_FEATURE_CLEAN_UP |
137 | if (his_front) { | 174 | if (his_front) { |
138 | struct history *n; | 175 | struct history *n; |
@@ -147,169 +184,348 @@ static void cmdedit_reset_term(void) | |||
147 | #endif | 184 | #endif |
148 | } | 185 | } |
149 | 186 | ||
150 | static void clean_up_and_die(int sig) | ||
151 | { | ||
152 | cmdedit_reset_term(); | ||
153 | printf("\n"); | ||
154 | if (sig!=SIGINT) | ||
155 | exit(EXIT_SUCCESS); | ||
156 | } | ||
157 | 187 | ||
158 | /* Go to HOME position */ | ||
159 | static void input_home(int outputFd, int *cursor) | ||
160 | { | ||
161 | while (*cursor > 0) { | ||
162 | xwrite(outputFd, "\b", 1); | ||
163 | --*cursor; | ||
164 | } | ||
165 | } | ||
166 | 188 | ||
167 | /* Go to END position */ | 189 | /* special for recount position for scroll and remove terminal margin effect */ |
168 | static void input_end(int outputFd, int *cursor, int len) | 190 | static void cmdedit_set_out_char(int c, int next_char) { |
169 | { | 191 | putchar(c); |
170 | while (*cursor < len) { | 192 | if(++cmdedit_x>=cmdedit_termw) { |
171 | xwrite(outputFd, "\033[C", 3); | 193 | /* terminal is scrolled down */ |
172 | ++*cursor; | 194 | cmdedit_y++; |
195 | cmdedit_x=0; | ||
196 | |||
197 | if(!next_char) | ||
198 | next_char = ' '; | ||
199 | /* destroy "(auto)margin" */ | ||
200 | putchar(next_char); | ||
201 | putchar('\b'); | ||
173 | } | 202 | } |
203 | cursor++; | ||
174 | } | 204 | } |
175 | 205 | ||
176 | /* Delete the char in back of the cursor */ | 206 | /* Move to end line. Bonus: rewrite line from cursor without use |
177 | static void input_backspace(char* command, int outputFd, int *cursor, int *len) | 207 | special control terminal strings, also saved size and speed! */ |
178 | { | 208 | static void input_end (void) { |
179 | int j = 0; | 209 | while(cursor < len) |
180 | 210 | cmdedit_set_out_char(command_ps[cursor], 0); | |
181 | /* Debug crap */ | 211 | } |
182 | //fprintf(stderr, "\nerik: len=%d, cursor=%d, strlen(command)='%d'\n", *len, *cursor, strlen(command)); | ||
183 | //xwrite(outputFd, command, *len); | ||
184 | //*cursor = *len; | ||
185 | 212 | ||
213 | /* Go to the next line */ | ||
214 | static void goto_new_line(void) { | ||
215 | input_end(); | ||
216 | cmdedit_set_out_char('\n', 0); | ||
217 | } | ||
186 | 218 | ||
187 | if (*cursor > 0) { | ||
188 | xwrite(outputFd, "\b \b", 3); | ||
189 | --*cursor; | ||
190 | memmove(command + *cursor, command + *cursor + 1, | ||
191 | BUFSIZ - *cursor + 1); | ||
192 | 219 | ||
193 | for (j = *cursor; j < (BUFSIZ - 1); j++) { | 220 | static inline void out1str(const char *s) { fputs (s, stdout); } |
194 | if (!*(command + j)) | 221 | static inline void beep (void) { putchar('\007'); } |
195 | break; | ||
196 | else | ||
197 | xwrite(outputFd, (command + j), 1); | ||
198 | } | ||
199 | 222 | ||
200 | xwrite(outputFd, " \b", 2); | 223 | /* Go to HOME position */ |
224 | static void input_home(void) | ||
225 | { | ||
226 | while(cmdedit_y>0) { /* up to start y */ | ||
227 | out1str("\033[A"); | ||
228 | cmdedit_y--; | ||
229 | } | ||
230 | putchar('\r'); | ||
231 | cursor = 0; | ||
232 | out1str(cmdedit_prompt); | ||
233 | cmdedit_x = cmdedit_prmt_len; | ||
201 | 234 | ||
202 | while (j-- > *cursor) | 235 | } |
203 | xwrite(outputFd, "\b", 1); | ||
204 | 236 | ||
205 | --*len; | 237 | /* Move back one charactor */ |
238 | static void input_backward(void) { | ||
239 | if (cursor > 0) { | ||
240 | cursor--; | ||
241 | if(cmdedit_x!=0) { /* no first position in terminal line */ | ||
242 | putchar('\b'); | ||
243 | cmdedit_x--; | ||
244 | } | ||
245 | else { | ||
246 | out1str("\033[A"); /* up */ | ||
247 | cmdedit_y--; | ||
248 | |||
249 | /* to end in current terminal line */ | ||
250 | while(cmdedit_x<(cmdedit_termw-1)) { | ||
251 | out1str("\033[C"); | ||
252 | cmdedit_x++; | ||
253 | } | ||
254 | } | ||
206 | } | 255 | } |
207 | } | 256 | } |
208 | 257 | ||
209 | /* Delete the char in front of the cursor */ | 258 | /* Delete the char in front of the cursor */ |
210 | static void input_delete(char* command, int outputFd, int cursor, int *len) | 259 | static void input_delete(void) |
211 | { | 260 | { |
212 | int j = 0; | 261 | int j = cursor; |
213 | 262 | ||
214 | if (cursor == *len) | 263 | if (j == len) |
215 | return; | 264 | return; |
216 | 265 | ||
217 | memmove(command + cursor, command + cursor + 1, | 266 | memmove (command_ps + j, command_ps + j + 1, BUFSIZ - j - 1); |
218 | BUFSIZ - cursor - 1); | 267 | len--; |
219 | for (j = cursor; j < (BUFSIZ - 1); j++) { | 268 | input_end(); /* rewtite new line */ |
220 | if (!*(command + j)) | 269 | cmdedit_set_out_char(' ', 0); /* destroy end char */ |
221 | break; | 270 | while (j < cursor) |
222 | else | 271 | input_backward(); /* back to old pos cursor */ |
223 | xwrite(outputFd, (command + j), 1); | 272 | } |
273 | |||
274 | /* Delete the char in back of the cursor */ | ||
275 | static void input_backspace(void) | ||
276 | { | ||
277 | if (cursor > 0) { | ||
278 | input_backward(); | ||
279 | input_delete (); | ||
224 | } | 280 | } |
281 | } | ||
225 | 282 | ||
226 | xwrite(outputFd, " \b", 2); | ||
227 | 283 | ||
228 | while (j-- > cursor) | 284 | /* Move forward one charactor */ |
229 | xwrite(outputFd, "\b", 1); | 285 | static void input_forward(void) |
230 | --*len; | 286 | { |
287 | if (cursor < len) | ||
288 | cmdedit_set_out_char(command_ps[cursor], command_ps[cursor + 1]); | ||
231 | } | 289 | } |
232 | 290 | ||
233 | /* Move forward one charactor */ | 291 | |
234 | static void input_forward(int outputFd, int *cursor, int len) | 292 | static void clean_up_and_die(int sig) |
235 | { | 293 | { |
236 | if (*cursor < len) { | 294 | goto_new_line(); |
237 | xwrite(outputFd, "\033[C", 3); | 295 | if (sig!=SIGINT) |
238 | ++*cursor; | 296 | exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */ |
239 | } | 297 | cmdedit_reset_term(); |
240 | } | 298 | } |
241 | 299 | ||
242 | /* Move back one charactor */ | 300 | static void cmdedit_setwidth(int w, int redraw_flg) |
243 | static void input_backward(int outputFd, int *cursor) | ||
244 | { | 301 | { |
245 | if (*cursor > 0) { | 302 | cmdedit_termw = cmdedit_prmt_len+2; |
246 | xwrite(outputFd, "\033[D", 3); | 303 | if (w > cmdedit_termw) { |
247 | --*cursor; | 304 | |
305 | cmdedit_termw = w; | ||
306 | |||
307 | if(redraw_flg) { | ||
308 | int sav_cursor = cursor; | ||
309 | |||
310 | /* set variables for new terminal size */ | ||
311 | cmdedit_y = sav_cursor/w; | ||
312 | cmdedit_x = sav_cursor-cmdedit_y*w; | ||
313 | |||
314 | /* redraw */ | ||
315 | input_home(); | ||
316 | input_end(); | ||
317 | while(sav_cursor<cursor) | ||
318 | input_backward(); | ||
319 | } | ||
320 | } else { | ||
321 | error_msg("\n*** Error: minimum screen width is %d\n", cmdedit_termw); | ||
248 | } | 322 | } |
249 | } | 323 | } |
250 | 324 | ||
325 | extern void cmdedit_init(void) | ||
326 | { | ||
327 | if((handlers_sets & SET_WCHG_HANDLERS)==0) { | ||
328 | /* emulate usage handler to set handler and call yours work */ | ||
329 | win_changed(-SIGWINCH); | ||
330 | handlers_sets |= SET_WCHG_HANDLERS; | ||
331 | } | ||
251 | 332 | ||
333 | if((handlers_sets & SET_ATEXIT)==0) { | ||
334 | atexit(cmdedit_reset_term); /* be sure to do this only once */ | ||
335 | handlers_sets |= SET_ATEXIT; | ||
336 | } | ||
337 | if((handlers_sets & SET_TERM_HANDLERS)==0) { | ||
338 | signal(SIGKILL, clean_up_and_die); | ||
339 | signal(SIGINT, clean_up_and_die); | ||
340 | signal(SIGQUIT, clean_up_and_die); | ||
341 | signal(SIGTERM, clean_up_and_die); | ||
342 | handlers_sets |= SET_TERM_HANDLERS; | ||
343 | } | ||
344 | } | ||
252 | 345 | ||
253 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | 346 | #ifdef BB_FEATURE_SH_TAB_COMPLETION |
254 | static char** username_tab_completion(char* command, int *num_matches) | 347 | |
348 | #ifdef BB_FEATURE_USERNAME_COMPLETION | ||
349 | static char** username_tab_completion(char *ud, int *num_matches) | ||
255 | { | 350 | { |
351 | static struct passwd *entry; | ||
352 | int userlen; | ||
256 | char **matches = (char **) NULL; | 353 | char **matches = (char **) NULL; |
257 | *num_matches=0; | 354 | char *temp; |
258 | fprintf(stderr, "\nin username_tab_completion\n"); | 355 | int nm = 0; |
356 | |||
357 | setpwent (); | ||
358 | userlen = strlen (ud + 1); | ||
359 | |||
360 | while ((entry = getpwent ()) != NULL) { | ||
361 | /* Null usernames should result in all users as possible completions. */ | ||
362 | if (!userlen || !strncmp (ud + 1, entry->pw_name, userlen)) { | ||
363 | |||
364 | temp = xmalloc (3 + strlen (entry->pw_name)); | ||
365 | sprintf(temp, "~%s/", entry->pw_name); | ||
366 | |||
367 | matches = xrealloc(matches, (nm+1)*sizeof(char *)); | ||
368 | matches[nm++] = temp; | ||
369 | } | ||
370 | } | ||
371 | |||
372 | endpwent (); | ||
373 | (*num_matches) = nm; | ||
259 | return (matches); | 374 | return (matches); |
260 | } | 375 | } |
376 | #endif | ||
377 | |||
378 | enum { | ||
379 | FIND_EXE_ONLY = 0, | ||
380 | FIND_DIR_ONLY = 1, | ||
381 | FIND_FILE_ONLY = 2, | ||
382 | }; | ||
261 | 383 | ||
262 | #include <dirent.h> | 384 | #include <dirent.h> |
263 | static char** exe_n_cwd_tab_completion(char* command, int *num_matches) | 385 | |
386 | static int path_parse(char ***p, int flags) | ||
387 | { | ||
388 | int npth; | ||
389 | char *tmp; | ||
390 | char *pth; | ||
391 | |||
392 | if(flags!=FIND_EXE_ONLY || (pth=getenv("PATH"))==0) { | ||
393 | /* if not setenv PATH variable, to search cur dir "." */ | ||
394 | (*p) = xmalloc(sizeof(char *)); | ||
395 | (*p)[0] = xstrdup("."); | ||
396 | return 1; | ||
397 | } | ||
398 | |||
399 | tmp = pth; | ||
400 | npth=0; | ||
401 | |||
402 | for(;;) { | ||
403 | npth++; /* count words is + 1 count ':' */ | ||
404 | tmp = strchr(tmp, ':'); | ||
405 | if(tmp) | ||
406 | tmp++; | ||
407 | else | ||
408 | break; | ||
409 | } | ||
410 | |||
411 | *p = xmalloc(npth*sizeof(char *)); | ||
412 | |||
413 | tmp = pth; | ||
414 | (*p)[0] = xstrdup(tmp); | ||
415 | npth=1; /* count words is + 1 count ':' */ | ||
416 | |||
417 | for(;;) { | ||
418 | tmp = strchr(tmp, ':'); | ||
419 | if(tmp) { | ||
420 | (*p)[0][(tmp-pth)]=0; /* ':' -> '\0'*/ | ||
421 | tmp++; | ||
422 | } else | ||
423 | break; | ||
424 | (*p)[npth++] = &(*p)[0][(tmp-pth)]; /* p[next]=p[0][&'\0'+1] */ | ||
425 | } | ||
426 | |||
427 | return npth; | ||
428 | } | ||
429 | |||
430 | static char** exe_n_cwd_tab_completion(char* command, int *num_matches, int type) | ||
264 | { | 431 | { |
265 | char *dirName; | 432 | char *dirName; |
266 | char **matches; | 433 | char **matches = 0; |
267 | DIR *dir; | 434 | DIR *dir; |
268 | struct dirent *next; | 435 | struct dirent *next; |
436 | char cmd [BUFSIZ+4]; | ||
437 | char *dirbuf; | ||
438 | char found [BUFSIZ+4]; | ||
439 | int nm = *num_matches; | ||
440 | struct stat st; | ||
441 | char **paths; | ||
442 | int npaths; | ||
443 | int i; | ||
444 | char full_pth[BUFSIZ+4+PATH_MAX]; | ||
445 | |||
446 | |||
447 | strcpy(cmd, command); /* save for change (last '/' to '\0') */ | ||
448 | |||
449 | dirName = strrchr(cmd, '/'); | ||
450 | if(dirName==NULL) { | ||
451 | /* no dir, if flags==EXE_ONLY - get paths, else "." */ | ||
452 | npaths = path_parse(&paths, type); | ||
453 | if(npaths==0) | ||
454 | return 0; | ||
455 | } else { | ||
456 | /* with dir */ | ||
457 | |||
458 | /* save dir */ | ||
459 | dirbuf = xstrdup(cmd); | ||
460 | /* set only dirname */ | ||
461 | dirbuf[(dirName-cmd)+1]=0; | ||
462 | |||
463 | /* strip dirname in cmd */ | ||
464 | strcpy(cmd, dirName+1); | ||
269 | 465 | ||
270 | matches = xmalloc( sizeof(char*)*50); | 466 | paths = xmalloc(sizeof(char*)); |
467 | paths[0] = dirbuf; | ||
468 | npaths = 1; /* only 1 dir */ | ||
469 | } | ||
271 | 470 | ||
272 | /* Stick a wildcard onto the command, for later use */ | 471 | for(i=0; i < npaths; i++) { |
273 | strcat( command, "*"); | ||
274 | 472 | ||
275 | /* Now wall the current directory */ | 473 | dir = opendir(paths[i]); |
276 | dirName = get_current_dir_name(); | ||
277 | dir = opendir(dirName); | ||
278 | if (!dir) { | 474 | if (!dir) { |
279 | /* Don't print an error, just shut up and return */ | 475 | /* Don't print an error, just shut up and return */ |
280 | *num_matches=0; | ||
281 | return (matches); | 476 | return (matches); |
282 | } | 477 | } |
283 | while ((next = readdir(dir)) != NULL) { | 478 | while ((next = readdir(dir)) != NULL) { |
284 | 479 | /* matched ? */ | |
285 | /* Some quick sanity checks */ | 480 | if(strncmp(next->d_name, cmd, strlen(cmd))) |
286 | if ((strcmp(next->d_name, "..") == 0) | 481 | continue; |
287 | || (strcmp(next->d_name, ".") == 0)) { | 482 | /* not see .name without .match */ |
483 | if(*next->d_name == '.' && *cmd != '.') | ||
484 | continue; | ||
485 | sprintf(full_pth, "%s/%s", paths[i], next->d_name); | ||
486 | /* hmm, remover in progress? */ | ||
487 | if(stat(full_pth, &st)<0) | ||
488 | continue; | ||
489 | /* Cool, found a match. */ | ||
490 | if (S_ISDIR(st.st_mode)) { | ||
491 | /* name is directory */ | ||
492 | strcpy(found, next->d_name); | ||
493 | strcat(found, "/"); | ||
494 | if(type==FIND_DIR_ONLY) | ||
495 | strcat(found, " "); | ||
496 | } else { | ||
497 | /* not put found file if search only dirs for cd */ | ||
498 | if(type==FIND_DIR_ONLY) | ||
288 | continue; | 499 | continue; |
500 | strcpy(found, next->d_name); | ||
501 | strcat(found, " "); | ||
289 | } | 502 | } |
290 | /* See if this matches */ | 503 | /* Add it to the list */ |
291 | if (check_wildcard_match(next->d_name, command) == TRUE) { | 504 | matches = xrealloc(matches, (nm+1)*sizeof(char *)); |
292 | /* Cool, found a match. Add it to the list */ | 505 | matches[nm++] = xstrdup(found); |
293 | matches[*num_matches] = xmalloc(strlen(next->d_name)+1); | ||
294 | strcpy( matches[*num_matches], next->d_name); | ||
295 | ++*num_matches; | ||
296 | //matches = realloc( matches, sizeof(char*)*(*num_matches)); | ||
297 | } | 506 | } |
298 | } | 507 | } |
299 | 508 | free(paths[0]); /* allocate memory only in first member */ | |
509 | free(paths); | ||
510 | *num_matches = nm; | ||
300 | return (matches); | 511 | return (matches); |
301 | } | 512 | } |
302 | 513 | ||
303 | static void input_tab(char* command, char* prompt, int outputFd, int *cursor, int *len, int lastWasTab) | 514 | static void input_tab(int lastWasTab) |
304 | { | 515 | { |
305 | /* Do TAB completion */ | 516 | /* Do TAB completion */ |
306 | static int num_matches=0; | 517 | static int num_matches; |
307 | static char **matches = (char **) NULL; | 518 | static char **matches; |
308 | int pos = *cursor; | 519 | |
520 | char matchBuf[BUFSIZ]; | ||
521 | |||
522 | int pos = cursor; | ||
523 | int find_type=FIND_FILE_ONLY; | ||
309 | 524 | ||
310 | 525 | ||
311 | if (lastWasTab == FALSE) { | 526 | if (lastWasTab == FALSE) { |
312 | char *tmp, *tmp1, *matchBuf; | 527 | char *tmp, *tmp1; |
528 | int len_found; | ||
313 | 529 | ||
314 | /* For now, we will not bother with trying to distinguish | 530 | /* For now, we will not bother with trying to distinguish |
315 | * whether the cursor is in/at a command extression -- we | 531 | * whether the cursor is in/at a command extression -- we |
@@ -319,50 +535,80 @@ static void input_tab(char* command, char* prompt, int outputFd, int *cursor, in | |||
319 | 535 | ||
320 | /* Make a local copy of the string -- up | 536 | /* Make a local copy of the string -- up |
321 | * to the position of the cursor */ | 537 | * to the position of the cursor */ |
322 | matchBuf = (char *) xcalloc(BUFSIZ, sizeof(char)); | 538 | memset(matchBuf, 0, BUFSIZ); |
323 | strncpy(matchBuf, command, *cursor); | 539 | tmp = strncpy(matchBuf, command_ps, cursor); |
324 | tmp=matchBuf; | ||
325 | 540 | ||
326 | /* skip past any command seperator tokens */ | 541 | /* skip past any command seperator tokens */ |
327 | while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { | 542 | while ( (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { |
328 | tmp=++tmp1; | 543 | tmp = ++tmp1; |
329 | /* skip any leading white space */ | ||
330 | while (*tmp && isspace(*tmp)) | ||
331 | ++tmp; | ||
332 | } | 544 | } |
333 | 545 | ||
334 | /* skip any leading white space */ | 546 | /* skip any leading white space */ |
335 | while (*tmp && isspace(*tmp)) | 547 | while (*tmp == ' ') |
336 | ++tmp; | 548 | tmp++; |
549 | |||
550 | if(strncmp(tmp, "cd ", 3)==0) | ||
551 | find_type = FIND_DIR_ONLY; | ||
552 | else if(strchr(tmp, ' ')==NULL) | ||
553 | find_type = FIND_EXE_ONLY; | ||
554 | |||
555 | /* find begin curent word */ | ||
556 | if( (tmp1=strrchr(tmp, ' ')) != NULL) { | ||
557 | tmp = ++tmp1; | ||
558 | } | ||
559 | strcpy(matchBuf, tmp); | ||
337 | 560 | ||
338 | /* Free up any memory already allocated */ | 561 | /* Free up any memory already allocated */ |
339 | if (matches) { | 562 | if (matches) { |
563 | while(num_matches>0) | ||
564 | free(matches[--num_matches]); | ||
340 | free(matches); | 565 | free(matches); |
341 | matches = (char **) NULL; | 566 | matches = (char **) NULL; |
342 | } | 567 | } |
343 | 568 | ||
569 | #ifdef BB_FEATURE_USERNAME_COMPLETION | ||
344 | /* If the word starts with `~' and there is no slash in the word, | 570 | /* If the word starts with `~' and there is no slash in the word, |
345 | * then try completing this word as a username. */ | 571 | * then try completing this word as a username. */ |
346 | 572 | ||
347 | /* FIXME -- this check is broken! */ | 573 | if (matchBuf[0]=='~' && strchr(matchBuf, '/')==0) { |
348 | if (*tmp == '~' && !strchr(tmp, '/')) | 574 | matches = username_tab_completion(matchBuf, &num_matches); |
349 | matches = username_tab_completion(tmp, &num_matches); | 575 | } |
350 | 576 | #endif | |
351 | /* Try to match any executable in our path and everything | 577 | /* Try to match any executable in our path and everything |
352 | * in the current working directory that matches. */ | 578 | * in the current working directory that matches. */ |
353 | if (!matches) | 579 | if (!matches) |
354 | matches = exe_n_cwd_tab_completion(tmp, &num_matches); | 580 | matches = exe_n_cwd_tab_completion(matchBuf, &num_matches, find_type); |
355 | |||
356 | /* Don't leak memory */ | ||
357 | free( matchBuf); | ||
358 | 581 | ||
359 | /* Did we find exactly one match? */ | 582 | /* Did we find exactly one match? */ |
360 | if (matches && num_matches==1) { | 583 | if(!matches || num_matches>1) { |
584 | beep(); | ||
585 | return; | ||
586 | } | ||
587 | |||
588 | len_found = strlen(matches[0]); | ||
589 | |||
590 | /* have space to placed match? */ | ||
591 | if ( (len_found-strlen(matchBuf)+len) < BUFSIZ ) { | ||
592 | |||
593 | int recalc_pos = len; | ||
594 | |||
595 | /* before word for match */ | ||
596 | command_ps[pos-strlen(matchBuf)]=0; | ||
597 | |||
598 | /* tail line */ | ||
599 | strcpy(matchBuf, command_ps+pos); | ||
600 | |||
601 | /* add match */ | ||
602 | strcat(command_ps, matches[0]); | ||
603 | /* add tail */ | ||
604 | strcat(command_ps, matchBuf); | ||
605 | |||
361 | /* write out the matched command */ | 606 | /* write out the matched command */ |
362 | strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos); | 607 | len=strlen(command_ps); |
363 | *len=strlen(command); | 608 | recalc_pos = len-recalc_pos+pos; |
364 | *cursor=*len; | 609 | input_end(); /* write */ |
365 | xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos); | 610 | while(recalc_pos<cursor) |
611 | input_backward(); | ||
366 | return; | 612 | return; |
367 | } | 613 | } |
368 | } else { | 614 | } else { |
@@ -371,28 +617,28 @@ static void input_tab(char* command, char* prompt, int outputFd, int *cursor, in | |||
371 | * available choices... */ | 617 | * available choices... */ |
372 | if ( matches && num_matches>0 ) { | 618 | if ( matches && num_matches>0 ) { |
373 | int i, col; | 619 | int i, col; |
620 | int sav_cursor = cursor; | ||
374 | 621 | ||
375 | /* Go to the next line */ | 622 | /* Go to the next line */ |
376 | xwrite(outputFd, "\n", 1); | 623 | goto_new_line(); |
377 | /* Print the list of matches */ | ||
378 | for (i=0,col=0; i<num_matches; i++) { | 624 | for (i=0,col=0; i<num_matches; i++) { |
379 | char foo[17]; | 625 | printf("%s ", matches[i]); |
380 | sprintf(foo, "%-14s ", matches[i]); | 626 | col += strlen(matches[i])+2; |
381 | col += xwrite(outputFd, foo, strlen(foo)); | 627 | col -= (col/cmdedit_termw)*cmdedit_termw; |
382 | if (col > 60 && matches[i+1] != NULL) { | 628 | if (col > 60 && matches[i+1] != NULL) { |
383 | xwrite(outputFd, "\n", 1); | 629 | putchar('\n'); |
384 | col = 0; | 630 | col = 0; |
385 | } | 631 | } |
386 | } | 632 | } |
387 | /* Go to the next line */ | 633 | /* Go to the next line and rewrite the prompt */ |
388 | xwrite(outputFd, "\n", 1); | 634 | printf("\n%s", cmdedit_prompt); |
389 | /* Rewrite the prompt */ | 635 | cmdedit_x = cmdedit_prmt_len; |
390 | xwrite(outputFd, prompt, strlen(prompt)); | 636 | cmdedit_y = 0; |
391 | /* Rewrite the command */ | 637 | cursor = 0; |
392 | xwrite(outputFd, command, *len); | 638 | input_end(); /* Rewrite the command */ |
393 | /* Put the cursor back to where it used to be */ | 639 | /* Put the cursor back to where it used to be */ |
394 | for (cursor=len; *cursor > pos; cursor--) | 640 | while (sav_cursor < cursor) |
395 | xwrite(outputFd, "\b", 1); | 641 | input_backward(); |
396 | } | 642 | } |
397 | } | 643 | } |
398 | } | 644 | } |
@@ -435,18 +681,19 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
435 | { | 681 | { |
436 | 682 | ||
437 | int inputFd=fileno(stdin); | 683 | int inputFd=fileno(stdin); |
438 | int outputFd=fileno(stdout); | 684 | |
439 | int nr = 0; | ||
440 | int len = 0; | ||
441 | int j = 0; | 685 | int j = 0; |
442 | int cursor = 0; | ||
443 | int break_out = 0; | 686 | int break_out = 0; |
444 | int ret = 0; | 687 | int ret = 0; |
445 | int lastWasTab = FALSE; | 688 | int lastWasTab = FALSE; |
446 | char c = 0; | 689 | char c = 0; |
447 | struct history *hp = his_end; | 690 | struct history *hp = his_end; |
448 | 691 | ||
449 | if (!reset_term) { | 692 | len = 0; |
693 | cursor = 0; | ||
694 | command_ps = command; | ||
695 | |||
696 | if (new_settings.c_cc[VMIN]==0) { | ||
450 | 697 | ||
451 | getTermSettings(inputFd, (void*) &initial_settings); | 698 | getTermSettings(inputFd, (void*) &initial_settings); |
452 | memcpy(&new_settings, &initial_settings, sizeof(struct termios)); | 699 | memcpy(&new_settings, &initial_settings, sizeof(struct termios)); |
@@ -455,17 +702,26 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
455 | new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */ | 702 | new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */ |
456 | new_settings.c_lflag &= ~ICANON; /* unbuffered input */ | 703 | new_settings.c_lflag &= ~ICANON; /* unbuffered input */ |
457 | new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */ | 704 | new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */ |
458 | reset_term = 1; | ||
459 | } | 705 | } |
460 | setTermSettings(inputFd, (void*) &new_settings); | 706 | setTermSettings(inputFd, (void*) &new_settings); |
707 | handlers_sets |= SET_RESET_TERM; | ||
461 | 708 | ||
462 | memset(command, 0, BUFSIZ); | 709 | memset(command, 0, BUFSIZ); |
463 | 710 | ||
711 | cmdedit_init(); | ||
712 | |||
464 | /* Print out the command prompt */ | 713 | /* Print out the command prompt */ |
465 | xwrite(outputFd, prompt, strlen(prompt)); | 714 | cmdedit_prompt = prompt; |
715 | cmdedit_prmt_len = strlen(prompt); | ||
716 | printf("%s", prompt); | ||
717 | cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */ | ||
718 | cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */ | ||
719 | |||
466 | 720 | ||
467 | while (1) { | 721 | while (1) { |
468 | 722 | ||
723 | fflush(stdout); /* buffered out to fast */ | ||
724 | |||
469 | if ((ret = read(inputFd, &c, 1)) < 1) | 725 | if ((ret = read(inputFd, &c, 1)) < 1) |
470 | return; | 726 | return; |
471 | //fprintf(stderr, "got a '%c' (%d)\n", c, c); | 727 | //fprintf(stderr, "got a '%c' (%d)\n", c, c); |
@@ -474,16 +730,18 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
474 | case '\n': | 730 | case '\n': |
475 | case '\r': | 731 | case '\r': |
476 | /* Enter */ | 732 | /* Enter */ |
477 | *(command + len++ + 1) = c; | 733 | *(command + len) = c; |
478 | xwrite(outputFd, &c, 1); | 734 | len++; |
735 | input_end (); | ||
479 | break_out = 1; | 736 | break_out = 1; |
480 | break; | 737 | break; |
481 | case 1: | 738 | case 1: |
482 | /* Control-a -- Beginning of line */ | 739 | /* Control-a -- Beginning of line */ |
483 | input_home(outputFd, &cursor); | 740 | input_home(); |
741 | break; | ||
484 | case 2: | 742 | case 2: |
485 | /* Control-b -- Move back one character */ | 743 | /* Control-b -- Move back one character */ |
486 | input_backward(outputFd, &cursor); | 744 | input_backward(); |
487 | break; | 745 | break; |
488 | case 3: | 746 | case 3: |
489 | /* Control-c -- stop gathering input */ | 747 | /* Control-c -- stop gathering input */ |
@@ -492,11 +750,11 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
492 | shell_context = 0; | 750 | shell_context = 0; |
493 | 751 | ||
494 | /* Go to the next line */ | 752 | /* Go to the next line */ |
495 | xwrite(outputFd, "\n", 1); | 753 | goto_new_line(); |
496 | 754 | ||
497 | #if 0 | 755 | #if 0 |
498 | /* Rewrite the prompt */ | 756 | /* Rewrite the prompt */ |
499 | xwrite(outputFd, prompt, strlen(prompt)); | 757 | printf("%s", prompt); |
500 | 758 | ||
501 | /* Reset the command string */ | 759 | /* Reset the command string */ |
502 | memset(command, 0, BUFSIZ); | 760 | memset(command, 0, BUFSIZ); |
@@ -508,29 +766,28 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
508 | /* Control-d -- Delete one character, or exit | 766 | /* Control-d -- Delete one character, or exit |
509 | * if the len=0 and no chars to delete */ | 767 | * if the len=0 and no chars to delete */ |
510 | if (len == 0) { | 768 | if (len == 0) { |
511 | xwrite(outputFd, "exit", 4); | 769 | printf("exit"); |
512 | clean_up_and_die(0); | 770 | clean_up_and_die(0); |
513 | } else { | 771 | } else { |
514 | input_delete(command, outputFd, cursor, &len); | 772 | input_delete(); |
515 | } | 773 | } |
516 | break; | 774 | break; |
517 | case 5: | 775 | case 5: |
518 | /* Control-e -- End of line */ | 776 | /* Control-e -- End of line */ |
519 | input_end(outputFd, &cursor, len); | 777 | input_end(); |
520 | break; | 778 | break; |
521 | case 6: | 779 | case 6: |
522 | /* Control-f -- Move forward one character */ | 780 | /* Control-f -- Move forward one character */ |
523 | input_forward(outputFd, &cursor, len); | 781 | input_forward(); |
524 | break; | 782 | break; |
525 | case '\b': | 783 | case '\b': |
526 | case DEL: | 784 | case DEL: |
527 | /* Control-h and DEL */ | 785 | /* Control-h and DEL */ |
528 | input_backspace(command, outputFd, &cursor, &len); | 786 | input_backspace(); |
529 | break; | 787 | break; |
530 | case '\t': | 788 | case '\t': |
531 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | 789 | #ifdef BB_FEATURE_SH_TAB_COMPLETION |
532 | input_tab(command, prompt, outputFd, &cursor, | 790 | input_tab(lastWasTab); |
533 | &len, lastWasTab); | ||
534 | #endif | 791 | #endif |
535 | break; | 792 | break; |
536 | case 14: | 793 | case 14: |
@@ -539,7 +796,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
539 | get_next_history(&hp, command); | 796 | get_next_history(&hp, command); |
540 | goto rewrite_line; | 797 | goto rewrite_line; |
541 | } else { | 798 | } else { |
542 | xwrite(outputFd, "\007", 1); | 799 | beep(); |
543 | } | 800 | } |
544 | break; | 801 | break; |
545 | case 16: | 802 | case 16: |
@@ -548,7 +805,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
548 | get_previous_history(&hp, command); | 805 | get_previous_history(&hp, command); |
549 | goto rewrite_line; | 806 | goto rewrite_line; |
550 | } else { | 807 | } else { |
551 | xwrite(outputFd, "\007", 1); | 808 | beep(); |
552 | } | 809 | } |
553 | break; | 810 | break; |
554 | case ESC:{ | 811 | case ESC:{ |
@@ -567,7 +824,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
567 | get_previous_history(&hp, command); | 824 | get_previous_history(&hp, command); |
568 | goto rewrite_line; | 825 | goto rewrite_line; |
569 | } else { | 826 | } else { |
570 | xwrite(outputFd, "\007", 1); | 827 | beep(); |
571 | } | 828 | } |
572 | break; | 829 | break; |
573 | case 'B': | 830 | case 'B': |
@@ -576,49 +833,52 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
576 | get_next_history(&hp, command); | 833 | get_next_history(&hp, command); |
577 | goto rewrite_line; | 834 | goto rewrite_line; |
578 | } else { | 835 | } else { |
579 | xwrite(outputFd, "\007", 1); | 836 | beep(); |
580 | } | 837 | } |
581 | break; | 838 | break; |
582 | 839 | ||
583 | /* Rewrite the line with the selected history item */ | 840 | /* Rewrite the line with the selected history item */ |
584 | rewrite_line: | 841 | rewrite_line: |
585 | /* erase old command from command line */ | 842 | /* return to begin of line */ |
586 | len = strlen(command)-strlen(hp->s); | 843 | input_home (); |
587 | 844 | /* for next memmoves without set '\0' */ | |
588 | while (len>cursor) | 845 | memset (command, 0, BUFSIZ); |
589 | input_delete(command, outputFd, cursor, &len); | 846 | /* change command */ |
590 | while (cursor>0) | 847 | strcpy (command, hp->s); |
591 | input_backspace(command, outputFd, &cursor, &len); | ||
592 | input_home(outputFd, &cursor); | ||
593 | |||
594 | /* write new command */ | 848 | /* write new command */ |
595 | strcpy(command, hp->s); | 849 | for (j=0; command[j]; j++) |
596 | len = strlen(hp->s); | 850 | cmdedit_set_out_char(command[j], 0); |
597 | xwrite(outputFd, command, len); | 851 | ret = cursor; |
598 | cursor = len; | 852 | /* erase tail if required */ |
853 | for (j = ret; j < len; j++) | ||
854 | cmdedit_set_out_char(' ', 0); | ||
855 | /* and backward cursor */ | ||
856 | for (j = ret; j < len; j++) | ||
857 | input_backward(); | ||
858 | len = cursor; /* set new len */ | ||
599 | break; | 859 | break; |
600 | case 'C': | 860 | case 'C': |
601 | /* Right Arrow -- Move forward one character */ | 861 | /* Right Arrow -- Move forward one character */ |
602 | input_forward(outputFd, &cursor, len); | 862 | input_forward(); |
603 | break; | 863 | break; |
604 | case 'D': | 864 | case 'D': |
605 | /* Left Arrow -- Move back one character */ | 865 | /* Left Arrow -- Move back one character */ |
606 | input_backward(outputFd, &cursor); | 866 | input_backward(); |
607 | break; | 867 | break; |
608 | case '3': | 868 | case '3': |
609 | /* Delete */ | 869 | /* Delete */ |
610 | input_delete(command, outputFd, cursor, &len); | 870 | input_delete(); |
611 | break; | 871 | break; |
612 | case '1': | 872 | case '1': |
613 | /* Home (Ctrl-A) */ | 873 | /* Home (Ctrl-A) */ |
614 | input_home(outputFd, &cursor); | 874 | input_home(); |
615 | break; | 875 | break; |
616 | case '4': | 876 | case '4': |
617 | /* End (Ctrl-E) */ | 877 | /* End (Ctrl-E) */ |
618 | input_end(outputFd, &cursor, len); | 878 | input_end(); |
619 | break; | 879 | break; |
620 | default: | 880 | default: |
621 | xwrite(outputFd, "\007", 1); | 881 | beep(); |
622 | } | 882 | } |
623 | if (c == '1' || c == '3' || c == '4') | 883 | if (c == '1' || c == '3' || c == '4') |
624 | if ((ret = read(inputFd, &c, 1)) < 1) | 884 | if ((ret = read(inputFd, &c, 1)) < 1) |
@@ -631,14 +891,14 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
631 | switch (c) { | 891 | switch (c) { |
632 | case 'H': | 892 | case 'H': |
633 | /* Home (xterm) */ | 893 | /* Home (xterm) */ |
634 | input_home(outputFd, &cursor); | 894 | input_home(); |
635 | break; | 895 | break; |
636 | case 'F': | 896 | case 'F': |
637 | /* End (xterm) */ | 897 | /* End (xterm) */ |
638 | input_end(outputFd, &cursor, len); | 898 | input_end(); |
639 | break; | 899 | break; |
640 | default: | 900 | default: |
641 | xwrite(outputFd, "\007", 1); | 901 | beep(); |
642 | } | 902 | } |
643 | } | 903 | } |
644 | c = 0; | 904 | c = 0; |
@@ -658,20 +918,20 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
658 | 918 | ||
659 | if (cursor == (len - 1)) { /* Append if at the end of the line */ | 919 | if (cursor == (len - 1)) { /* Append if at the end of the line */ |
660 | *(command + cursor) = c; | 920 | *(command + cursor) = c; |
921 | cmdedit_set_out_char(c, command[cursor+1]); | ||
661 | } else { /* Insert otherwise */ | 922 | } else { /* Insert otherwise */ |
662 | memmove(command + cursor + 1, command + cursor, | 923 | memmove(command + cursor + 1, command + cursor, |
663 | len - cursor - 1); | 924 | len - cursor - 1); |
664 | 925 | ||
665 | *(command + cursor) = c; | 926 | *(command + cursor) = c; |
666 | 927 | j = cursor+1; | |
667 | for (j = cursor; j < len; j++) | 928 | /* rewrite from cursor */ |
668 | xwrite(outputFd, command + j, 1); | 929 | input_end (); |
669 | for (; j > cursor; j--) | 930 | /* to prev x pos + 1 */ |
670 | xwrite(outputFd, "\033[D", 3); | 931 | while(cursor > j) |
932 | input_backward(); | ||
671 | } | 933 | } |
672 | 934 | ||
673 | cursor++; | ||
674 | xwrite(outputFd, &c, 1); | ||
675 | break; | 935 | break; |
676 | } | 936 | } |
677 | if (c == '\t') | 937 | if (c == '\t') |
@@ -683,15 +943,18 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
683 | break; | 943 | break; |
684 | } | 944 | } |
685 | 945 | ||
686 | nr = len + 1; | 946 | setTermSettings (inputFd, (void *) &initial_settings); |
687 | setTermSettings(inputFd, (void *) &initial_settings); | 947 | handlers_sets &= ~SET_RESET_TERM; |
688 | reset_term = 0; | ||
689 | |||
690 | 948 | ||
691 | /* Handle command history log */ | 949 | /* Handle command history log */ |
692 | if (*(command)) { | 950 | if (len>1) { /* no put empty line (only '\n') */ |
693 | 951 | ||
694 | struct history *h = his_end; | 952 | struct history *h = his_end; |
953 | char *ss; | ||
954 | |||
955 | command[len-1] = 0; /* destroy end '\n' */ | ||
956 | ss = strdup(command); /* duplicate without '\n' */ | ||
957 | command[len-1] = '\n'; /* restore '\n' */ | ||
695 | 958 | ||
696 | if (!h) { | 959 | if (!h) { |
697 | /* No previous history -- this memory is never freed */ | 960 | /* No previous history -- this memory is never freed */ |
@@ -699,7 +962,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
699 | h->n = xmalloc(sizeof(struct history)); | 962 | h->n = xmalloc(sizeof(struct history)); |
700 | 963 | ||
701 | h->p = NULL; | 964 | h->p = NULL; |
702 | h->s = strdup(command); | 965 | h->s = ss; |
703 | h->n->p = h; | 966 | h->n->p = h; |
704 | h->n->n = NULL; | 967 | h->n->n = NULL; |
705 | h->n->s = NULL; | 968 | h->n->s = NULL; |
@@ -712,7 +975,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
712 | h->n->p = h; | 975 | h->n->p = h; |
713 | h->n->n = NULL; | 976 | h->n->n = NULL; |
714 | h->n->s = NULL; | 977 | h->n->s = NULL; |
715 | h->s = strdup(command); | 978 | h->s = ss; |
716 | his_end = h->n; | 979 | his_end = h->n; |
717 | 980 | ||
718 | /* After max history, remove the oldest command */ | 981 | /* After max history, remove the oldest command */ |
@@ -733,38 +996,21 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
733 | return; | 996 | return; |
734 | } | 997 | } |
735 | 998 | ||
736 | extern void cmdedit_init(void) | ||
737 | { | ||
738 | win_changed(0); | ||
739 | signal(SIGWINCH, win_changed); | ||
740 | 999 | ||
741 | if(exithandler_set == 0) { | 1000 | /* Undo the effects of cmdedit_init(). */ |
742 | atexit(cmdedit_reset_term); /* be sure to do this only once */ | ||
743 | exithandler_set = 1; | ||
744 | } | ||
745 | signal(SIGKILL, clean_up_and_die); | ||
746 | signal(SIGINT, clean_up_and_die); | ||
747 | signal(SIGQUIT, clean_up_and_die); | ||
748 | signal(SIGTERM, clean_up_and_die); | ||
749 | } | ||
750 | |||
751 | /* | ||
752 | ** Undo the effects of cmdedit_init() as good as we can: | ||
753 | ** I am not aware of a way to revoke an atexit() handler, | ||
754 | ** but, fortunately, our particular handler can be made | ||
755 | ** a no-op by setting reset_term = 0. | ||
756 | */ | ||
757 | extern void cmdedit_terminate(void) | 1001 | extern void cmdedit_terminate(void) |
758 | { | 1002 | { |
759 | cmdedit_reset_term(); | 1003 | cmdedit_reset_term(); |
760 | reset_term = 0; | 1004 | if((handlers_sets & SET_TERM_HANDLERS)!=0) { |
761 | signal(SIGKILL, SIG_DFL); | 1005 | signal(SIGKILL, SIG_DFL); |
762 | signal(SIGINT, SIG_DFL); | 1006 | signal(SIGINT, SIG_DFL); |
763 | signal(SIGQUIT, SIG_DFL); | 1007 | signal(SIGQUIT, SIG_DFL); |
764 | signal(SIGTERM, SIG_DFL); | 1008 | signal(SIGTERM, SIG_DFL); |
765 | signal(SIGWINCH, SIG_DFL); | 1009 | signal(SIGWINCH, SIG_DFL); |
1010 | handlers_sets &= ~SET_TERM_HANDLERS; | ||
1011 | } | ||
766 | } | 1012 | } |
767 | 1013 | ||
768 | 1014 | ||
769 | 1015 | ||
770 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ | 1016 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ |
diff --git a/shell/cmdedit.c b/shell/cmdedit.c index 12c78dc76..c2b4123db 100644 --- a/shell/cmdedit.c +++ b/shell/cmdedit.c | |||
@@ -25,13 +25,20 @@ | |||
25 | need to be added. This version was created on Debian GNU/Linux 2.x. | 25 | need to be added. This version was created on Debian GNU/Linux 2.x. |
26 | Delete, Backspace, Home, End, and the arrow keys were tested | 26 | Delete, Backspace, Home, End, and the arrow keys were tested |
27 | to work in an Xterm and console. Ctrl-A also works as Home. | 27 | to work in an Xterm and console. Ctrl-A also works as Home. |
28 | Ctrl-E also works as End. The binary size increase is <3K. | 28 | Ctrl-E also works as End. |
29 | 29 | ||
30 | Editting will not display correctly for lines greater then the | 30 | |
31 | terminal width. (more then one line.) However, history will. | 31 | Editor with vertical scrolling and completion by |
32 | Vladimir Oleynik. vodz@usa.net (c) 2001 | ||
33 | |||
34 | Small bug: not true work if terminal size (x*y symbols) less | ||
35 | size (prompt + editor`s line + 2 symbols) | ||
32 | */ | 36 | */ |
33 | 37 | ||
38 | |||
39 | |||
34 | #include "busybox.h" | 40 | #include "busybox.h" |
41 | |||
35 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | 42 | #ifdef BB_FEATURE_SH_COMMAND_EDITING |
36 | 43 | ||
37 | #include <stdio.h> | 44 | #include <stdio.h> |
@@ -43,6 +50,13 @@ | |||
43 | #include <ctype.h> | 50 | #include <ctype.h> |
44 | #include <signal.h> | 51 | #include <signal.h> |
45 | 52 | ||
53 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | ||
54 | #include <sys/stat.h> | ||
55 | #endif | ||
56 | |||
57 | #ifdef BB_FEATURE_USERNAME_COMPLETION | ||
58 | #include <pwd.h> | ||
59 | #endif | ||
46 | 60 | ||
47 | static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */ | 61 | static const int MAX_HISTORY = 15; /* Maximum length of the linked list for the command line history */ |
48 | 62 | ||
@@ -58,7 +72,6 @@ static struct history *his_front = NULL; /* First element in command line list * | |||
58 | static struct history *his_end = NULL; /* Last element in command line list */ | 72 | static struct history *his_end = NULL; /* Last element in command line list */ |
59 | 73 | ||
60 | /* ED: sparc termios is broken: revert back to old termio handling. */ | 74 | /* ED: sparc termios is broken: revert back to old termio handling. */ |
61 | #ifdef BB_FEATURE_USE_TERMIOS | ||
62 | 75 | ||
63 | #if #cpu(sparc) | 76 | #if #cpu(sparc) |
64 | # include <termio.h> | 77 | # include <termio.h> |
@@ -79,16 +92,35 @@ static struct termios initial_settings, new_settings; | |||
79 | #define _POSIX_VDISABLE '\0' | 92 | #define _POSIX_VDISABLE '\0' |
80 | #endif | 93 | #endif |
81 | 94 | ||
82 | #endif | ||
83 | 95 | ||
96 | static | ||
97 | volatile int cmdedit_termw; /* actual terminal width */ | ||
98 | static int history_counter = 0; /* Number of commands in history list */ | ||
84 | 99 | ||
100 | static | ||
101 | volatile int handlers_sets = 0; /* Set next bites | ||
102 | when atexit() has been called | ||
103 | and set many "terminates" signal handlers | ||
104 | and winchg signal handler | ||
105 | and if the terminal needs to be reset upon exit | ||
106 | */ | ||
107 | enum { | ||
108 | SET_ATEXIT = 1, | ||
109 | SET_TERM_HANDLERS = 2, | ||
110 | SET_WCHG_HANDLERS = 4, | ||
111 | SET_RESET_TERM = 8, | ||
112 | }; | ||
85 | 113 | ||
86 | static int cmdedit_termw = 80; /* actual terminal width */ | ||
87 | static int cmdedit_scroll = 27; /* width of EOL scrolling region */ | ||
88 | static int history_counter = 0; /* Number of commands in history list */ | ||
89 | static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ | ||
90 | static int exithandler_set = 0; /* Set to true when atexit() has been called */ | ||
91 | 114 | ||
115 | static int cmdedit_x; /* real x terminal position, | ||
116 | require put prompt in start x position */ | ||
117 | static int cmdedit_y; /* pseudoreal y terminal position */ | ||
118 | static int cmdedit_prmt_len; /* for fast running, without duplicate calculate */ | ||
119 | |||
120 | static int cursor; /* required global for signal handler */ | ||
121 | static int len; /* --- "" - - "" - -"- --""-- --""--- */ | ||
122 | static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */ | ||
123 | static const char *cmdedit_prompt;/* --- "" - - "" - -"- --""-- --""--- */ | ||
92 | 124 | ||
93 | /* Link into lash to reset context to 0 | 125 | /* Link into lash to reset context to 0 |
94 | * on ^C and such */ | 126 | * on ^C and such */ |
@@ -101,38 +133,43 @@ struct history { | |||
101 | struct history *n; | 133 | struct history *n; |
102 | }; | 134 | }; |
103 | 135 | ||
104 | #define xwrite write | 136 | static void cmdedit_setwidth(int w, int redraw_flg); |
105 | 137 | ||
106 | /* | 138 | static void win_changed(int nsig) |
107 | * TODO: Someday we want to implement 'horizontal scrolling' of the | ||
108 | * command-line when the user has typed more than the current width. This | ||
109 | * would allow the user to see a 'window' of what he has typed. | ||
110 | */ | ||
111 | static void cmdedit_setwidth(int w) | ||
112 | { | ||
113 | if (w > 20) { | ||
114 | cmdedit_termw = w; | ||
115 | cmdedit_scroll = w / 3; | ||
116 | } else { | ||
117 | error_msg("\n*** Error: minimum screen width is 21\n"); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | static void win_changed(int junk) | ||
122 | { | 139 | { |
123 | struct winsize win = { 0, 0, 0, 0 }; | 140 | struct winsize win = { 0, 0, 0, 0 }; |
124 | ioctl(0, TIOCGWINSZ, &win); | 141 | static __sighandler_t previous_SIGWINCH_handler; /* for reset */ |
125 | if (win.ws_col > 0) { | 142 | |
126 | cmdedit_setwidth( win.ws_col - 1); | 143 | /* emulate || signal call */ |
144 | if(nsig == -SIGWINCH || nsig == SIGWINCH) { | ||
145 | ioctl(0, TIOCGWINSZ, &win); | ||
146 | if (win.ws_col > 0) { | ||
147 | cmdedit_setwidth( win.ws_col, nsig == SIGWINCH ); | ||
148 | } | ||
127 | } | 149 | } |
150 | /* Unix not all standart in recall signal */ | ||
151 | |||
152 | if(nsig == -SIGWINCH) /* save previous handler */ | ||
153 | previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); | ||
154 | else if(nsig == SIGWINCH) /* signaled called handler */ | ||
155 | signal(SIGWINCH, win_changed); /* set for next call */ | ||
156 | else /* set previous handler */ | ||
157 | signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */ | ||
128 | } | 158 | } |
129 | 159 | ||
130 | |||
131 | static void cmdedit_reset_term(void) | 160 | static void cmdedit_reset_term(void) |
132 | { | 161 | { |
133 | if (reset_term) | 162 | if((handlers_sets & SET_RESET_TERM)!=0) { |
134 | /* sparc and other have broken termios support: use old termio handling. */ | 163 | /* sparc and other have broken termios support: use old termio handling. */ |
135 | setTermSettings(fileno(stdin), (void*) &initial_settings); | 164 | setTermSettings(fileno(stdin), (void*) &initial_settings); |
165 | handlers_sets &= ~SET_RESET_TERM; | ||
166 | } | ||
167 | if((handlers_sets & SET_WCHG_HANDLERS)!=0) { | ||
168 | /* reset SIGWINCH handler to previous (default) */ | ||
169 | win_changed(0); | ||
170 | handlers_sets &= ~SET_WCHG_HANDLERS; | ||
171 | } | ||
172 | fflush(stdout); | ||
136 | #ifdef BB_FEATURE_CLEAN_UP | 173 | #ifdef BB_FEATURE_CLEAN_UP |
137 | if (his_front) { | 174 | if (his_front) { |
138 | struct history *n; | 175 | struct history *n; |
@@ -147,169 +184,348 @@ static void cmdedit_reset_term(void) | |||
147 | #endif | 184 | #endif |
148 | } | 185 | } |
149 | 186 | ||
150 | static void clean_up_and_die(int sig) | ||
151 | { | ||
152 | cmdedit_reset_term(); | ||
153 | printf("\n"); | ||
154 | if (sig!=SIGINT) | ||
155 | exit(EXIT_SUCCESS); | ||
156 | } | ||
157 | 187 | ||
158 | /* Go to HOME position */ | ||
159 | static void input_home(int outputFd, int *cursor) | ||
160 | { | ||
161 | while (*cursor > 0) { | ||
162 | xwrite(outputFd, "\b", 1); | ||
163 | --*cursor; | ||
164 | } | ||
165 | } | ||
166 | 188 | ||
167 | /* Go to END position */ | 189 | /* special for recount position for scroll and remove terminal margin effect */ |
168 | static void input_end(int outputFd, int *cursor, int len) | 190 | static void cmdedit_set_out_char(int c, int next_char) { |
169 | { | 191 | putchar(c); |
170 | while (*cursor < len) { | 192 | if(++cmdedit_x>=cmdedit_termw) { |
171 | xwrite(outputFd, "\033[C", 3); | 193 | /* terminal is scrolled down */ |
172 | ++*cursor; | 194 | cmdedit_y++; |
195 | cmdedit_x=0; | ||
196 | |||
197 | if(!next_char) | ||
198 | next_char = ' '; | ||
199 | /* destroy "(auto)margin" */ | ||
200 | putchar(next_char); | ||
201 | putchar('\b'); | ||
173 | } | 202 | } |
203 | cursor++; | ||
174 | } | 204 | } |
175 | 205 | ||
176 | /* Delete the char in back of the cursor */ | 206 | /* Move to end line. Bonus: rewrite line from cursor without use |
177 | static void input_backspace(char* command, int outputFd, int *cursor, int *len) | 207 | special control terminal strings, also saved size and speed! */ |
178 | { | 208 | static void input_end (void) { |
179 | int j = 0; | 209 | while(cursor < len) |
180 | 210 | cmdedit_set_out_char(command_ps[cursor], 0); | |
181 | /* Debug crap */ | 211 | } |
182 | //fprintf(stderr, "\nerik: len=%d, cursor=%d, strlen(command)='%d'\n", *len, *cursor, strlen(command)); | ||
183 | //xwrite(outputFd, command, *len); | ||
184 | //*cursor = *len; | ||
185 | 212 | ||
213 | /* Go to the next line */ | ||
214 | static void goto_new_line(void) { | ||
215 | input_end(); | ||
216 | cmdedit_set_out_char('\n', 0); | ||
217 | } | ||
186 | 218 | ||
187 | if (*cursor > 0) { | ||
188 | xwrite(outputFd, "\b \b", 3); | ||
189 | --*cursor; | ||
190 | memmove(command + *cursor, command + *cursor + 1, | ||
191 | BUFSIZ - *cursor + 1); | ||
192 | 219 | ||
193 | for (j = *cursor; j < (BUFSIZ - 1); j++) { | 220 | static inline void out1str(const char *s) { fputs (s, stdout); } |
194 | if (!*(command + j)) | 221 | static inline void beep (void) { putchar('\007'); } |
195 | break; | ||
196 | else | ||
197 | xwrite(outputFd, (command + j), 1); | ||
198 | } | ||
199 | 222 | ||
200 | xwrite(outputFd, " \b", 2); | 223 | /* Go to HOME position */ |
224 | static void input_home(void) | ||
225 | { | ||
226 | while(cmdedit_y>0) { /* up to start y */ | ||
227 | out1str("\033[A"); | ||
228 | cmdedit_y--; | ||
229 | } | ||
230 | putchar('\r'); | ||
231 | cursor = 0; | ||
232 | out1str(cmdedit_prompt); | ||
233 | cmdedit_x = cmdedit_prmt_len; | ||
201 | 234 | ||
202 | while (j-- > *cursor) | 235 | } |
203 | xwrite(outputFd, "\b", 1); | ||
204 | 236 | ||
205 | --*len; | 237 | /* Move back one charactor */ |
238 | static void input_backward(void) { | ||
239 | if (cursor > 0) { | ||
240 | cursor--; | ||
241 | if(cmdedit_x!=0) { /* no first position in terminal line */ | ||
242 | putchar('\b'); | ||
243 | cmdedit_x--; | ||
244 | } | ||
245 | else { | ||
246 | out1str("\033[A"); /* up */ | ||
247 | cmdedit_y--; | ||
248 | |||
249 | /* to end in current terminal line */ | ||
250 | while(cmdedit_x<(cmdedit_termw-1)) { | ||
251 | out1str("\033[C"); | ||
252 | cmdedit_x++; | ||
253 | } | ||
254 | } | ||
206 | } | 255 | } |
207 | } | 256 | } |
208 | 257 | ||
209 | /* Delete the char in front of the cursor */ | 258 | /* Delete the char in front of the cursor */ |
210 | static void input_delete(char* command, int outputFd, int cursor, int *len) | 259 | static void input_delete(void) |
211 | { | 260 | { |
212 | int j = 0; | 261 | int j = cursor; |
213 | 262 | ||
214 | if (cursor == *len) | 263 | if (j == len) |
215 | return; | 264 | return; |
216 | 265 | ||
217 | memmove(command + cursor, command + cursor + 1, | 266 | memmove (command_ps + j, command_ps + j + 1, BUFSIZ - j - 1); |
218 | BUFSIZ - cursor - 1); | 267 | len--; |
219 | for (j = cursor; j < (BUFSIZ - 1); j++) { | 268 | input_end(); /* rewtite new line */ |
220 | if (!*(command + j)) | 269 | cmdedit_set_out_char(' ', 0); /* destroy end char */ |
221 | break; | 270 | while (j < cursor) |
222 | else | 271 | input_backward(); /* back to old pos cursor */ |
223 | xwrite(outputFd, (command + j), 1); | 272 | } |
273 | |||
274 | /* Delete the char in back of the cursor */ | ||
275 | static void input_backspace(void) | ||
276 | { | ||
277 | if (cursor > 0) { | ||
278 | input_backward(); | ||
279 | input_delete (); | ||
224 | } | 280 | } |
281 | } | ||
225 | 282 | ||
226 | xwrite(outputFd, " \b", 2); | ||
227 | 283 | ||
228 | while (j-- > cursor) | 284 | /* Move forward one charactor */ |
229 | xwrite(outputFd, "\b", 1); | 285 | static void input_forward(void) |
230 | --*len; | 286 | { |
287 | if (cursor < len) | ||
288 | cmdedit_set_out_char(command_ps[cursor], command_ps[cursor + 1]); | ||
231 | } | 289 | } |
232 | 290 | ||
233 | /* Move forward one charactor */ | 291 | |
234 | static void input_forward(int outputFd, int *cursor, int len) | 292 | static void clean_up_and_die(int sig) |
235 | { | 293 | { |
236 | if (*cursor < len) { | 294 | goto_new_line(); |
237 | xwrite(outputFd, "\033[C", 3); | 295 | if (sig!=SIGINT) |
238 | ++*cursor; | 296 | exit(EXIT_SUCCESS); /* cmdedit_reset_term() called in atexit */ |
239 | } | 297 | cmdedit_reset_term(); |
240 | } | 298 | } |
241 | 299 | ||
242 | /* Move back one charactor */ | 300 | static void cmdedit_setwidth(int w, int redraw_flg) |
243 | static void input_backward(int outputFd, int *cursor) | ||
244 | { | 301 | { |
245 | if (*cursor > 0) { | 302 | cmdedit_termw = cmdedit_prmt_len+2; |
246 | xwrite(outputFd, "\033[D", 3); | 303 | if (w > cmdedit_termw) { |
247 | --*cursor; | 304 | |
305 | cmdedit_termw = w; | ||
306 | |||
307 | if(redraw_flg) { | ||
308 | int sav_cursor = cursor; | ||
309 | |||
310 | /* set variables for new terminal size */ | ||
311 | cmdedit_y = sav_cursor/w; | ||
312 | cmdedit_x = sav_cursor-cmdedit_y*w; | ||
313 | |||
314 | /* redraw */ | ||
315 | input_home(); | ||
316 | input_end(); | ||
317 | while(sav_cursor<cursor) | ||
318 | input_backward(); | ||
319 | } | ||
320 | } else { | ||
321 | error_msg("\n*** Error: minimum screen width is %d\n", cmdedit_termw); | ||
248 | } | 322 | } |
249 | } | 323 | } |
250 | 324 | ||
325 | extern void cmdedit_init(void) | ||
326 | { | ||
327 | if((handlers_sets & SET_WCHG_HANDLERS)==0) { | ||
328 | /* emulate usage handler to set handler and call yours work */ | ||
329 | win_changed(-SIGWINCH); | ||
330 | handlers_sets |= SET_WCHG_HANDLERS; | ||
331 | } | ||
251 | 332 | ||
333 | if((handlers_sets & SET_ATEXIT)==0) { | ||
334 | atexit(cmdedit_reset_term); /* be sure to do this only once */ | ||
335 | handlers_sets |= SET_ATEXIT; | ||
336 | } | ||
337 | if((handlers_sets & SET_TERM_HANDLERS)==0) { | ||
338 | signal(SIGKILL, clean_up_and_die); | ||
339 | signal(SIGINT, clean_up_and_die); | ||
340 | signal(SIGQUIT, clean_up_and_die); | ||
341 | signal(SIGTERM, clean_up_and_die); | ||
342 | handlers_sets |= SET_TERM_HANDLERS; | ||
343 | } | ||
344 | } | ||
252 | 345 | ||
253 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | 346 | #ifdef BB_FEATURE_SH_TAB_COMPLETION |
254 | static char** username_tab_completion(char* command, int *num_matches) | 347 | |
348 | #ifdef BB_FEATURE_USERNAME_COMPLETION | ||
349 | static char** username_tab_completion(char *ud, int *num_matches) | ||
255 | { | 350 | { |
351 | static struct passwd *entry; | ||
352 | int userlen; | ||
256 | char **matches = (char **) NULL; | 353 | char **matches = (char **) NULL; |
257 | *num_matches=0; | 354 | char *temp; |
258 | fprintf(stderr, "\nin username_tab_completion\n"); | 355 | int nm = 0; |
356 | |||
357 | setpwent (); | ||
358 | userlen = strlen (ud + 1); | ||
359 | |||
360 | while ((entry = getpwent ()) != NULL) { | ||
361 | /* Null usernames should result in all users as possible completions. */ | ||
362 | if (!userlen || !strncmp (ud + 1, entry->pw_name, userlen)) { | ||
363 | |||
364 | temp = xmalloc (3 + strlen (entry->pw_name)); | ||
365 | sprintf(temp, "~%s/", entry->pw_name); | ||
366 | |||
367 | matches = xrealloc(matches, (nm+1)*sizeof(char *)); | ||
368 | matches[nm++] = temp; | ||
369 | } | ||
370 | } | ||
371 | |||
372 | endpwent (); | ||
373 | (*num_matches) = nm; | ||
259 | return (matches); | 374 | return (matches); |
260 | } | 375 | } |
376 | #endif | ||
377 | |||
378 | enum { | ||
379 | FIND_EXE_ONLY = 0, | ||
380 | FIND_DIR_ONLY = 1, | ||
381 | FIND_FILE_ONLY = 2, | ||
382 | }; | ||
261 | 383 | ||
262 | #include <dirent.h> | 384 | #include <dirent.h> |
263 | static char** exe_n_cwd_tab_completion(char* command, int *num_matches) | 385 | |
386 | static int path_parse(char ***p, int flags) | ||
387 | { | ||
388 | int npth; | ||
389 | char *tmp; | ||
390 | char *pth; | ||
391 | |||
392 | if(flags!=FIND_EXE_ONLY || (pth=getenv("PATH"))==0) { | ||
393 | /* if not setenv PATH variable, to search cur dir "." */ | ||
394 | (*p) = xmalloc(sizeof(char *)); | ||
395 | (*p)[0] = xstrdup("."); | ||
396 | return 1; | ||
397 | } | ||
398 | |||
399 | tmp = pth; | ||
400 | npth=0; | ||
401 | |||
402 | for(;;) { | ||
403 | npth++; /* count words is + 1 count ':' */ | ||
404 | tmp = strchr(tmp, ':'); | ||
405 | if(tmp) | ||
406 | tmp++; | ||
407 | else | ||
408 | break; | ||
409 | } | ||
410 | |||
411 | *p = xmalloc(npth*sizeof(char *)); | ||
412 | |||
413 | tmp = pth; | ||
414 | (*p)[0] = xstrdup(tmp); | ||
415 | npth=1; /* count words is + 1 count ':' */ | ||
416 | |||
417 | for(;;) { | ||
418 | tmp = strchr(tmp, ':'); | ||
419 | if(tmp) { | ||
420 | (*p)[0][(tmp-pth)]=0; /* ':' -> '\0'*/ | ||
421 | tmp++; | ||
422 | } else | ||
423 | break; | ||
424 | (*p)[npth++] = &(*p)[0][(tmp-pth)]; /* p[next]=p[0][&'\0'+1] */ | ||
425 | } | ||
426 | |||
427 | return npth; | ||
428 | } | ||
429 | |||
430 | static char** exe_n_cwd_tab_completion(char* command, int *num_matches, int type) | ||
264 | { | 431 | { |
265 | char *dirName; | 432 | char *dirName; |
266 | char **matches; | 433 | char **matches = 0; |
267 | DIR *dir; | 434 | DIR *dir; |
268 | struct dirent *next; | 435 | struct dirent *next; |
436 | char cmd [BUFSIZ+4]; | ||
437 | char *dirbuf; | ||
438 | char found [BUFSIZ+4]; | ||
439 | int nm = *num_matches; | ||
440 | struct stat st; | ||
441 | char **paths; | ||
442 | int npaths; | ||
443 | int i; | ||
444 | char full_pth[BUFSIZ+4+PATH_MAX]; | ||
445 | |||
446 | |||
447 | strcpy(cmd, command); /* save for change (last '/' to '\0') */ | ||
448 | |||
449 | dirName = strrchr(cmd, '/'); | ||
450 | if(dirName==NULL) { | ||
451 | /* no dir, if flags==EXE_ONLY - get paths, else "." */ | ||
452 | npaths = path_parse(&paths, type); | ||
453 | if(npaths==0) | ||
454 | return 0; | ||
455 | } else { | ||
456 | /* with dir */ | ||
457 | |||
458 | /* save dir */ | ||
459 | dirbuf = xstrdup(cmd); | ||
460 | /* set only dirname */ | ||
461 | dirbuf[(dirName-cmd)+1]=0; | ||
462 | |||
463 | /* strip dirname in cmd */ | ||
464 | strcpy(cmd, dirName+1); | ||
269 | 465 | ||
270 | matches = xmalloc( sizeof(char*)*50); | 466 | paths = xmalloc(sizeof(char*)); |
467 | paths[0] = dirbuf; | ||
468 | npaths = 1; /* only 1 dir */ | ||
469 | } | ||
271 | 470 | ||
272 | /* Stick a wildcard onto the command, for later use */ | 471 | for(i=0; i < npaths; i++) { |
273 | strcat( command, "*"); | ||
274 | 472 | ||
275 | /* Now wall the current directory */ | 473 | dir = opendir(paths[i]); |
276 | dirName = get_current_dir_name(); | ||
277 | dir = opendir(dirName); | ||
278 | if (!dir) { | 474 | if (!dir) { |
279 | /* Don't print an error, just shut up and return */ | 475 | /* Don't print an error, just shut up and return */ |
280 | *num_matches=0; | ||
281 | return (matches); | 476 | return (matches); |
282 | } | 477 | } |
283 | while ((next = readdir(dir)) != NULL) { | 478 | while ((next = readdir(dir)) != NULL) { |
284 | 479 | /* matched ? */ | |
285 | /* Some quick sanity checks */ | 480 | if(strncmp(next->d_name, cmd, strlen(cmd))) |
286 | if ((strcmp(next->d_name, "..") == 0) | 481 | continue; |
287 | || (strcmp(next->d_name, ".") == 0)) { | 482 | /* not see .name without .match */ |
483 | if(*next->d_name == '.' && *cmd != '.') | ||
484 | continue; | ||
485 | sprintf(full_pth, "%s/%s", paths[i], next->d_name); | ||
486 | /* hmm, remover in progress? */ | ||
487 | if(stat(full_pth, &st)<0) | ||
488 | continue; | ||
489 | /* Cool, found a match. */ | ||
490 | if (S_ISDIR(st.st_mode)) { | ||
491 | /* name is directory */ | ||
492 | strcpy(found, next->d_name); | ||
493 | strcat(found, "/"); | ||
494 | if(type==FIND_DIR_ONLY) | ||
495 | strcat(found, " "); | ||
496 | } else { | ||
497 | /* not put found file if search only dirs for cd */ | ||
498 | if(type==FIND_DIR_ONLY) | ||
288 | continue; | 499 | continue; |
500 | strcpy(found, next->d_name); | ||
501 | strcat(found, " "); | ||
289 | } | 502 | } |
290 | /* See if this matches */ | 503 | /* Add it to the list */ |
291 | if (check_wildcard_match(next->d_name, command) == TRUE) { | 504 | matches = xrealloc(matches, (nm+1)*sizeof(char *)); |
292 | /* Cool, found a match. Add it to the list */ | 505 | matches[nm++] = xstrdup(found); |
293 | matches[*num_matches] = xmalloc(strlen(next->d_name)+1); | ||
294 | strcpy( matches[*num_matches], next->d_name); | ||
295 | ++*num_matches; | ||
296 | //matches = realloc( matches, sizeof(char*)*(*num_matches)); | ||
297 | } | 506 | } |
298 | } | 507 | } |
299 | 508 | free(paths[0]); /* allocate memory only in first member */ | |
509 | free(paths); | ||
510 | *num_matches = nm; | ||
300 | return (matches); | 511 | return (matches); |
301 | } | 512 | } |
302 | 513 | ||
303 | static void input_tab(char* command, char* prompt, int outputFd, int *cursor, int *len, int lastWasTab) | 514 | static void input_tab(int lastWasTab) |
304 | { | 515 | { |
305 | /* Do TAB completion */ | 516 | /* Do TAB completion */ |
306 | static int num_matches=0; | 517 | static int num_matches; |
307 | static char **matches = (char **) NULL; | 518 | static char **matches; |
308 | int pos = *cursor; | 519 | |
520 | char matchBuf[BUFSIZ]; | ||
521 | |||
522 | int pos = cursor; | ||
523 | int find_type=FIND_FILE_ONLY; | ||
309 | 524 | ||
310 | 525 | ||
311 | if (lastWasTab == FALSE) { | 526 | if (lastWasTab == FALSE) { |
312 | char *tmp, *tmp1, *matchBuf; | 527 | char *tmp, *tmp1; |
528 | int len_found; | ||
313 | 529 | ||
314 | /* For now, we will not bother with trying to distinguish | 530 | /* For now, we will not bother with trying to distinguish |
315 | * whether the cursor is in/at a command extression -- we | 531 | * whether the cursor is in/at a command extression -- we |
@@ -319,50 +535,80 @@ static void input_tab(char* command, char* prompt, int outputFd, int *cursor, in | |||
319 | 535 | ||
320 | /* Make a local copy of the string -- up | 536 | /* Make a local copy of the string -- up |
321 | * to the position of the cursor */ | 537 | * to the position of the cursor */ |
322 | matchBuf = (char *) xcalloc(BUFSIZ, sizeof(char)); | 538 | memset(matchBuf, 0, BUFSIZ); |
323 | strncpy(matchBuf, command, *cursor); | 539 | tmp = strncpy(matchBuf, command_ps, cursor); |
324 | tmp=matchBuf; | ||
325 | 540 | ||
326 | /* skip past any command seperator tokens */ | 541 | /* skip past any command seperator tokens */ |
327 | while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { | 542 | while ( (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { |
328 | tmp=++tmp1; | 543 | tmp = ++tmp1; |
329 | /* skip any leading white space */ | ||
330 | while (*tmp && isspace(*tmp)) | ||
331 | ++tmp; | ||
332 | } | 544 | } |
333 | 545 | ||
334 | /* skip any leading white space */ | 546 | /* skip any leading white space */ |
335 | while (*tmp && isspace(*tmp)) | 547 | while (*tmp == ' ') |
336 | ++tmp; | 548 | tmp++; |
549 | |||
550 | if(strncmp(tmp, "cd ", 3)==0) | ||
551 | find_type = FIND_DIR_ONLY; | ||
552 | else if(strchr(tmp, ' ')==NULL) | ||
553 | find_type = FIND_EXE_ONLY; | ||
554 | |||
555 | /* find begin curent word */ | ||
556 | if( (tmp1=strrchr(tmp, ' ')) != NULL) { | ||
557 | tmp = ++tmp1; | ||
558 | } | ||
559 | strcpy(matchBuf, tmp); | ||
337 | 560 | ||
338 | /* Free up any memory already allocated */ | 561 | /* Free up any memory already allocated */ |
339 | if (matches) { | 562 | if (matches) { |
563 | while(num_matches>0) | ||
564 | free(matches[--num_matches]); | ||
340 | free(matches); | 565 | free(matches); |
341 | matches = (char **) NULL; | 566 | matches = (char **) NULL; |
342 | } | 567 | } |
343 | 568 | ||
569 | #ifdef BB_FEATURE_USERNAME_COMPLETION | ||
344 | /* If the word starts with `~' and there is no slash in the word, | 570 | /* If the word starts with `~' and there is no slash in the word, |
345 | * then try completing this word as a username. */ | 571 | * then try completing this word as a username. */ |
346 | 572 | ||
347 | /* FIXME -- this check is broken! */ | 573 | if (matchBuf[0]=='~' && strchr(matchBuf, '/')==0) { |
348 | if (*tmp == '~' && !strchr(tmp, '/')) | 574 | matches = username_tab_completion(matchBuf, &num_matches); |
349 | matches = username_tab_completion(tmp, &num_matches); | 575 | } |
350 | 576 | #endif | |
351 | /* Try to match any executable in our path and everything | 577 | /* Try to match any executable in our path and everything |
352 | * in the current working directory that matches. */ | 578 | * in the current working directory that matches. */ |
353 | if (!matches) | 579 | if (!matches) |
354 | matches = exe_n_cwd_tab_completion(tmp, &num_matches); | 580 | matches = exe_n_cwd_tab_completion(matchBuf, &num_matches, find_type); |
355 | |||
356 | /* Don't leak memory */ | ||
357 | free( matchBuf); | ||
358 | 581 | ||
359 | /* Did we find exactly one match? */ | 582 | /* Did we find exactly one match? */ |
360 | if (matches && num_matches==1) { | 583 | if(!matches || num_matches>1) { |
584 | beep(); | ||
585 | return; | ||
586 | } | ||
587 | |||
588 | len_found = strlen(matches[0]); | ||
589 | |||
590 | /* have space to placed match? */ | ||
591 | if ( (len_found-strlen(matchBuf)+len) < BUFSIZ ) { | ||
592 | |||
593 | int recalc_pos = len; | ||
594 | |||
595 | /* before word for match */ | ||
596 | command_ps[pos-strlen(matchBuf)]=0; | ||
597 | |||
598 | /* tail line */ | ||
599 | strcpy(matchBuf, command_ps+pos); | ||
600 | |||
601 | /* add match */ | ||
602 | strcat(command_ps, matches[0]); | ||
603 | /* add tail */ | ||
604 | strcat(command_ps, matchBuf); | ||
605 | |||
361 | /* write out the matched command */ | 606 | /* write out the matched command */ |
362 | strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos); | 607 | len=strlen(command_ps); |
363 | *len=strlen(command); | 608 | recalc_pos = len-recalc_pos+pos; |
364 | *cursor=*len; | 609 | input_end(); /* write */ |
365 | xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos); | 610 | while(recalc_pos<cursor) |
611 | input_backward(); | ||
366 | return; | 612 | return; |
367 | } | 613 | } |
368 | } else { | 614 | } else { |
@@ -371,28 +617,28 @@ static void input_tab(char* command, char* prompt, int outputFd, int *cursor, in | |||
371 | * available choices... */ | 617 | * available choices... */ |
372 | if ( matches && num_matches>0 ) { | 618 | if ( matches && num_matches>0 ) { |
373 | int i, col; | 619 | int i, col; |
620 | int sav_cursor = cursor; | ||
374 | 621 | ||
375 | /* Go to the next line */ | 622 | /* Go to the next line */ |
376 | xwrite(outputFd, "\n", 1); | 623 | goto_new_line(); |
377 | /* Print the list of matches */ | ||
378 | for (i=0,col=0; i<num_matches; i++) { | 624 | for (i=0,col=0; i<num_matches; i++) { |
379 | char foo[17]; | 625 | printf("%s ", matches[i]); |
380 | sprintf(foo, "%-14s ", matches[i]); | 626 | col += strlen(matches[i])+2; |
381 | col += xwrite(outputFd, foo, strlen(foo)); | 627 | col -= (col/cmdedit_termw)*cmdedit_termw; |
382 | if (col > 60 && matches[i+1] != NULL) { | 628 | if (col > 60 && matches[i+1] != NULL) { |
383 | xwrite(outputFd, "\n", 1); | 629 | putchar('\n'); |
384 | col = 0; | 630 | col = 0; |
385 | } | 631 | } |
386 | } | 632 | } |
387 | /* Go to the next line */ | 633 | /* Go to the next line and rewrite the prompt */ |
388 | xwrite(outputFd, "\n", 1); | 634 | printf("\n%s", cmdedit_prompt); |
389 | /* Rewrite the prompt */ | 635 | cmdedit_x = cmdedit_prmt_len; |
390 | xwrite(outputFd, prompt, strlen(prompt)); | 636 | cmdedit_y = 0; |
391 | /* Rewrite the command */ | 637 | cursor = 0; |
392 | xwrite(outputFd, command, *len); | 638 | input_end(); /* Rewrite the command */ |
393 | /* Put the cursor back to where it used to be */ | 639 | /* Put the cursor back to where it used to be */ |
394 | for (cursor=len; *cursor > pos; cursor--) | 640 | while (sav_cursor < cursor) |
395 | xwrite(outputFd, "\b", 1); | 641 | input_backward(); |
396 | } | 642 | } |
397 | } | 643 | } |
398 | } | 644 | } |
@@ -435,18 +681,19 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
435 | { | 681 | { |
436 | 682 | ||
437 | int inputFd=fileno(stdin); | 683 | int inputFd=fileno(stdin); |
438 | int outputFd=fileno(stdout); | 684 | |
439 | int nr = 0; | ||
440 | int len = 0; | ||
441 | int j = 0; | 685 | int j = 0; |
442 | int cursor = 0; | ||
443 | int break_out = 0; | 686 | int break_out = 0; |
444 | int ret = 0; | 687 | int ret = 0; |
445 | int lastWasTab = FALSE; | 688 | int lastWasTab = FALSE; |
446 | char c = 0; | 689 | char c = 0; |
447 | struct history *hp = his_end; | 690 | struct history *hp = his_end; |
448 | 691 | ||
449 | if (!reset_term) { | 692 | len = 0; |
693 | cursor = 0; | ||
694 | command_ps = command; | ||
695 | |||
696 | if (new_settings.c_cc[VMIN]==0) { | ||
450 | 697 | ||
451 | getTermSettings(inputFd, (void*) &initial_settings); | 698 | getTermSettings(inputFd, (void*) &initial_settings); |
452 | memcpy(&new_settings, &initial_settings, sizeof(struct termios)); | 699 | memcpy(&new_settings, &initial_settings, sizeof(struct termios)); |
@@ -455,17 +702,26 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
455 | new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */ | 702 | new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */ |
456 | new_settings.c_lflag &= ~ICANON; /* unbuffered input */ | 703 | new_settings.c_lflag &= ~ICANON; /* unbuffered input */ |
457 | new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */ | 704 | new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */ |
458 | reset_term = 1; | ||
459 | } | 705 | } |
460 | setTermSettings(inputFd, (void*) &new_settings); | 706 | setTermSettings(inputFd, (void*) &new_settings); |
707 | handlers_sets |= SET_RESET_TERM; | ||
461 | 708 | ||
462 | memset(command, 0, BUFSIZ); | 709 | memset(command, 0, BUFSIZ); |
463 | 710 | ||
711 | cmdedit_init(); | ||
712 | |||
464 | /* Print out the command prompt */ | 713 | /* Print out the command prompt */ |
465 | xwrite(outputFd, prompt, strlen(prompt)); | 714 | cmdedit_prompt = prompt; |
715 | cmdedit_prmt_len = strlen(prompt); | ||
716 | printf("%s", prompt); | ||
717 | cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */ | ||
718 | cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */ | ||
719 | |||
466 | 720 | ||
467 | while (1) { | 721 | while (1) { |
468 | 722 | ||
723 | fflush(stdout); /* buffered out to fast */ | ||
724 | |||
469 | if ((ret = read(inputFd, &c, 1)) < 1) | 725 | if ((ret = read(inputFd, &c, 1)) < 1) |
470 | return; | 726 | return; |
471 | //fprintf(stderr, "got a '%c' (%d)\n", c, c); | 727 | //fprintf(stderr, "got a '%c' (%d)\n", c, c); |
@@ -474,16 +730,18 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
474 | case '\n': | 730 | case '\n': |
475 | case '\r': | 731 | case '\r': |
476 | /* Enter */ | 732 | /* Enter */ |
477 | *(command + len++ + 1) = c; | 733 | *(command + len) = c; |
478 | xwrite(outputFd, &c, 1); | 734 | len++; |
735 | input_end (); | ||
479 | break_out = 1; | 736 | break_out = 1; |
480 | break; | 737 | break; |
481 | case 1: | 738 | case 1: |
482 | /* Control-a -- Beginning of line */ | 739 | /* Control-a -- Beginning of line */ |
483 | input_home(outputFd, &cursor); | 740 | input_home(); |
741 | break; | ||
484 | case 2: | 742 | case 2: |
485 | /* Control-b -- Move back one character */ | 743 | /* Control-b -- Move back one character */ |
486 | input_backward(outputFd, &cursor); | 744 | input_backward(); |
487 | break; | 745 | break; |
488 | case 3: | 746 | case 3: |
489 | /* Control-c -- stop gathering input */ | 747 | /* Control-c -- stop gathering input */ |
@@ -492,11 +750,11 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
492 | shell_context = 0; | 750 | shell_context = 0; |
493 | 751 | ||
494 | /* Go to the next line */ | 752 | /* Go to the next line */ |
495 | xwrite(outputFd, "\n", 1); | 753 | goto_new_line(); |
496 | 754 | ||
497 | #if 0 | 755 | #if 0 |
498 | /* Rewrite the prompt */ | 756 | /* Rewrite the prompt */ |
499 | xwrite(outputFd, prompt, strlen(prompt)); | 757 | printf("%s", prompt); |
500 | 758 | ||
501 | /* Reset the command string */ | 759 | /* Reset the command string */ |
502 | memset(command, 0, BUFSIZ); | 760 | memset(command, 0, BUFSIZ); |
@@ -508,29 +766,28 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
508 | /* Control-d -- Delete one character, or exit | 766 | /* Control-d -- Delete one character, or exit |
509 | * if the len=0 and no chars to delete */ | 767 | * if the len=0 and no chars to delete */ |
510 | if (len == 0) { | 768 | if (len == 0) { |
511 | xwrite(outputFd, "exit", 4); | 769 | printf("exit"); |
512 | clean_up_and_die(0); | 770 | clean_up_and_die(0); |
513 | } else { | 771 | } else { |
514 | input_delete(command, outputFd, cursor, &len); | 772 | input_delete(); |
515 | } | 773 | } |
516 | break; | 774 | break; |
517 | case 5: | 775 | case 5: |
518 | /* Control-e -- End of line */ | 776 | /* Control-e -- End of line */ |
519 | input_end(outputFd, &cursor, len); | 777 | input_end(); |
520 | break; | 778 | break; |
521 | case 6: | 779 | case 6: |
522 | /* Control-f -- Move forward one character */ | 780 | /* Control-f -- Move forward one character */ |
523 | input_forward(outputFd, &cursor, len); | 781 | input_forward(); |
524 | break; | 782 | break; |
525 | case '\b': | 783 | case '\b': |
526 | case DEL: | 784 | case DEL: |
527 | /* Control-h and DEL */ | 785 | /* Control-h and DEL */ |
528 | input_backspace(command, outputFd, &cursor, &len); | 786 | input_backspace(); |
529 | break; | 787 | break; |
530 | case '\t': | 788 | case '\t': |
531 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | 789 | #ifdef BB_FEATURE_SH_TAB_COMPLETION |
532 | input_tab(command, prompt, outputFd, &cursor, | 790 | input_tab(lastWasTab); |
533 | &len, lastWasTab); | ||
534 | #endif | 791 | #endif |
535 | break; | 792 | break; |
536 | case 14: | 793 | case 14: |
@@ -539,7 +796,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
539 | get_next_history(&hp, command); | 796 | get_next_history(&hp, command); |
540 | goto rewrite_line; | 797 | goto rewrite_line; |
541 | } else { | 798 | } else { |
542 | xwrite(outputFd, "\007", 1); | 799 | beep(); |
543 | } | 800 | } |
544 | break; | 801 | break; |
545 | case 16: | 802 | case 16: |
@@ -548,7 +805,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
548 | get_previous_history(&hp, command); | 805 | get_previous_history(&hp, command); |
549 | goto rewrite_line; | 806 | goto rewrite_line; |
550 | } else { | 807 | } else { |
551 | xwrite(outputFd, "\007", 1); | 808 | beep(); |
552 | } | 809 | } |
553 | break; | 810 | break; |
554 | case ESC:{ | 811 | case ESC:{ |
@@ -567,7 +824,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
567 | get_previous_history(&hp, command); | 824 | get_previous_history(&hp, command); |
568 | goto rewrite_line; | 825 | goto rewrite_line; |
569 | } else { | 826 | } else { |
570 | xwrite(outputFd, "\007", 1); | 827 | beep(); |
571 | } | 828 | } |
572 | break; | 829 | break; |
573 | case 'B': | 830 | case 'B': |
@@ -576,49 +833,52 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
576 | get_next_history(&hp, command); | 833 | get_next_history(&hp, command); |
577 | goto rewrite_line; | 834 | goto rewrite_line; |
578 | } else { | 835 | } else { |
579 | xwrite(outputFd, "\007", 1); | 836 | beep(); |
580 | } | 837 | } |
581 | break; | 838 | break; |
582 | 839 | ||
583 | /* Rewrite the line with the selected history item */ | 840 | /* Rewrite the line with the selected history item */ |
584 | rewrite_line: | 841 | rewrite_line: |
585 | /* erase old command from command line */ | 842 | /* return to begin of line */ |
586 | len = strlen(command)-strlen(hp->s); | 843 | input_home (); |
587 | 844 | /* for next memmoves without set '\0' */ | |
588 | while (len>cursor) | 845 | memset (command, 0, BUFSIZ); |
589 | input_delete(command, outputFd, cursor, &len); | 846 | /* change command */ |
590 | while (cursor>0) | 847 | strcpy (command, hp->s); |
591 | input_backspace(command, outputFd, &cursor, &len); | ||
592 | input_home(outputFd, &cursor); | ||
593 | |||
594 | /* write new command */ | 848 | /* write new command */ |
595 | strcpy(command, hp->s); | 849 | for (j=0; command[j]; j++) |
596 | len = strlen(hp->s); | 850 | cmdedit_set_out_char(command[j], 0); |
597 | xwrite(outputFd, command, len); | 851 | ret = cursor; |
598 | cursor = len; | 852 | /* erase tail if required */ |
853 | for (j = ret; j < len; j++) | ||
854 | cmdedit_set_out_char(' ', 0); | ||
855 | /* and backward cursor */ | ||
856 | for (j = ret; j < len; j++) | ||
857 | input_backward(); | ||
858 | len = cursor; /* set new len */ | ||
599 | break; | 859 | break; |
600 | case 'C': | 860 | case 'C': |
601 | /* Right Arrow -- Move forward one character */ | 861 | /* Right Arrow -- Move forward one character */ |
602 | input_forward(outputFd, &cursor, len); | 862 | input_forward(); |
603 | break; | 863 | break; |
604 | case 'D': | 864 | case 'D': |
605 | /* Left Arrow -- Move back one character */ | 865 | /* Left Arrow -- Move back one character */ |
606 | input_backward(outputFd, &cursor); | 866 | input_backward(); |
607 | break; | 867 | break; |
608 | case '3': | 868 | case '3': |
609 | /* Delete */ | 869 | /* Delete */ |
610 | input_delete(command, outputFd, cursor, &len); | 870 | input_delete(); |
611 | break; | 871 | break; |
612 | case '1': | 872 | case '1': |
613 | /* Home (Ctrl-A) */ | 873 | /* Home (Ctrl-A) */ |
614 | input_home(outputFd, &cursor); | 874 | input_home(); |
615 | break; | 875 | break; |
616 | case '4': | 876 | case '4': |
617 | /* End (Ctrl-E) */ | 877 | /* End (Ctrl-E) */ |
618 | input_end(outputFd, &cursor, len); | 878 | input_end(); |
619 | break; | 879 | break; |
620 | default: | 880 | default: |
621 | xwrite(outputFd, "\007", 1); | 881 | beep(); |
622 | } | 882 | } |
623 | if (c == '1' || c == '3' || c == '4') | 883 | if (c == '1' || c == '3' || c == '4') |
624 | if ((ret = read(inputFd, &c, 1)) < 1) | 884 | if ((ret = read(inputFd, &c, 1)) < 1) |
@@ -631,14 +891,14 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
631 | switch (c) { | 891 | switch (c) { |
632 | case 'H': | 892 | case 'H': |
633 | /* Home (xterm) */ | 893 | /* Home (xterm) */ |
634 | input_home(outputFd, &cursor); | 894 | input_home(); |
635 | break; | 895 | break; |
636 | case 'F': | 896 | case 'F': |
637 | /* End (xterm) */ | 897 | /* End (xterm) */ |
638 | input_end(outputFd, &cursor, len); | 898 | input_end(); |
639 | break; | 899 | break; |
640 | default: | 900 | default: |
641 | xwrite(outputFd, "\007", 1); | 901 | beep(); |
642 | } | 902 | } |
643 | } | 903 | } |
644 | c = 0; | 904 | c = 0; |
@@ -658,20 +918,20 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
658 | 918 | ||
659 | if (cursor == (len - 1)) { /* Append if at the end of the line */ | 919 | if (cursor == (len - 1)) { /* Append if at the end of the line */ |
660 | *(command + cursor) = c; | 920 | *(command + cursor) = c; |
921 | cmdedit_set_out_char(c, command[cursor+1]); | ||
661 | } else { /* Insert otherwise */ | 922 | } else { /* Insert otherwise */ |
662 | memmove(command + cursor + 1, command + cursor, | 923 | memmove(command + cursor + 1, command + cursor, |
663 | len - cursor - 1); | 924 | len - cursor - 1); |
664 | 925 | ||
665 | *(command + cursor) = c; | 926 | *(command + cursor) = c; |
666 | 927 | j = cursor+1; | |
667 | for (j = cursor; j < len; j++) | 928 | /* rewrite from cursor */ |
668 | xwrite(outputFd, command + j, 1); | 929 | input_end (); |
669 | for (; j > cursor; j--) | 930 | /* to prev x pos + 1 */ |
670 | xwrite(outputFd, "\033[D", 3); | 931 | while(cursor > j) |
932 | input_backward(); | ||
671 | } | 933 | } |
672 | 934 | ||
673 | cursor++; | ||
674 | xwrite(outputFd, &c, 1); | ||
675 | break; | 935 | break; |
676 | } | 936 | } |
677 | if (c == '\t') | 937 | if (c == '\t') |
@@ -683,15 +943,18 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
683 | break; | 943 | break; |
684 | } | 944 | } |
685 | 945 | ||
686 | nr = len + 1; | 946 | setTermSettings (inputFd, (void *) &initial_settings); |
687 | setTermSettings(inputFd, (void *) &initial_settings); | 947 | handlers_sets &= ~SET_RESET_TERM; |
688 | reset_term = 0; | ||
689 | |||
690 | 948 | ||
691 | /* Handle command history log */ | 949 | /* Handle command history log */ |
692 | if (*(command)) { | 950 | if (len>1) { /* no put empty line (only '\n') */ |
693 | 951 | ||
694 | struct history *h = his_end; | 952 | struct history *h = his_end; |
953 | char *ss; | ||
954 | |||
955 | command[len-1] = 0; /* destroy end '\n' */ | ||
956 | ss = strdup(command); /* duplicate without '\n' */ | ||
957 | command[len-1] = '\n'; /* restore '\n' */ | ||
695 | 958 | ||
696 | if (!h) { | 959 | if (!h) { |
697 | /* No previous history -- this memory is never freed */ | 960 | /* No previous history -- this memory is never freed */ |
@@ -699,7 +962,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
699 | h->n = xmalloc(sizeof(struct history)); | 962 | h->n = xmalloc(sizeof(struct history)); |
700 | 963 | ||
701 | h->p = NULL; | 964 | h->p = NULL; |
702 | h->s = strdup(command); | 965 | h->s = ss; |
703 | h->n->p = h; | 966 | h->n->p = h; |
704 | h->n->n = NULL; | 967 | h->n->n = NULL; |
705 | h->n->s = NULL; | 968 | h->n->s = NULL; |
@@ -712,7 +975,7 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
712 | h->n->p = h; | 975 | h->n->p = h; |
713 | h->n->n = NULL; | 976 | h->n->n = NULL; |
714 | h->n->s = NULL; | 977 | h->n->s = NULL; |
715 | h->s = strdup(command); | 978 | h->s = ss; |
716 | his_end = h->n; | 979 | his_end = h->n; |
717 | 980 | ||
718 | /* After max history, remove the oldest command */ | 981 | /* After max history, remove the oldest command */ |
@@ -733,38 +996,21 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) | |||
733 | return; | 996 | return; |
734 | } | 997 | } |
735 | 998 | ||
736 | extern void cmdedit_init(void) | ||
737 | { | ||
738 | win_changed(0); | ||
739 | signal(SIGWINCH, win_changed); | ||
740 | 999 | ||
741 | if(exithandler_set == 0) { | 1000 | /* Undo the effects of cmdedit_init(). */ |
742 | atexit(cmdedit_reset_term); /* be sure to do this only once */ | ||
743 | exithandler_set = 1; | ||
744 | } | ||
745 | signal(SIGKILL, clean_up_and_die); | ||
746 | signal(SIGINT, clean_up_and_die); | ||
747 | signal(SIGQUIT, clean_up_and_die); | ||
748 | signal(SIGTERM, clean_up_and_die); | ||
749 | } | ||
750 | |||
751 | /* | ||
752 | ** Undo the effects of cmdedit_init() as good as we can: | ||
753 | ** I am not aware of a way to revoke an atexit() handler, | ||
754 | ** but, fortunately, our particular handler can be made | ||
755 | ** a no-op by setting reset_term = 0. | ||
756 | */ | ||
757 | extern void cmdedit_terminate(void) | 1001 | extern void cmdedit_terminate(void) |
758 | { | 1002 | { |
759 | cmdedit_reset_term(); | 1003 | cmdedit_reset_term(); |
760 | reset_term = 0; | 1004 | if((handlers_sets & SET_TERM_HANDLERS)!=0) { |
761 | signal(SIGKILL, SIG_DFL); | 1005 | signal(SIGKILL, SIG_DFL); |
762 | signal(SIGINT, SIG_DFL); | 1006 | signal(SIGINT, SIG_DFL); |
763 | signal(SIGQUIT, SIG_DFL); | 1007 | signal(SIGQUIT, SIG_DFL); |
764 | signal(SIGTERM, SIG_DFL); | 1008 | signal(SIGTERM, SIG_DFL); |
765 | signal(SIGWINCH, SIG_DFL); | 1009 | signal(SIGWINCH, SIG_DFL); |
1010 | handlers_sets &= ~SET_TERM_HANDLERS; | ||
1011 | } | ||
766 | } | 1012 | } |
767 | 1013 | ||
768 | 1014 | ||
769 | 1015 | ||
770 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ | 1016 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ |