diff options
author | Erik Andersen <andersen@codepoet.org> | 2000-04-12 17:49:52 +0000 |
---|---|---|
committer | Erik Andersen <andersen@codepoet.org> | 2000-04-12 17:49:52 +0000 |
commit | f0657d322937ad2ff865be9f89cc8c979693088e (patch) | |
tree | a82cc2f19f97a86d394b66a87af8321d91b4d9c4 | |
parent | a2685735b1ace4323f0c6fae6c31e0888ed62c34 (diff) | |
download | busybox-w32-f0657d322937ad2ff865be9f89cc8c979693088e.tar.gz busybox-w32-f0657d322937ad2ff865be9f89cc8c979693088e.tar.bz2 busybox-w32-f0657d322937ad2ff865be9f89cc8c979693088e.zip |
Some enhancements I've been working on over the weekend,
-Erik
-rw-r--r-- | busybox.def.h | 2 | ||||
-rw-r--r-- | cmdedit.c | 619 | ||||
-rw-r--r-- | cmdedit.h | 46 | ||||
-rw-r--r-- | internal.h | 6 | ||||
-rw-r--r-- | lash.c | 39 | ||||
-rw-r--r-- | sh.c | 39 | ||||
-rw-r--r-- | shell/cmdedit.c | 619 | ||||
-rw-r--r-- | shell/cmdedit.h | 46 | ||||
-rw-r--r-- | shell/lash.c | 39 |
9 files changed, 783 insertions, 672 deletions
diff --git a/busybox.def.h b/busybox.def.h index eebe9b973..9a2ba3f69 100644 --- a/busybox.def.h +++ b/busybox.def.h | |||
@@ -187,7 +187,7 @@ | |||
187 | //#define BB_FEATURE_SORT_REVERSE | 187 | //#define BB_FEATURE_SORT_REVERSE |
188 | // | 188 | // |
189 | // Enable command line editing in the shell | 189 | // Enable command line editing in the shell |
190 | #define BB_FEATURE_SH_COMMAND_EDITING | 190 | //#define BB_FEATURE_SH_COMMAND_EDITING |
191 | // | 191 | // |
192 | // Enable tab completion in the shell (not yet working very well) | 192 | // Enable tab completion in the shell (not yet working very well) |
193 | //#define BB_FEATURE_SH_TAB_COMPLETION | 193 | //#define BB_FEATURE_SH_TAB_COMPLETION |
@@ -53,9 +53,10 @@ static struct history *his_front = NULL; /* First element in command line list * | |||
53 | static struct history *his_end = NULL; /* Last element in command line list */ | 53 | static struct history *his_end = NULL; /* Last element in command line list */ |
54 | static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */ | 54 | static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */ |
55 | 55 | ||
56 | static int cmdedit_termw = 80; /* actual terminal width */ | ||
57 | static int cmdedit_scroll = 27; /* width of EOL scrolling region */ | ||
56 | static int history_counter = 0; /* Number of commands in history list */ | 58 | static int history_counter = 0; /* Number of commands in history list */ |
57 | static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ | 59 | static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ |
58 | char *parsenextc; /* copy of parsefile->nextc */ | ||
59 | 60 | ||
60 | struct history { | 61 | struct history { |
61 | char *s; | 62 | char *s; |
@@ -63,85 +64,42 @@ struct history { | |||
63 | struct history *n; | 64 | struct history *n; |
64 | }; | 65 | }; |
65 | 66 | ||
67 | #define xwrite write | ||
66 | 68 | ||
67 | /* Version of write which resumes after a signal is caught. */ | 69 | void |
68 | int xwrite(int fd, char *buf, int nbytes) | 70 | cmdedit_setwidth(int w) |
69 | { | 71 | { |
70 | int ntry; | 72 | if (w > 20) { |
71 | int i; | 73 | cmdedit_termw = w; |
72 | int n; | 74 | cmdedit_scroll = w / 3; |
73 | 75 | } else { | |
74 | n = nbytes; | 76 | errorMsg("\n*** Error: minimum screen width is 21\n"); |
75 | ntry = 0; | 77 | } |
76 | for (;;) { | ||
77 | i = write(fd, buf, n); | ||
78 | if (i > 0) { | ||
79 | if ((n -= i) <= 0) | ||
80 | return nbytes; | ||
81 | buf += i; | ||
82 | ntry = 0; | ||
83 | } else if (i == 0) { | ||
84 | if (++ntry > 10) | ||
85 | return nbytes - n; | ||
86 | } else if (errno != EINTR) { | ||
87 | return -1; | ||
88 | } | ||
89 | } | ||
90 | } | 78 | } |
91 | 79 | ||
92 | |||
93 | /* Version of ioctl that retries after a signal is caught. */ | ||
94 | int xioctl(int fd, unsigned long request, char *arg) | ||
95 | { | ||
96 | int i; | ||
97 | |||
98 | while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); | ||
99 | return i; | ||
100 | } | ||
101 | |||
102 | |||
103 | void cmdedit_reset_term(void) | 80 | void cmdedit_reset_term(void) |
104 | { | 81 | { |
105 | if (reset_term) | 82 | if (reset_term) |
106 | xioctl(fileno(stdin), TCSETA, (void *) &old_term); | 83 | ioctl(fileno(stdin), TCSETA, (void *) &old_term); |
107 | } | 84 | } |
108 | 85 | ||
109 | void prepareToDie(int sig) | 86 | void clean_up_and_die(int sig) |
110 | { | 87 | { |
111 | cmdedit_reset_term(); | 88 | cmdedit_reset_term(); |
112 | fprintf(stdout, "\n"); | 89 | fprintf(stdout, "\n"); |
113 | exit(TRUE); | 90 | exit(TRUE); |
114 | } | 91 | } |
115 | 92 | ||
93 | /* Go to HOME position */ | ||
116 | void input_home(int outputFd, int *cursor) | 94 | void input_home(int outputFd, int *cursor) |
117 | { /* Command line input routines */ | 95 | { |
118 | while (*cursor > 0) { | 96 | while (*cursor > 0) { |
119 | xwrite(outputFd, "\b", 1); | 97 | xwrite(outputFd, "\b", 1); |
120 | --*cursor; | 98 | --*cursor; |
121 | } | 99 | } |
122 | } | 100 | } |
123 | 101 | ||
124 | 102 | /* Go to END position */ | |
125 | void input_delete(int outputFd, int cursor) | ||
126 | { | ||
127 | int j = 0; | ||
128 | |||
129 | memmove(parsenextc + cursor, parsenextc + cursor + 1, | ||
130 | BUFSIZ - cursor - 1); | ||
131 | for (j = cursor; j < (BUFSIZ - 1); j++) { | ||
132 | if (!*(parsenextc + j)) | ||
133 | break; | ||
134 | else | ||
135 | xwrite(outputFd, (parsenextc + j), 1); | ||
136 | } | ||
137 | |||
138 | xwrite(outputFd, " \b", 2); | ||
139 | |||
140 | while (j-- > cursor) | ||
141 | xwrite(outputFd, "\b", 1); | ||
142 | } | ||
143 | |||
144 | |||
145 | void input_end(int outputFd, int *cursor, int len) | 103 | void input_end(int outputFd, int *cursor, int len) |
146 | { | 104 | { |
147 | while (*cursor < len) { | 105 | while (*cursor < len) { |
@@ -150,22 +108,22 @@ void input_end(int outputFd, int *cursor, int len) | |||
150 | } | 108 | } |
151 | } | 109 | } |
152 | 110 | ||
153 | 111 | /* Delete the char in back of the cursor */ | |
154 | void input_backspace(int outputFd, int *cursor, int *len) | 112 | void input_backspace(char* command, int outputFd, int *cursor, int *len) |
155 | { | 113 | { |
156 | int j = 0; | 114 | int j = 0; |
157 | 115 | ||
158 | if (*cursor > 0) { | 116 | if (*cursor > 0) { |
159 | xwrite(outputFd, "\b \b", 3); | 117 | xwrite(outputFd, "\b \b", 3); |
160 | --*cursor; | 118 | --*cursor; |
161 | memmove(parsenextc + *cursor, parsenextc + *cursor + 1, | 119 | memmove(command + *cursor, command + *cursor + 1, |
162 | BUFSIZ - *cursor + 1); | 120 | BUFSIZ - *cursor + 1); |
163 | 121 | ||
164 | for (j = *cursor; j < (BUFSIZ - 1); j++) { | 122 | for (j = *cursor; j < (BUFSIZ - 1); j++) { |
165 | if (!*(parsenextc + j)) | 123 | if (!*(command + j)) |
166 | break; | 124 | break; |
167 | else | 125 | else |
168 | xwrite(outputFd, (parsenextc + j), 1); | 126 | xwrite(outputFd, (command + j), 1); |
169 | } | 127 | } |
170 | 128 | ||
171 | xwrite(outputFd, " \b", 2); | 129 | xwrite(outputFd, " \b", 2); |
@@ -177,18 +135,61 @@ void input_backspace(int outputFd, int *cursor, int *len) | |||
177 | } | 135 | } |
178 | } | 136 | } |
179 | 137 | ||
180 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | 138 | /* Delete the char in front of the cursor */ |
139 | void input_delete(char* command, int outputFd, int cursor, int *len) | ||
140 | { | ||
141 | int j = 0; | ||
181 | 142 | ||
182 | char** username_completion_matches(char* command, int *num_matches) | 143 | if (cursor == *len) |
144 | return; | ||
145 | |||
146 | memmove(command + cursor, command + cursor + 1, | ||
147 | BUFSIZ - cursor - 1); | ||
148 | for (j = cursor; j < (BUFSIZ - 1); j++) { | ||
149 | if (!*(command + j)) | ||
150 | break; | ||
151 | else | ||
152 | xwrite(outputFd, (command + j), 1); | ||
153 | } | ||
154 | |||
155 | xwrite(outputFd, " \b", 2); | ||
156 | |||
157 | while (j-- > cursor) | ||
158 | xwrite(outputFd, "\b", 1); | ||
159 | --*len; | ||
160 | } | ||
161 | |||
162 | /* Move forward one charactor */ | ||
163 | void input_forward(int outputFd, int *cursor, int len) | ||
164 | { | ||
165 | if (*cursor < len) { | ||
166 | xwrite(outputFd, "\033[C", 3); | ||
167 | ++*cursor; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | /* Move back one charactor */ | ||
172 | void input_backward(int outputFd, int *cursor) | ||
173 | { | ||
174 | if (*cursor > 0) { | ||
175 | xwrite(outputFd, "\033[D", 3); | ||
176 | --*cursor; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | |||
181 | |||
182 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | ||
183 | char** username_tab_completion(char* command, int *num_matches) | ||
183 | { | 184 | { |
184 | char **matches = (char **) NULL; | 185 | char **matches = (char **) NULL; |
185 | *num_matches=0; | 186 | *num_matches=0; |
186 | fprintf(stderr, "\nin username_completion_matches\n"); | 187 | fprintf(stderr, "\nin username_tab_completion\n"); |
187 | return (matches); | 188 | return (matches); |
188 | } | 189 | } |
189 | 190 | ||
190 | #include <dirent.h> | 191 | #include <dirent.h> |
191 | char** find_path_executable_n_cwd_matches(char* command, int *num_matches) | 192 | char** exe_n_cwd_tab_completion(char* command, int *num_matches) |
192 | { | 193 | { |
193 | char *dirName; | 194 | char *dirName; |
194 | char **matches = (char **) NULL; | 195 | char **matches = (char **) NULL; |
@@ -227,6 +228,170 @@ char** find_path_executable_n_cwd_matches(char* command, int *num_matches) | |||
227 | 228 | ||
228 | return (matches); | 229 | return (matches); |
229 | } | 230 | } |
231 | |||
232 | void input_tab(char* command, int outputFd, int *cursor, int *len) | ||
233 | { | ||
234 | /* Do TAB completion */ | ||
235 | static int num_matches=0; | ||
236 | static char **matches = (char **) NULL; | ||
237 | int pos = cursor; | ||
238 | |||
239 | |||
240 | if (lastWasTab == FALSE) { | ||
241 | char *tmp, *tmp1, *matchBuf; | ||
242 | |||
243 | /* For now, we will not bother with trying to distinguish | ||
244 | * whether the cursor is in/at a command extression -- we | ||
245 | * will always try all possable matches. If you don't like | ||
246 | * that then feel free to fix it. | ||
247 | */ | ||
248 | |||
249 | /* Make a local copy of the string -- up | ||
250 | * to the position of the cursor */ | ||
251 | matchBuf = (char *) calloc(BUFSIZ, sizeof(char)); | ||
252 | strncpy(matchBuf, command, cursor); | ||
253 | tmp=matchBuf; | ||
254 | |||
255 | /* skip past any command seperator tokens */ | ||
256 | while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { | ||
257 | tmp=++tmp1; | ||
258 | /* skip any leading white space */ | ||
259 | while (*tmp && isspace(*tmp)) | ||
260 | ++tmp; | ||
261 | } | ||
262 | |||
263 | /* skip any leading white space */ | ||
264 | while (*tmp && isspace(*tmp)) | ||
265 | ++tmp; | ||
266 | |||
267 | /* Free up any memory already allocated */ | ||
268 | if (matches) { | ||
269 | free(matches); | ||
270 | matches = (char **) NULL; | ||
271 | } | ||
272 | |||
273 | /* If the word starts with `~' and there is no slash in the word, | ||
274 | * then try completing this word as a username. */ | ||
275 | |||
276 | /* FIXME -- this check is broken! */ | ||
277 | if (*tmp == '~' && !strchr(tmp, '/')) | ||
278 | matches = username_tab_completion(tmp, &num_matches); | ||
279 | |||
280 | /* Try to match any executable in our path and everything | ||
281 | * in the current working directory that matches. */ | ||
282 | if (!matches) | ||
283 | matches = exe_n_cwd_tab_completion(tmp, &num_matches); | ||
284 | |||
285 | /* Don't leak memory */ | ||
286 | free( matchBuf); | ||
287 | |||
288 | /* Did we find exactly one match? */ | ||
289 | if (matches && num_matches==1) { | ||
290 | /* write out the matched command */ | ||
291 | strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos); | ||
292 | len=strlen(command); | ||
293 | cursor=len; | ||
294 | xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos); | ||
295 | break; | ||
296 | } | ||
297 | } else { | ||
298 | /* Ok -- the last char was a TAB. Since they | ||
299 | * just hit TAB again, print a list of all the | ||
300 | * available choices... */ | ||
301 | if ( matches && num_matches>0 ) { | ||
302 | int i, col; | ||
303 | |||
304 | /* Go to the next line */ | ||
305 | xwrite(outputFd, "\n", 1); | ||
306 | /* Print the list of matches */ | ||
307 | for (i=0,col=0; i<num_matches; i++) { | ||
308 | char foo[17]; | ||
309 | sprintf(foo, "%-14s ", matches[i]); | ||
310 | col += xwrite(outputFd, foo, strlen(foo)); | ||
311 | if (col > 60 && matches[i+1] != NULL) { | ||
312 | xwrite(outputFd, "\n", 1); | ||
313 | col = 0; | ||
314 | } | ||
315 | } | ||
316 | /* Go to the next line */ | ||
317 | xwrite(outputFd, "\n", 1); | ||
318 | /* Rewrite the prompt */ | ||
319 | xwrite(outputFd, prompt, strlen(prompt)); | ||
320 | /* Rewrite the command */ | ||
321 | xwrite(outputFd, command, len); | ||
322 | /* Put the cursor back to where it used to be */ | ||
323 | for (cursor=len; cursor > pos; cursor--) | ||
324 | xwrite(outputFd, "\b", 1); | ||
325 | } | ||
326 | } | ||
327 | } | ||
328 | #endif | ||
329 | |||
330 | void get_previous_history(struct history **hp, char* command) | ||
331 | { | ||
332 | free((*hp)->s); | ||
333 | (*hp)->s = strdup(command); | ||
334 | *hp = (*hp)->p; | ||
335 | } | ||
336 | |||
337 | void get_next_history(struct history **hp, char* command) | ||
338 | { | ||
339 | free((*hp)->s); | ||
340 | (*hp)->s = strdup(command); | ||
341 | *hp = (*hp)->n; | ||
342 | |||
343 | cmdedit_redraw( NULL, hp->s, -2, -2); | ||
344 | } | ||
345 | |||
346 | #if 0 | ||
347 | /* prompt : if !=NULL, print the prompt | ||
348 | * command: the command line to be displayed | ||
349 | * where : where to display changes from. | ||
350 | * -1 for no change, -2 for new line | ||
351 | * cursor : desired location for the cursor. | ||
352 | * -1 for Beginning of line. | ||
353 | * -2 for End of Line, | ||
354 | */ | ||
355 | static void | ||
356 | cmdedit_redraw(char* prompt, char* command, int where, int cursor) | ||
357 | { | ||
358 | static char* last_command; | ||
359 | int cmdedit_width; | ||
360 | |||
361 | if (where == -2) { | ||
362 | /* Rewrite the prompt and clean up static variables */ | ||
363 | xwrite(outputFd, "\n", 1); | ||
364 | if (prompt) { | ||
365 | strcpy(last_command, prompt); | ||
366 | xwrite(outputFd, prompt, strlen(prompt)); | ||
367 | } else { | ||
368 | last_command[0] = '\0'; | ||
369 | xwrite(outputFd, "# ", 2); | ||
370 | } | ||
371 | cmdedit_width = cmdedit_termw - cmdedit_strlen(prompt); | ||
372 | } else if (strcmp(command, last_command) != 0) { | ||
373 | strcpy(last_command, prompt); | ||
374 | } | ||
375 | |||
376 | /* erase old command from command line */ | ||
377 | len = strlen(command)-strlen(last_command); | ||
378 | while (len>0) | ||
379 | input_backspace(command, outputFd, &cursor, &len); | ||
380 | input_home(outputFd, &cursor); | ||
381 | |||
382 | /* Rewrite the command */ | ||
383 | xwrite(outputFd, command+where, len); | ||
384 | |||
385 | /* Put the where it is supposed to be */ | ||
386 | for (cursor=len; cursor > where; cursor--) | ||
387 | xwrite(outputFd, "\b", 1); | ||
388 | |||
389 | /* write new command */ | ||
390 | strcpy(command, hp->s); | ||
391 | len = strlen(hp->s); | ||
392 | xwrite(outputFd, command+where, len); | ||
393 | cursor = len; | ||
394 | } | ||
230 | #endif | 395 | #endif |
231 | 396 | ||
232 | /* | 397 | /* |
@@ -245,12 +410,12 @@ char** find_path_executable_n_cwd_matches(char* command, int *num_matches) | |||
245 | * Furthermore, the "vi" command editing keys are not implemented. | 410 | * Furthermore, the "vi" command editing keys are not implemented. |
246 | * | 411 | * |
247 | * TODO: implement TAB command completion. :) | 412 | * TODO: implement TAB command completion. :) |
248 | * | ||
249 | */ | 413 | */ |
250 | extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | 414 | extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) |
251 | char command[BUFSIZ]) | ||
252 | { | 415 | { |
253 | 416 | ||
417 | int inputFd=fileno(stdin); | ||
418 | int outputFd=fileno(stdout); | ||
254 | int nr = 0; | 419 | int nr = 0; |
255 | int len = 0; | 420 | int len = 0; |
256 | int j = 0; | 421 | int j = 0; |
@@ -262,297 +427,162 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
262 | struct history *hp = his_end; | 427 | struct history *hp = his_end; |
263 | 428 | ||
264 | memset(command, 0, sizeof(command)); | 429 | memset(command, 0, sizeof(command)); |
265 | parsenextc = command; | ||
266 | if (!reset_term) { | 430 | if (!reset_term) { |
267 | xioctl(inputFd, TCGETA, (void *) &old_term); | 431 | ioctl(inputFd, TCGETA, (void *) &old_term); |
268 | memcpy(&new_term, &old_term, sizeof(struct termio)); | 432 | memcpy(&new_term, &old_term, sizeof(struct termio)); |
269 | 433 | ||
270 | new_term.c_cc[VMIN] = 1; | 434 | new_term.c_cc[VMIN] = 1; |
271 | new_term.c_cc[VTIME] = 0; | 435 | new_term.c_cc[VTIME] = 0; |
272 | new_term.c_lflag &= ~ICANON; /* unbuffered input */ | 436 | new_term.c_lflag &= ~ICANON; /* unbuffered input */ |
273 | new_term.c_lflag &= ~ECHO; | 437 | new_term.c_lflag &= ~ECHO; |
274 | xioctl(inputFd, TCSETA, (void *) &new_term); | ||
275 | reset_term = 1; | 438 | reset_term = 1; |
439 | ioctl(inputFd, TCSETA, (void *) &new_term); | ||
276 | } else { | 440 | } else { |
277 | xioctl(inputFd, TCSETA, (void *) &new_term); | 441 | ioctl(inputFd, TCSETA, (void *) &new_term); |
278 | } | 442 | } |
279 | 443 | ||
280 | memset(parsenextc, 0, BUFSIZ); | 444 | memset(command, 0, BUFSIZ); |
281 | 445 | ||
282 | while (1) { | 446 | while (1) { |
283 | 447 | ||
284 | if ((ret = read(inputFd, &c, 1)) < 1) | 448 | if ((ret = read(inputFd, &c, 1)) < 1) |
285 | return ret; | 449 | return; |
286 | |||
287 | fprintf(stderr, "\n\nkey=%d (%c)\n\n", c, c); | ||
288 | /* Go to the next line */ | ||
289 | xwrite(outputFd, "\n", 1); | ||
290 | /* Rewrite the prompt */ | ||
291 | xwrite(outputFd, prompt, strlen(prompt)); | ||
292 | /* Rewrite the command */ | ||
293 | xwrite(outputFd, parsenextc, len); | ||
294 | 450 | ||
295 | switch (c) { | 451 | switch (c) { |
452 | case '\n': | ||
453 | case '\r': | ||
454 | /* Enter */ | ||
455 | *(command + len++ + 1) = c; | ||
456 | xwrite(outputFd, &c, 1); | ||
457 | break_out = 1; | ||
458 | break; | ||
296 | case 1: | 459 | case 1: |
297 | /* Control-a -- Beginning of line */ | 460 | /* Control-a -- Beginning of line */ |
298 | input_home(outputFd, &cursor); | 461 | input_home(outputFd, &cursor); |
299 | case 5: | ||
300 | /* Control-e -- End of line */ | ||
301 | input_end(outputFd, &cursor, len); | ||
302 | break; | ||
303 | case 2: | 462 | case 2: |
304 | /* Control-b -- Move back one character */ | 463 | /* Control-b -- Move back one character */ |
305 | if (cursor > 0) { | 464 | input_backward(outputFd, &cursor); |
306 | xwrite(outputFd, "\033[D", 3); | 465 | break; |
307 | cursor--; | 466 | case 4: |
467 | /* Control-d -- Delete one character, or exit | ||
468 | * if the len=0 and no chars to delete */ | ||
469 | if (len == 0) { | ||
470 | xwrite(outputFd, "exit", 4); | ||
471 | clean_up_and_die(0); | ||
472 | } else { | ||
473 | input_delete(command, outputFd, cursor, &len); | ||
308 | } | 474 | } |
309 | break; | 475 | break; |
476 | case 5: | ||
477 | /* Control-e -- End of line */ | ||
478 | input_end(outputFd, &cursor, len); | ||
479 | break; | ||
310 | case 6: | 480 | case 6: |
311 | /* Control-f -- Move forward one character */ | 481 | /* Control-f -- Move forward one character */ |
312 | if (cursor < len) { | 482 | input_forward(outputFd, &cursor, len); |
313 | xwrite(outputFd, "\033[C", 3); | ||
314 | cursor++; | ||
315 | } | ||
316 | break; | 483 | break; |
317 | case 4: | 484 | case '\b': |
318 | /* Control-d -- Delete one character */ | 485 | case DEL: |
319 | if (cursor != len) { | 486 | /* control-h and DEL */ |
320 | input_delete(outputFd, cursor); | 487 | input_backspace(command, outputFd, &cursor, &len); |
321 | len--; | 488 | break; |
322 | } else if (len == 0) { | 489 | case '\t': |
323 | prepareToDie(0); | 490 | #ifdef BB_FEATURE_SH_TAB_COMPLETION |
324 | exit(0); | 491 | input_tab(command, outputFd, &cursor, &len); |
325 | } | 492 | #endif |
326 | break; | 493 | break; |
327 | case 14: | 494 | case 14: |
328 | /* Control-n -- Get next command */ | 495 | /* Control-n -- Get next command in history */ |
329 | if (hp && hp->n && hp->n->s) { | 496 | if (hp && hp->n && hp->n->s) { |
330 | free(hp->s); | 497 | get_next_history(&hp, command); |
331 | hp->s = strdup(parsenextc); | 498 | goto rewrite_line; |
332 | hp = hp->n; | 499 | } else { |
333 | goto hop; | 500 | xwrite(outputFd, "\007", 1); |
334 | } | 501 | } |
335 | break; | 502 | break; |
336 | case 16: | 503 | case 16: |
337 | /* Control-p -- Get previous command */ | 504 | /* Control-p -- Get previous command from history */ |
338 | if (hp && hp->p) { | 505 | if (hp && hp->p) { |
339 | free(hp->s); | 506 | get_previous_history(&hp, command); |
340 | hp->s = strdup(parsenextc); | 507 | goto rewrite_line; |
341 | hp = hp->p; | 508 | } else { |
342 | goto hop; | 509 | xwrite(outputFd, "\007", 1); |
343 | } | ||
344 | break; | ||
345 | case '\t': | ||
346 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | ||
347 | { | ||
348 | /* Do TAB completion */ | ||
349 | static int num_matches=0; | ||
350 | static char **matches = (char **) NULL; | ||
351 | int pos = cursor; | ||
352 | |||
353 | |||
354 | if (lastWasTab == FALSE) { | ||
355 | char *tmp, *tmp1, *matchBuf; | ||
356 | |||
357 | /* For now, we will not bother with trying to distinguish | ||
358 | * whether the cursor is in/at a command extression -- we | ||
359 | * will always try all possable matches. If you don't like | ||
360 | * that then feel free to fix it. | ||
361 | */ | ||
362 | |||
363 | /* Make a local copy of the string -- up | ||
364 | * to the position of the cursor */ | ||
365 | matchBuf = (char *) calloc(BUFSIZ, sizeof(char)); | ||
366 | strncpy(matchBuf, parsenextc, cursor); | ||
367 | tmp=matchBuf; | ||
368 | |||
369 | /* skip past any command seperator tokens */ | ||
370 | while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { | ||
371 | tmp=++tmp1; | ||
372 | /* skip any leading white space */ | ||
373 | while (*tmp && isspace(*tmp)) | ||
374 | ++tmp; | ||
375 | } | ||
376 | |||
377 | /* skip any leading white space */ | ||
378 | while (*tmp && isspace(*tmp)) | ||
379 | ++tmp; | ||
380 | |||
381 | /* Free up any memory already allocated */ | ||
382 | if (matches) { | ||
383 | free(matches); | ||
384 | matches = (char **) NULL; | ||
385 | } | ||
386 | |||
387 | /* If the word starts with `~' and there is no slash in the word, | ||
388 | * then try completing this word as a username. */ | ||
389 | |||
390 | /* FIXME -- this check is broken! */ | ||
391 | if (*tmp == '~' && !strchr(tmp, '/')) | ||
392 | matches = username_completion_matches(tmp, &num_matches); | ||
393 | |||
394 | /* Try to match any executable in our path and everything | ||
395 | * in the current working directory that matches. */ | ||
396 | if (!matches) | ||
397 | matches = find_path_executable_n_cwd_matches(tmp, &num_matches); | ||
398 | |||
399 | /* Don't leak memory */ | ||
400 | free( matchBuf); | ||
401 | |||
402 | /* Did we find exactly one match? */ | ||
403 | if (matches && num_matches==1) { | ||
404 | /* write out the matched command */ | ||
405 | strncpy(parsenextc+pos, matches[0]+pos, strlen(matches[0])-pos); | ||
406 | len=strlen(parsenextc); | ||
407 | cursor=len; | ||
408 | xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos); | ||
409 | break; | ||
410 | } | ||
411 | } else { | ||
412 | /* Ok -- the last char was a TAB. Since they | ||
413 | * just hit TAB again, print a list of all the | ||
414 | * available choices... */ | ||
415 | if ( matches && num_matches>0 ) { | ||
416 | int i, col; | ||
417 | |||
418 | /* Go to the next line */ | ||
419 | xwrite(outputFd, "\n", 1); | ||
420 | /* Print the list of matches */ | ||
421 | for (i=0,col=0; i<num_matches; i++) { | ||
422 | char foo[17]; | ||
423 | sprintf(foo, "%-14s ", matches[i]); | ||
424 | col += xwrite(outputFd, foo, strlen(foo)); | ||
425 | if (col > 60 && matches[i+1] != NULL) { | ||
426 | xwrite(outputFd, "\n", 1); | ||
427 | col = 0; | ||
428 | } | ||
429 | } | ||
430 | /* Go to the next line */ | ||
431 | xwrite(outputFd, "\n", 1); | ||
432 | /* Rewrite the prompt */ | ||
433 | xwrite(outputFd, prompt, strlen(prompt)); | ||
434 | /* Rewrite the command */ | ||
435 | xwrite(outputFd, parsenextc, len); | ||
436 | /* Put the cursor back to where it used to be */ | ||
437 | for (cursor=len; cursor > pos; cursor--) | ||
438 | xwrite(outputFd, "\b", 1); | ||
439 | } | ||
440 | } | ||
441 | break; | ||
442 | } | 510 | } |
443 | #else | ||
444 | break; | ||
445 | #endif | ||
446 | case '\b': | ||
447 | case DEL: | ||
448 | /* Backspace */ | ||
449 | input_backspace(outputFd, &cursor, &len); | ||
450 | break; | ||
451 | case '\n': | ||
452 | /* Enter */ | ||
453 | *(parsenextc + len++ + 1) = c; | ||
454 | xwrite(outputFd, &c, 1); | ||
455 | break_out = 1; | ||
456 | break; | 511 | break; |
457 | case ESC:{ | 512 | case ESC:{ |
458 | /* escape sequence follows */ | 513 | /* escape sequence follows */ |
459 | if ((ret = read(inputFd, &c, 1)) < 1) | 514 | if ((ret = read(inputFd, &c, 1)) < 1) |
460 | return ret; | 515 | return; |
461 | 516 | ||
462 | if (c == '[') { /* 91 */ | 517 | if (c == '[') { /* 91 */ |
463 | if ((ret = read(inputFd, &c, 1)) < 1) | 518 | if ((ret = read(inputFd, &c, 1)) < 1) |
464 | return ret; | 519 | return; |
465 | 520 | ||
466 | switch (c) { | 521 | switch (c) { |
467 | case 'A': | 522 | case 'A': |
468 | /* Up Arrow -- Get previous command */ | 523 | /* Up Arrow -- Get previous command from history */ |
469 | if (hp && hp->p) { | 524 | if (hp && hp->p) { |
470 | free(hp->s); | 525 | get_previous_history(&hp, command); |
471 | hp->s = strdup(parsenextc); | 526 | goto rewrite_line; |
472 | hp = hp->p; | 527 | } else { |
473 | goto hop; | 528 | xwrite(outputFd, "\007", 1); |
474 | } | 529 | } |
475 | break; | 530 | break; |
476 | case 'B': | 531 | case 'B': |
477 | /* Down Arrow -- Get next command */ | 532 | /* Down Arrow -- Get next command in history */ |
478 | if (hp && hp->n && hp->n->s) { | 533 | if (hp && hp->n && hp->n->s) { |
479 | free(hp->s); | 534 | get_next_history(&hp, command); |
480 | hp->s = strdup(parsenextc); | 535 | goto rewrite_line; |
481 | hp = hp->n; | 536 | } else { |
482 | goto hop; | 537 | xwrite(outputFd, "\007", 1); |
483 | } | 538 | } |
484 | break; | 539 | break; |
485 | 540 | ||
486 | /* This is where we rewrite the line | 541 | /* Rewrite the line with the selected history item */ |
487 | * using the selected history item */ | 542 | rewrite_line: |
488 | hop: | 543 | /* erase old command from command line */ |
489 | len = strlen(parsenextc); | 544 | len = strlen(command)-strlen(hp->s); |
490 | 545 | while (len>0) | |
491 | /* return to begining of line */ | 546 | input_backspace(command, outputFd, &cursor, &len); |
492 | for (; cursor > 0; cursor--) | 547 | input_home(outputFd, &cursor); |
493 | xwrite(outputFd, "\b", 1); | 548 | |
494 | |||
495 | /* erase old command */ | ||
496 | for (j = 0; j < len; j++) | ||
497 | xwrite(outputFd, " ", 1); | ||
498 | |||
499 | /* return to begining of line */ | ||
500 | for (j = len; j > 0; j--) | ||
501 | xwrite(outputFd, "\b", 1); | ||
502 | |||
503 | memset(parsenextc, 0, BUFSIZ); | ||
504 | len = strlen(parsenextc); | ||
505 | /* write new command */ | 549 | /* write new command */ |
506 | strcpy(parsenextc, hp->s); | 550 | strcpy(command, hp->s); |
507 | len = strlen(hp->s); | 551 | len = strlen(hp->s); |
508 | xwrite(outputFd, parsenextc, len); | 552 | xwrite(outputFd, command, len); |
509 | cursor = len; | 553 | cursor = len; |
510 | break; | 554 | break; |
511 | case 'C': | 555 | case 'C': |
512 | /* Right Arrow -- Move forward one character */ | 556 | /* Right Arrow -- Move forward one character */ |
513 | if (cursor < len) { | 557 | input_forward(outputFd, &cursor, len); |
514 | xwrite(outputFd, "\033[C", 3); | ||
515 | cursor++; | ||
516 | } | ||
517 | break; | 558 | break; |
518 | case 'D': | 559 | case 'D': |
519 | /* Left Arrow -- Move back one character */ | 560 | /* Left Arrow -- Move back one character */ |
520 | if (cursor > 0) { | 561 | input_backward(outputFd, &cursor); |
521 | xwrite(outputFd, "\033[D", 3); | ||
522 | cursor--; | ||
523 | } | ||
524 | break; | 562 | break; |
525 | case '3': | 563 | case '3': |
526 | /* Delete */ | 564 | /* Delete */ |
527 | if (cursor != len) { | 565 | input_delete(command, outputFd, cursor, &len); |
528 | input_delete(outputFd, cursor); | ||
529 | len--; | ||
530 | } | ||
531 | break; | 566 | break; |
532 | |||
533 | //case '5': case '6': /* pgup/pgdown */ | ||
534 | |||
535 | case '7': | ||
536 | /* rxvt home */ | ||
537 | case '1': | 567 | case '1': |
538 | /* Home (Ctrl-A) */ | 568 | /* Home (Ctrl-A) */ |
539 | input_home(outputFd, &cursor); | 569 | input_home(outputFd, &cursor); |
540 | break; | 570 | break; |
541 | case '8': | ||
542 | /* rxvt END */ | ||
543 | case '4': | 571 | case '4': |
544 | /* End (Ctrl-E) */ | 572 | /* End (Ctrl-E) */ |
545 | input_end(outputFd, &cursor, len); | 573 | input_end(outputFd, &cursor, len); |
546 | break; | 574 | break; |
575 | default: | ||
576 | xwrite(outputFd, "\007", 1); | ||
547 | } | 577 | } |
548 | if (c == '1' || c == '3' || c == '4') | 578 | if (c == '1' || c == '3' || c == '4') |
549 | if ((ret = read(inputFd, &c, 1)) < 1) | 579 | if ((ret = read(inputFd, &c, 1)) < 1) |
550 | return ret; /* read 126 (~) */ | 580 | return; /* read 126 (~) */ |
551 | } | 581 | } |
552 | if (c == 'O') { | 582 | if (c == 'O') { |
553 | /* 79 */ | 583 | /* 79 */ |
554 | if ((ret = read(inputFd, &c, 1)) < 1) | 584 | if ((ret = read(inputFd, &c, 1)) < 1) |
555 | return ret; | 585 | return; |
556 | switch (c) { | 586 | switch (c) { |
557 | case 'H': | 587 | case 'H': |
558 | /* Home (xterm) */ | 588 | /* Home (xterm) */ |
@@ -562,6 +592,8 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
562 | /* End (xterm) */ | 592 | /* End (xterm) */ |
563 | input_end(outputFd, &cursor, len); | 593 | input_end(outputFd, &cursor, len); |
564 | break; | 594 | break; |
595 | default: | ||
596 | xwrite(outputFd, "\007", 1); | ||
565 | } | 597 | } |
566 | } | 598 | } |
567 | c = 0; | 599 | c = 0; |
@@ -570,8 +602,9 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
570 | 602 | ||
571 | default: /* If it's regular input, do the normal thing */ | 603 | default: /* If it's regular input, do the normal thing */ |
572 | 604 | ||
573 | if (!isprint(c)) /* Skip non-printable characters */ | 605 | if (!isprint(c)) { /* Skip non-printable characters */ |
574 | break; | 606 | break; |
607 | } | ||
575 | 608 | ||
576 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ | 609 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ |
577 | break; | 610 | break; |
@@ -579,15 +612,15 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
579 | len++; | 612 | len++; |
580 | 613 | ||
581 | if (cursor == (len - 1)) { /* Append if at the end of the line */ | 614 | if (cursor == (len - 1)) { /* Append if at the end of the line */ |
582 | *(parsenextc + cursor) = c; | 615 | *(command + cursor) = c; |
583 | } else { /* Insert otherwise */ | 616 | } else { /* Insert otherwise */ |
584 | memmove(parsenextc + cursor + 1, parsenextc + cursor, | 617 | memmove(command + cursor + 1, command + cursor, |
585 | len - cursor - 1); | 618 | len - cursor - 1); |
586 | 619 | ||
587 | *(parsenextc + cursor) = c; | 620 | *(command + cursor) = c; |
588 | 621 | ||
589 | for (j = cursor; j < len; j++) | 622 | for (j = cursor; j < len; j++) |
590 | xwrite(outputFd, parsenextc + j, 1); | 623 | xwrite(outputFd, command + j, 1); |
591 | for (; j > cursor; j--) | 624 | for (; j > cursor; j--) |
592 | xwrite(outputFd, "\033[D", 3); | 625 | xwrite(outputFd, "\033[D", 3); |
593 | } | 626 | } |
@@ -606,12 +639,12 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
606 | } | 639 | } |
607 | 640 | ||
608 | nr = len + 1; | 641 | nr = len + 1; |
609 | xioctl(inputFd, TCSETA, (void *) &old_term); | 642 | ioctl(inputFd, TCSETA, (void *) &old_term); |
610 | reset_term = 0; | 643 | reset_term = 0; |
611 | 644 | ||
612 | 645 | ||
613 | /* Handle command history log */ | 646 | /* Handle command history log */ |
614 | if (*(parsenextc)) { | 647 | if (*(command)) { |
615 | 648 | ||
616 | struct history *h = his_end; | 649 | struct history *h = his_end; |
617 | 650 | ||
@@ -621,7 +654,7 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
621 | h->n = malloc(sizeof(struct history)); | 654 | h->n = malloc(sizeof(struct history)); |
622 | 655 | ||
623 | h->p = NULL; | 656 | h->p = NULL; |
624 | h->s = strdup(parsenextc); | 657 | h->s = strdup(command); |
625 | h->n->p = h; | 658 | h->n->p = h; |
626 | h->n->n = NULL; | 659 | h->n->n = NULL; |
627 | h->n->s = NULL; | 660 | h->n->s = NULL; |
@@ -634,7 +667,7 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
634 | h->n->p = h; | 667 | h->n->p = h; |
635 | h->n->n = NULL; | 668 | h->n->n = NULL; |
636 | h->n->s = NULL; | 669 | h->n->s = NULL; |
637 | h->s = strdup(parsenextc); | 670 | h->s = strdup(command); |
638 | his_end = h->n; | 671 | his_end = h->n; |
639 | 672 | ||
640 | /* After max history, remove the oldest command */ | 673 | /* After max history, remove the oldest command */ |
@@ -652,14 +685,14 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
652 | } | 685 | } |
653 | } | 686 | } |
654 | 687 | ||
655 | return nr; | 688 | return; |
656 | } | 689 | } |
657 | 690 | ||
658 | extern void cmdedit_init(void) | 691 | extern void cmdedit_init(void) |
659 | { | 692 | { |
660 | atexit(cmdedit_reset_term); | 693 | atexit(cmdedit_reset_term); |
661 | signal(SIGINT, prepareToDie); | 694 | signal(SIGINT, clean_up_and_die); |
662 | signal(SIGQUIT, prepareToDie); | 695 | signal(SIGQUIT, clean_up_and_die); |
663 | signal(SIGTERM, prepareToDie); | 696 | signal(SIGTERM, clean_up_and_die); |
664 | } | 697 | } |
665 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ | 698 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ |
@@ -1,17 +1,35 @@ | |||
1 | /* | 1 | #ifndef GETLINE_H |
2 | * Termios command line History and Editting for NetBSD sh (ash) | 2 | #define GETLINE_H |
3 | * Copyright (c) 1999 | 3 | |
4 | * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu> | 4 | /* unix systems can #define POSIX to use termios, otherwise |
5 | * Etc: Dave Cinege <dcinege@psychosis.com> | 5 | * the bsd or sysv interface will be used |
6 | * Adjusted for busybox: Erik Andersen <andersee@debian.org> | ||
7 | * | ||
8 | * You may use this code as you wish, so long as the original author(s) | ||
9 | * are attributed in any redistributions of the source code. | ||
10 | * This code is 'as is' with no warranty. | ||
11 | * This code may safely be consumed by a BSD or GPL license. | ||
12 | * | ||
13 | */ | 6 | */ |
14 | 7 | ||
15 | extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]); | 8 | #ifdef __STDC__ |
16 | extern void cmdedit_init(void); | 9 | #include <stddef.h> |
10 | |||
11 | typedef size_t (*cmdedit_strwidth_proc)(char *); | ||
12 | |||
13 | void cmdedit_read_input(char* promptStr, char* command); /* read a line of input */ | ||
14 | void cmdedit_setwidth(int); /* specify width of screen */ | ||
15 | void cmdedit_histadd(char *); /* adds entries to hist */ | ||
16 | void cmdedit_strwidth(cmdedit_strwidth_proc); /* to bind cmdedit_strlen */ | ||
17 | |||
18 | extern int (*cmdedit_in_hook)(char *); | ||
19 | extern int (*cmdedit_out_hook)(char *); | ||
20 | extern int (*cmdedit_tab_hook)(char *, int, int *); | ||
21 | |||
22 | #else /* not __STDC__ */ | ||
23 | |||
24 | void cmdedit_read_input(char* promptStr, char* command); | ||
25 | void cmdedit_setwidth(); | ||
26 | void cmdedit_histadd(); | ||
27 | void cmdedit_strwidth(); | ||
28 | |||
29 | extern int (*cmdedit_in_hook)(); | ||
30 | extern int (*cmdedit_out_hook)(); | ||
31 | extern int (*cmdedit_tab_hook)(); | ||
32 | |||
33 | #endif /* __STDC__ */ | ||
17 | 34 | ||
35 | #endif /* GETLINE_H */ | ||
diff --git a/internal.h b/internal.h index 1313f3644..18159b1d3 100644 --- a/internal.h +++ b/internal.h | |||
@@ -220,12 +220,6 @@ extern pid_t* findPidByName( char* pidName); | |||
220 | extern void *xmalloc (size_t size); | 220 | extern void *xmalloc (size_t size); |
221 | extern int find_real_root_device_name(char* name); | 221 | extern int find_real_root_device_name(char* name); |
222 | 222 | ||
223 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
224 | #include <stdio.h> | ||
225 | extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]); | ||
226 | extern void cmdedit_init(void); | ||
227 | #endif | ||
228 | |||
229 | #if defined BB_INIT || defined BB_SYSLOGD | 223 | #if defined BB_INIT || defined BB_SYSLOGD |
230 | extern int device_open(char *device, int mode); | 224 | extern int device_open(char *device, int mode); |
231 | #endif | 225 | #endif |
@@ -37,6 +37,9 @@ | |||
37 | #include <sys/ioctl.h> | 37 | #include <sys/ioctl.h> |
38 | #include <sys/wait.h> | 38 | #include <sys/wait.h> |
39 | #include <unistd.h> | 39 | #include <unistd.h> |
40 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
41 | #include "cmdedit.h" | ||
42 | #endif | ||
40 | 43 | ||
41 | 44 | ||
42 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | 45 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" |
@@ -132,6 +135,16 @@ static const char shell_usage[] = | |||
132 | static char cwd[1024]; | 135 | static char cwd[1024]; |
133 | static char *prompt = "# "; | 136 | static char *prompt = "# "; |
134 | 137 | ||
138 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
139 | void win_changed(int sig) | ||
140 | { | ||
141 | struct winsize win = { 0, 0 }; | ||
142 | ioctl(0, TIOCGWINSZ, &win); | ||
143 | if (win.ws_col > 0) { | ||
144 | cmdedit_setwidth( win.ws_col - 1); | ||
145 | } | ||
146 | } | ||
147 | #endif | ||
135 | 148 | ||
136 | 149 | ||
137 | /* built-in 'cd <path>' handler */ | 150 | /* built-in 'cd <path>' handler */ |
@@ -398,7 +411,7 @@ static int getCommand(FILE * source, char *command) | |||
398 | fflush(stdout); | 411 | fflush(stdout); |
399 | promptStr=(char*)malloc(sizeof(char)*(len+1)); | 412 | promptStr=(char*)malloc(sizeof(char)*(len+1)); |
400 | sprintf(promptStr, "%s %s", cwd, prompt); | 413 | sprintf(promptStr, "%s %s", cwd, prompt); |
401 | cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); | 414 | cmdedit_read_input(promptStr, command); |
402 | free( promptStr); | 415 | free( promptStr); |
403 | return 0; | 416 | return 0; |
404 | #else | 417 | #else |
@@ -696,7 +709,6 @@ static int parseCommand(char **commandPtr, struct job *job, int *isBg) | |||
696 | strcpy(job->text, *commandPtr); | 709 | strcpy(job->text, *commandPtr); |
697 | } else { | 710 | } else { |
698 | /* This leaves any trailing spaces, which is a bit sloppy */ | 711 | /* This leaves any trailing spaces, which is a bit sloppy */ |
699 | |||
700 | count = returnCommand - *commandPtr; | 712 | count = returnCommand - *commandPtr; |
701 | job->text = malloc(count + 1); | 713 | job->text = malloc(count + 1); |
702 | strncpy(job->text, *commandPtr, count); | 714 | strncpy(job->text, *commandPtr, count); |
@@ -793,14 +805,12 @@ static int runCommand(struct job newJob, struct jobSet *jobList, int inBg) | |||
793 | if (inBg) { | 805 | if (inBg) { |
794 | /* we don't wait for background jobs to return -- append it | 806 | /* we don't wait for background jobs to return -- append it |
795 | to the list of backgrounded jobs and leave it alone */ | 807 | to the list of backgrounded jobs and leave it alone */ |
796 | |||
797 | printf("[%d] %d\n", job->jobId, | 808 | printf("[%d] %d\n", job->jobId, |
798 | newJob.progs[newJob.numProgs - 1].pid); | 809 | newJob.progs[newJob.numProgs - 1].pid); |
799 | } else { | 810 | } else { |
800 | jobList->fg = job; | 811 | jobList->fg = job; |
801 | 812 | ||
802 | /* move the new process group into the foreground */ | 813 | /* move the new process group into the foreground */ |
803 | |||
804 | if (tcsetpgrp(0, newJob.pgrp)) | 814 | if (tcsetpgrp(0, newJob.pgrp)) |
805 | perror("tcsetpgrp"); | 815 | perror("tcsetpgrp"); |
806 | } | 816 | } |
@@ -938,29 +948,24 @@ int shell_main(int argc, char **argv) | |||
938 | /* initialize the cwd */ | 948 | /* initialize the cwd */ |
939 | getcwd(cwd, sizeof(cwd)); | 949 | getcwd(cwd, sizeof(cwd)); |
940 | 950 | ||
951 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
952 | signal(SIGWINCH, win_changed); | ||
953 | win_changed(0); | ||
954 | #endif | ||
941 | 955 | ||
942 | //if (argv[0] && argv[0][0] == '-') { | 956 | //if (argv[0] && argv[0][0] == '-') { |
943 | // shell_source("/etc/profile"); | 957 | // shell_source("/etc/profile"); |
944 | //} | 958 | //} |
945 | 959 | ||
946 | if (argc < 2) { | 960 | if (argc < 2) { |
947 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, | 961 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); |
948 | BB_BT); | 962 | fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); |
949 | fprintf(stdout, | ||
950 | "Enter 'help' for a list of built-in commands.\n\n"); | ||
951 | } else { | 963 | } else { |
952 | input = fopen(argv[1], "r"); | 964 | input = fopen(argv[1], "r"); |
953 | if (!input) | 965 | if (!input) { |
954 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], | 966 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], |
955 | strerror(errno)); | 967 | strerror(errno)); |
956 | // else | 968 | } |
957 | // fatalError("Got it.\n"); | ||
958 | //exit(shell_source(argv[1])); | ||
959 | |||
960 | /* Set terminal IO to canonical mode, and save old term settings. */ | ||
961 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
962 | cmdedit_init(); | ||
963 | #endif | ||
964 | } | 969 | } |
965 | 970 | ||
966 | return (busy_loop(input)); | 971 | return (busy_loop(input)); |
@@ -37,6 +37,9 @@ | |||
37 | #include <sys/ioctl.h> | 37 | #include <sys/ioctl.h> |
38 | #include <sys/wait.h> | 38 | #include <sys/wait.h> |
39 | #include <unistd.h> | 39 | #include <unistd.h> |
40 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
41 | #include "cmdedit.h" | ||
42 | #endif | ||
40 | 43 | ||
41 | 44 | ||
42 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | 45 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" |
@@ -132,6 +135,16 @@ static const char shell_usage[] = | |||
132 | static char cwd[1024]; | 135 | static char cwd[1024]; |
133 | static char *prompt = "# "; | 136 | static char *prompt = "# "; |
134 | 137 | ||
138 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
139 | void win_changed(int sig) | ||
140 | { | ||
141 | struct winsize win = { 0, 0 }; | ||
142 | ioctl(0, TIOCGWINSZ, &win); | ||
143 | if (win.ws_col > 0) { | ||
144 | cmdedit_setwidth( win.ws_col - 1); | ||
145 | } | ||
146 | } | ||
147 | #endif | ||
135 | 148 | ||
136 | 149 | ||
137 | /* built-in 'cd <path>' handler */ | 150 | /* built-in 'cd <path>' handler */ |
@@ -398,7 +411,7 @@ static int getCommand(FILE * source, char *command) | |||
398 | fflush(stdout); | 411 | fflush(stdout); |
399 | promptStr=(char*)malloc(sizeof(char)*(len+1)); | 412 | promptStr=(char*)malloc(sizeof(char)*(len+1)); |
400 | sprintf(promptStr, "%s %s", cwd, prompt); | 413 | sprintf(promptStr, "%s %s", cwd, prompt); |
401 | cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); | 414 | cmdedit_read_input(promptStr, command); |
402 | free( promptStr); | 415 | free( promptStr); |
403 | return 0; | 416 | return 0; |
404 | #else | 417 | #else |
@@ -696,7 +709,6 @@ static int parseCommand(char **commandPtr, struct job *job, int *isBg) | |||
696 | strcpy(job->text, *commandPtr); | 709 | strcpy(job->text, *commandPtr); |
697 | } else { | 710 | } else { |
698 | /* This leaves any trailing spaces, which is a bit sloppy */ | 711 | /* This leaves any trailing spaces, which is a bit sloppy */ |
699 | |||
700 | count = returnCommand - *commandPtr; | 712 | count = returnCommand - *commandPtr; |
701 | job->text = malloc(count + 1); | 713 | job->text = malloc(count + 1); |
702 | strncpy(job->text, *commandPtr, count); | 714 | strncpy(job->text, *commandPtr, count); |
@@ -793,14 +805,12 @@ static int runCommand(struct job newJob, struct jobSet *jobList, int inBg) | |||
793 | if (inBg) { | 805 | if (inBg) { |
794 | /* we don't wait for background jobs to return -- append it | 806 | /* we don't wait for background jobs to return -- append it |
795 | to the list of backgrounded jobs and leave it alone */ | 807 | to the list of backgrounded jobs and leave it alone */ |
796 | |||
797 | printf("[%d] %d\n", job->jobId, | 808 | printf("[%d] %d\n", job->jobId, |
798 | newJob.progs[newJob.numProgs - 1].pid); | 809 | newJob.progs[newJob.numProgs - 1].pid); |
799 | } else { | 810 | } else { |
800 | jobList->fg = job; | 811 | jobList->fg = job; |
801 | 812 | ||
802 | /* move the new process group into the foreground */ | 813 | /* move the new process group into the foreground */ |
803 | |||
804 | if (tcsetpgrp(0, newJob.pgrp)) | 814 | if (tcsetpgrp(0, newJob.pgrp)) |
805 | perror("tcsetpgrp"); | 815 | perror("tcsetpgrp"); |
806 | } | 816 | } |
@@ -938,29 +948,24 @@ int shell_main(int argc, char **argv) | |||
938 | /* initialize the cwd */ | 948 | /* initialize the cwd */ |
939 | getcwd(cwd, sizeof(cwd)); | 949 | getcwd(cwd, sizeof(cwd)); |
940 | 950 | ||
951 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
952 | signal(SIGWINCH, win_changed); | ||
953 | win_changed(0); | ||
954 | #endif | ||
941 | 955 | ||
942 | //if (argv[0] && argv[0][0] == '-') { | 956 | //if (argv[0] && argv[0][0] == '-') { |
943 | // shell_source("/etc/profile"); | 957 | // shell_source("/etc/profile"); |
944 | //} | 958 | //} |
945 | 959 | ||
946 | if (argc < 2) { | 960 | if (argc < 2) { |
947 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, | 961 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); |
948 | BB_BT); | 962 | fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); |
949 | fprintf(stdout, | ||
950 | "Enter 'help' for a list of built-in commands.\n\n"); | ||
951 | } else { | 963 | } else { |
952 | input = fopen(argv[1], "r"); | 964 | input = fopen(argv[1], "r"); |
953 | if (!input) | 965 | if (!input) { |
954 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], | 966 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], |
955 | strerror(errno)); | 967 | strerror(errno)); |
956 | // else | 968 | } |
957 | // fatalError("Got it.\n"); | ||
958 | //exit(shell_source(argv[1])); | ||
959 | |||
960 | /* Set terminal IO to canonical mode, and save old term settings. */ | ||
961 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
962 | cmdedit_init(); | ||
963 | #endif | ||
964 | } | 969 | } |
965 | 970 | ||
966 | return (busy_loop(input)); | 971 | return (busy_loop(input)); |
diff --git a/shell/cmdedit.c b/shell/cmdedit.c index d15c69497..9800dd1c6 100644 --- a/shell/cmdedit.c +++ b/shell/cmdedit.c | |||
@@ -53,9 +53,10 @@ static struct history *his_front = NULL; /* First element in command line list * | |||
53 | static struct history *his_end = NULL; /* Last element in command line list */ | 53 | static struct history *his_end = NULL; /* Last element in command line list */ |
54 | static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */ | 54 | static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */ |
55 | 55 | ||
56 | static int cmdedit_termw = 80; /* actual terminal width */ | ||
57 | static int cmdedit_scroll = 27; /* width of EOL scrolling region */ | ||
56 | static int history_counter = 0; /* Number of commands in history list */ | 58 | static int history_counter = 0; /* Number of commands in history list */ |
57 | static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ | 59 | static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ |
58 | char *parsenextc; /* copy of parsefile->nextc */ | ||
59 | 60 | ||
60 | struct history { | 61 | struct history { |
61 | char *s; | 62 | char *s; |
@@ -63,85 +64,42 @@ struct history { | |||
63 | struct history *n; | 64 | struct history *n; |
64 | }; | 65 | }; |
65 | 66 | ||
67 | #define xwrite write | ||
66 | 68 | ||
67 | /* Version of write which resumes after a signal is caught. */ | 69 | void |
68 | int xwrite(int fd, char *buf, int nbytes) | 70 | cmdedit_setwidth(int w) |
69 | { | 71 | { |
70 | int ntry; | 72 | if (w > 20) { |
71 | int i; | 73 | cmdedit_termw = w; |
72 | int n; | 74 | cmdedit_scroll = w / 3; |
73 | 75 | } else { | |
74 | n = nbytes; | 76 | errorMsg("\n*** Error: minimum screen width is 21\n"); |
75 | ntry = 0; | 77 | } |
76 | for (;;) { | ||
77 | i = write(fd, buf, n); | ||
78 | if (i > 0) { | ||
79 | if ((n -= i) <= 0) | ||
80 | return nbytes; | ||
81 | buf += i; | ||
82 | ntry = 0; | ||
83 | } else if (i == 0) { | ||
84 | if (++ntry > 10) | ||
85 | return nbytes - n; | ||
86 | } else if (errno != EINTR) { | ||
87 | return -1; | ||
88 | } | ||
89 | } | ||
90 | } | 78 | } |
91 | 79 | ||
92 | |||
93 | /* Version of ioctl that retries after a signal is caught. */ | ||
94 | int xioctl(int fd, unsigned long request, char *arg) | ||
95 | { | ||
96 | int i; | ||
97 | |||
98 | while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); | ||
99 | return i; | ||
100 | } | ||
101 | |||
102 | |||
103 | void cmdedit_reset_term(void) | 80 | void cmdedit_reset_term(void) |
104 | { | 81 | { |
105 | if (reset_term) | 82 | if (reset_term) |
106 | xioctl(fileno(stdin), TCSETA, (void *) &old_term); | 83 | ioctl(fileno(stdin), TCSETA, (void *) &old_term); |
107 | } | 84 | } |
108 | 85 | ||
109 | void prepareToDie(int sig) | 86 | void clean_up_and_die(int sig) |
110 | { | 87 | { |
111 | cmdedit_reset_term(); | 88 | cmdedit_reset_term(); |
112 | fprintf(stdout, "\n"); | 89 | fprintf(stdout, "\n"); |
113 | exit(TRUE); | 90 | exit(TRUE); |
114 | } | 91 | } |
115 | 92 | ||
93 | /* Go to HOME position */ | ||
116 | void input_home(int outputFd, int *cursor) | 94 | void input_home(int outputFd, int *cursor) |
117 | { /* Command line input routines */ | 95 | { |
118 | while (*cursor > 0) { | 96 | while (*cursor > 0) { |
119 | xwrite(outputFd, "\b", 1); | 97 | xwrite(outputFd, "\b", 1); |
120 | --*cursor; | 98 | --*cursor; |
121 | } | 99 | } |
122 | } | 100 | } |
123 | 101 | ||
124 | 102 | /* Go to END position */ | |
125 | void input_delete(int outputFd, int cursor) | ||
126 | { | ||
127 | int j = 0; | ||
128 | |||
129 | memmove(parsenextc + cursor, parsenextc + cursor + 1, | ||
130 | BUFSIZ - cursor - 1); | ||
131 | for (j = cursor; j < (BUFSIZ - 1); j++) { | ||
132 | if (!*(parsenextc + j)) | ||
133 | break; | ||
134 | else | ||
135 | xwrite(outputFd, (parsenextc + j), 1); | ||
136 | } | ||
137 | |||
138 | xwrite(outputFd, " \b", 2); | ||
139 | |||
140 | while (j-- > cursor) | ||
141 | xwrite(outputFd, "\b", 1); | ||
142 | } | ||
143 | |||
144 | |||
145 | void input_end(int outputFd, int *cursor, int len) | 103 | void input_end(int outputFd, int *cursor, int len) |
146 | { | 104 | { |
147 | while (*cursor < len) { | 105 | while (*cursor < len) { |
@@ -150,22 +108,22 @@ void input_end(int outputFd, int *cursor, int len) | |||
150 | } | 108 | } |
151 | } | 109 | } |
152 | 110 | ||
153 | 111 | /* Delete the char in back of the cursor */ | |
154 | void input_backspace(int outputFd, int *cursor, int *len) | 112 | void input_backspace(char* command, int outputFd, int *cursor, int *len) |
155 | { | 113 | { |
156 | int j = 0; | 114 | int j = 0; |
157 | 115 | ||
158 | if (*cursor > 0) { | 116 | if (*cursor > 0) { |
159 | xwrite(outputFd, "\b \b", 3); | 117 | xwrite(outputFd, "\b \b", 3); |
160 | --*cursor; | 118 | --*cursor; |
161 | memmove(parsenextc + *cursor, parsenextc + *cursor + 1, | 119 | memmove(command + *cursor, command + *cursor + 1, |
162 | BUFSIZ - *cursor + 1); | 120 | BUFSIZ - *cursor + 1); |
163 | 121 | ||
164 | for (j = *cursor; j < (BUFSIZ - 1); j++) { | 122 | for (j = *cursor; j < (BUFSIZ - 1); j++) { |
165 | if (!*(parsenextc + j)) | 123 | if (!*(command + j)) |
166 | break; | 124 | break; |
167 | else | 125 | else |
168 | xwrite(outputFd, (parsenextc + j), 1); | 126 | xwrite(outputFd, (command + j), 1); |
169 | } | 127 | } |
170 | 128 | ||
171 | xwrite(outputFd, " \b", 2); | 129 | xwrite(outputFd, " \b", 2); |
@@ -177,18 +135,61 @@ void input_backspace(int outputFd, int *cursor, int *len) | |||
177 | } | 135 | } |
178 | } | 136 | } |
179 | 137 | ||
180 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | 138 | /* Delete the char in front of the cursor */ |
139 | void input_delete(char* command, int outputFd, int cursor, int *len) | ||
140 | { | ||
141 | int j = 0; | ||
181 | 142 | ||
182 | char** username_completion_matches(char* command, int *num_matches) | 143 | if (cursor == *len) |
144 | return; | ||
145 | |||
146 | memmove(command + cursor, command + cursor + 1, | ||
147 | BUFSIZ - cursor - 1); | ||
148 | for (j = cursor; j < (BUFSIZ - 1); j++) { | ||
149 | if (!*(command + j)) | ||
150 | break; | ||
151 | else | ||
152 | xwrite(outputFd, (command + j), 1); | ||
153 | } | ||
154 | |||
155 | xwrite(outputFd, " \b", 2); | ||
156 | |||
157 | while (j-- > cursor) | ||
158 | xwrite(outputFd, "\b", 1); | ||
159 | --*len; | ||
160 | } | ||
161 | |||
162 | /* Move forward one charactor */ | ||
163 | void input_forward(int outputFd, int *cursor, int len) | ||
164 | { | ||
165 | if (*cursor < len) { | ||
166 | xwrite(outputFd, "\033[C", 3); | ||
167 | ++*cursor; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | /* Move back one charactor */ | ||
172 | void input_backward(int outputFd, int *cursor) | ||
173 | { | ||
174 | if (*cursor > 0) { | ||
175 | xwrite(outputFd, "\033[D", 3); | ||
176 | --*cursor; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | |||
181 | |||
182 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | ||
183 | char** username_tab_completion(char* command, int *num_matches) | ||
183 | { | 184 | { |
184 | char **matches = (char **) NULL; | 185 | char **matches = (char **) NULL; |
185 | *num_matches=0; | 186 | *num_matches=0; |
186 | fprintf(stderr, "\nin username_completion_matches\n"); | 187 | fprintf(stderr, "\nin username_tab_completion\n"); |
187 | return (matches); | 188 | return (matches); |
188 | } | 189 | } |
189 | 190 | ||
190 | #include <dirent.h> | 191 | #include <dirent.h> |
191 | char** find_path_executable_n_cwd_matches(char* command, int *num_matches) | 192 | char** exe_n_cwd_tab_completion(char* command, int *num_matches) |
192 | { | 193 | { |
193 | char *dirName; | 194 | char *dirName; |
194 | char **matches = (char **) NULL; | 195 | char **matches = (char **) NULL; |
@@ -227,6 +228,170 @@ char** find_path_executable_n_cwd_matches(char* command, int *num_matches) | |||
227 | 228 | ||
228 | return (matches); | 229 | return (matches); |
229 | } | 230 | } |
231 | |||
232 | void input_tab(char* command, int outputFd, int *cursor, int *len) | ||
233 | { | ||
234 | /* Do TAB completion */ | ||
235 | static int num_matches=0; | ||
236 | static char **matches = (char **) NULL; | ||
237 | int pos = cursor; | ||
238 | |||
239 | |||
240 | if (lastWasTab == FALSE) { | ||
241 | char *tmp, *tmp1, *matchBuf; | ||
242 | |||
243 | /* For now, we will not bother with trying to distinguish | ||
244 | * whether the cursor is in/at a command extression -- we | ||
245 | * will always try all possable matches. If you don't like | ||
246 | * that then feel free to fix it. | ||
247 | */ | ||
248 | |||
249 | /* Make a local copy of the string -- up | ||
250 | * to the position of the cursor */ | ||
251 | matchBuf = (char *) calloc(BUFSIZ, sizeof(char)); | ||
252 | strncpy(matchBuf, command, cursor); | ||
253 | tmp=matchBuf; | ||
254 | |||
255 | /* skip past any command seperator tokens */ | ||
256 | while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { | ||
257 | tmp=++tmp1; | ||
258 | /* skip any leading white space */ | ||
259 | while (*tmp && isspace(*tmp)) | ||
260 | ++tmp; | ||
261 | } | ||
262 | |||
263 | /* skip any leading white space */ | ||
264 | while (*tmp && isspace(*tmp)) | ||
265 | ++tmp; | ||
266 | |||
267 | /* Free up any memory already allocated */ | ||
268 | if (matches) { | ||
269 | free(matches); | ||
270 | matches = (char **) NULL; | ||
271 | } | ||
272 | |||
273 | /* If the word starts with `~' and there is no slash in the word, | ||
274 | * then try completing this word as a username. */ | ||
275 | |||
276 | /* FIXME -- this check is broken! */ | ||
277 | if (*tmp == '~' && !strchr(tmp, '/')) | ||
278 | matches = username_tab_completion(tmp, &num_matches); | ||
279 | |||
280 | /* Try to match any executable in our path and everything | ||
281 | * in the current working directory that matches. */ | ||
282 | if (!matches) | ||
283 | matches = exe_n_cwd_tab_completion(tmp, &num_matches); | ||
284 | |||
285 | /* Don't leak memory */ | ||
286 | free( matchBuf); | ||
287 | |||
288 | /* Did we find exactly one match? */ | ||
289 | if (matches && num_matches==1) { | ||
290 | /* write out the matched command */ | ||
291 | strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos); | ||
292 | len=strlen(command); | ||
293 | cursor=len; | ||
294 | xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos); | ||
295 | break; | ||
296 | } | ||
297 | } else { | ||
298 | /* Ok -- the last char was a TAB. Since they | ||
299 | * just hit TAB again, print a list of all the | ||
300 | * available choices... */ | ||
301 | if ( matches && num_matches>0 ) { | ||
302 | int i, col; | ||
303 | |||
304 | /* Go to the next line */ | ||
305 | xwrite(outputFd, "\n", 1); | ||
306 | /* Print the list of matches */ | ||
307 | for (i=0,col=0; i<num_matches; i++) { | ||
308 | char foo[17]; | ||
309 | sprintf(foo, "%-14s ", matches[i]); | ||
310 | col += xwrite(outputFd, foo, strlen(foo)); | ||
311 | if (col > 60 && matches[i+1] != NULL) { | ||
312 | xwrite(outputFd, "\n", 1); | ||
313 | col = 0; | ||
314 | } | ||
315 | } | ||
316 | /* Go to the next line */ | ||
317 | xwrite(outputFd, "\n", 1); | ||
318 | /* Rewrite the prompt */ | ||
319 | xwrite(outputFd, prompt, strlen(prompt)); | ||
320 | /* Rewrite the command */ | ||
321 | xwrite(outputFd, command, len); | ||
322 | /* Put the cursor back to where it used to be */ | ||
323 | for (cursor=len; cursor > pos; cursor--) | ||
324 | xwrite(outputFd, "\b", 1); | ||
325 | } | ||
326 | } | ||
327 | } | ||
328 | #endif | ||
329 | |||
330 | void get_previous_history(struct history **hp, char* command) | ||
331 | { | ||
332 | free((*hp)->s); | ||
333 | (*hp)->s = strdup(command); | ||
334 | *hp = (*hp)->p; | ||
335 | } | ||
336 | |||
337 | void get_next_history(struct history **hp, char* command) | ||
338 | { | ||
339 | free((*hp)->s); | ||
340 | (*hp)->s = strdup(command); | ||
341 | *hp = (*hp)->n; | ||
342 | |||
343 | cmdedit_redraw( NULL, hp->s, -2, -2); | ||
344 | } | ||
345 | |||
346 | #if 0 | ||
347 | /* prompt : if !=NULL, print the prompt | ||
348 | * command: the command line to be displayed | ||
349 | * where : where to display changes from. | ||
350 | * -1 for no change, -2 for new line | ||
351 | * cursor : desired location for the cursor. | ||
352 | * -1 for Beginning of line. | ||
353 | * -2 for End of Line, | ||
354 | */ | ||
355 | static void | ||
356 | cmdedit_redraw(char* prompt, char* command, int where, int cursor) | ||
357 | { | ||
358 | static char* last_command; | ||
359 | int cmdedit_width; | ||
360 | |||
361 | if (where == -2) { | ||
362 | /* Rewrite the prompt and clean up static variables */ | ||
363 | xwrite(outputFd, "\n", 1); | ||
364 | if (prompt) { | ||
365 | strcpy(last_command, prompt); | ||
366 | xwrite(outputFd, prompt, strlen(prompt)); | ||
367 | } else { | ||
368 | last_command[0] = '\0'; | ||
369 | xwrite(outputFd, "# ", 2); | ||
370 | } | ||
371 | cmdedit_width = cmdedit_termw - cmdedit_strlen(prompt); | ||
372 | } else if (strcmp(command, last_command) != 0) { | ||
373 | strcpy(last_command, prompt); | ||
374 | } | ||
375 | |||
376 | /* erase old command from command line */ | ||
377 | len = strlen(command)-strlen(last_command); | ||
378 | while (len>0) | ||
379 | input_backspace(command, outputFd, &cursor, &len); | ||
380 | input_home(outputFd, &cursor); | ||
381 | |||
382 | /* Rewrite the command */ | ||
383 | xwrite(outputFd, command+where, len); | ||
384 | |||
385 | /* Put the where it is supposed to be */ | ||
386 | for (cursor=len; cursor > where; cursor--) | ||
387 | xwrite(outputFd, "\b", 1); | ||
388 | |||
389 | /* write new command */ | ||
390 | strcpy(command, hp->s); | ||
391 | len = strlen(hp->s); | ||
392 | xwrite(outputFd, command+where, len); | ||
393 | cursor = len; | ||
394 | } | ||
230 | #endif | 395 | #endif |
231 | 396 | ||
232 | /* | 397 | /* |
@@ -245,12 +410,12 @@ char** find_path_executable_n_cwd_matches(char* command, int *num_matches) | |||
245 | * Furthermore, the "vi" command editing keys are not implemented. | 410 | * Furthermore, the "vi" command editing keys are not implemented. |
246 | * | 411 | * |
247 | * TODO: implement TAB command completion. :) | 412 | * TODO: implement TAB command completion. :) |
248 | * | ||
249 | */ | 413 | */ |
250 | extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | 414 | extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) |
251 | char command[BUFSIZ]) | ||
252 | { | 415 | { |
253 | 416 | ||
417 | int inputFd=fileno(stdin); | ||
418 | int outputFd=fileno(stdout); | ||
254 | int nr = 0; | 419 | int nr = 0; |
255 | int len = 0; | 420 | int len = 0; |
256 | int j = 0; | 421 | int j = 0; |
@@ -262,297 +427,162 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
262 | struct history *hp = his_end; | 427 | struct history *hp = his_end; |
263 | 428 | ||
264 | memset(command, 0, sizeof(command)); | 429 | memset(command, 0, sizeof(command)); |
265 | parsenextc = command; | ||
266 | if (!reset_term) { | 430 | if (!reset_term) { |
267 | xioctl(inputFd, TCGETA, (void *) &old_term); | 431 | ioctl(inputFd, TCGETA, (void *) &old_term); |
268 | memcpy(&new_term, &old_term, sizeof(struct termio)); | 432 | memcpy(&new_term, &old_term, sizeof(struct termio)); |
269 | 433 | ||
270 | new_term.c_cc[VMIN] = 1; | 434 | new_term.c_cc[VMIN] = 1; |
271 | new_term.c_cc[VTIME] = 0; | 435 | new_term.c_cc[VTIME] = 0; |
272 | new_term.c_lflag &= ~ICANON; /* unbuffered input */ | 436 | new_term.c_lflag &= ~ICANON; /* unbuffered input */ |
273 | new_term.c_lflag &= ~ECHO; | 437 | new_term.c_lflag &= ~ECHO; |
274 | xioctl(inputFd, TCSETA, (void *) &new_term); | ||
275 | reset_term = 1; | 438 | reset_term = 1; |
439 | ioctl(inputFd, TCSETA, (void *) &new_term); | ||
276 | } else { | 440 | } else { |
277 | xioctl(inputFd, TCSETA, (void *) &new_term); | 441 | ioctl(inputFd, TCSETA, (void *) &new_term); |
278 | } | 442 | } |
279 | 443 | ||
280 | memset(parsenextc, 0, BUFSIZ); | 444 | memset(command, 0, BUFSIZ); |
281 | 445 | ||
282 | while (1) { | 446 | while (1) { |
283 | 447 | ||
284 | if ((ret = read(inputFd, &c, 1)) < 1) | 448 | if ((ret = read(inputFd, &c, 1)) < 1) |
285 | return ret; | 449 | return; |
286 | |||
287 | fprintf(stderr, "\n\nkey=%d (%c)\n\n", c, c); | ||
288 | /* Go to the next line */ | ||
289 | xwrite(outputFd, "\n", 1); | ||
290 | /* Rewrite the prompt */ | ||
291 | xwrite(outputFd, prompt, strlen(prompt)); | ||
292 | /* Rewrite the command */ | ||
293 | xwrite(outputFd, parsenextc, len); | ||
294 | 450 | ||
295 | switch (c) { | 451 | switch (c) { |
452 | case '\n': | ||
453 | case '\r': | ||
454 | /* Enter */ | ||
455 | *(command + len++ + 1) = c; | ||
456 | xwrite(outputFd, &c, 1); | ||
457 | break_out = 1; | ||
458 | break; | ||
296 | case 1: | 459 | case 1: |
297 | /* Control-a -- Beginning of line */ | 460 | /* Control-a -- Beginning of line */ |
298 | input_home(outputFd, &cursor); | 461 | input_home(outputFd, &cursor); |
299 | case 5: | ||
300 | /* Control-e -- End of line */ | ||
301 | input_end(outputFd, &cursor, len); | ||
302 | break; | ||
303 | case 2: | 462 | case 2: |
304 | /* Control-b -- Move back one character */ | 463 | /* Control-b -- Move back one character */ |
305 | if (cursor > 0) { | 464 | input_backward(outputFd, &cursor); |
306 | xwrite(outputFd, "\033[D", 3); | 465 | break; |
307 | cursor--; | 466 | case 4: |
467 | /* Control-d -- Delete one character, or exit | ||
468 | * if the len=0 and no chars to delete */ | ||
469 | if (len == 0) { | ||
470 | xwrite(outputFd, "exit", 4); | ||
471 | clean_up_and_die(0); | ||
472 | } else { | ||
473 | input_delete(command, outputFd, cursor, &len); | ||
308 | } | 474 | } |
309 | break; | 475 | break; |
476 | case 5: | ||
477 | /* Control-e -- End of line */ | ||
478 | input_end(outputFd, &cursor, len); | ||
479 | break; | ||
310 | case 6: | 480 | case 6: |
311 | /* Control-f -- Move forward one character */ | 481 | /* Control-f -- Move forward one character */ |
312 | if (cursor < len) { | 482 | input_forward(outputFd, &cursor, len); |
313 | xwrite(outputFd, "\033[C", 3); | ||
314 | cursor++; | ||
315 | } | ||
316 | break; | 483 | break; |
317 | case 4: | 484 | case '\b': |
318 | /* Control-d -- Delete one character */ | 485 | case DEL: |
319 | if (cursor != len) { | 486 | /* control-h and DEL */ |
320 | input_delete(outputFd, cursor); | 487 | input_backspace(command, outputFd, &cursor, &len); |
321 | len--; | 488 | break; |
322 | } else if (len == 0) { | 489 | case '\t': |
323 | prepareToDie(0); | 490 | #ifdef BB_FEATURE_SH_TAB_COMPLETION |
324 | exit(0); | 491 | input_tab(command, outputFd, &cursor, &len); |
325 | } | 492 | #endif |
326 | break; | 493 | break; |
327 | case 14: | 494 | case 14: |
328 | /* Control-n -- Get next command */ | 495 | /* Control-n -- Get next command in history */ |
329 | if (hp && hp->n && hp->n->s) { | 496 | if (hp && hp->n && hp->n->s) { |
330 | free(hp->s); | 497 | get_next_history(&hp, command); |
331 | hp->s = strdup(parsenextc); | 498 | goto rewrite_line; |
332 | hp = hp->n; | 499 | } else { |
333 | goto hop; | 500 | xwrite(outputFd, "\007", 1); |
334 | } | 501 | } |
335 | break; | 502 | break; |
336 | case 16: | 503 | case 16: |
337 | /* Control-p -- Get previous command */ | 504 | /* Control-p -- Get previous command from history */ |
338 | if (hp && hp->p) { | 505 | if (hp && hp->p) { |
339 | free(hp->s); | 506 | get_previous_history(&hp, command); |
340 | hp->s = strdup(parsenextc); | 507 | goto rewrite_line; |
341 | hp = hp->p; | 508 | } else { |
342 | goto hop; | 509 | xwrite(outputFd, "\007", 1); |
343 | } | ||
344 | break; | ||
345 | case '\t': | ||
346 | #ifdef BB_FEATURE_SH_TAB_COMPLETION | ||
347 | { | ||
348 | /* Do TAB completion */ | ||
349 | static int num_matches=0; | ||
350 | static char **matches = (char **) NULL; | ||
351 | int pos = cursor; | ||
352 | |||
353 | |||
354 | if (lastWasTab == FALSE) { | ||
355 | char *tmp, *tmp1, *matchBuf; | ||
356 | |||
357 | /* For now, we will not bother with trying to distinguish | ||
358 | * whether the cursor is in/at a command extression -- we | ||
359 | * will always try all possable matches. If you don't like | ||
360 | * that then feel free to fix it. | ||
361 | */ | ||
362 | |||
363 | /* Make a local copy of the string -- up | ||
364 | * to the position of the cursor */ | ||
365 | matchBuf = (char *) calloc(BUFSIZ, sizeof(char)); | ||
366 | strncpy(matchBuf, parsenextc, cursor); | ||
367 | tmp=matchBuf; | ||
368 | |||
369 | /* skip past any command seperator tokens */ | ||
370 | while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { | ||
371 | tmp=++tmp1; | ||
372 | /* skip any leading white space */ | ||
373 | while (*tmp && isspace(*tmp)) | ||
374 | ++tmp; | ||
375 | } | ||
376 | |||
377 | /* skip any leading white space */ | ||
378 | while (*tmp && isspace(*tmp)) | ||
379 | ++tmp; | ||
380 | |||
381 | /* Free up any memory already allocated */ | ||
382 | if (matches) { | ||
383 | free(matches); | ||
384 | matches = (char **) NULL; | ||
385 | } | ||
386 | |||
387 | /* If the word starts with `~' and there is no slash in the word, | ||
388 | * then try completing this word as a username. */ | ||
389 | |||
390 | /* FIXME -- this check is broken! */ | ||
391 | if (*tmp == '~' && !strchr(tmp, '/')) | ||
392 | matches = username_completion_matches(tmp, &num_matches); | ||
393 | |||
394 | /* Try to match any executable in our path and everything | ||
395 | * in the current working directory that matches. */ | ||
396 | if (!matches) | ||
397 | matches = find_path_executable_n_cwd_matches(tmp, &num_matches); | ||
398 | |||
399 | /* Don't leak memory */ | ||
400 | free( matchBuf); | ||
401 | |||
402 | /* Did we find exactly one match? */ | ||
403 | if (matches && num_matches==1) { | ||
404 | /* write out the matched command */ | ||
405 | strncpy(parsenextc+pos, matches[0]+pos, strlen(matches[0])-pos); | ||
406 | len=strlen(parsenextc); | ||
407 | cursor=len; | ||
408 | xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos); | ||
409 | break; | ||
410 | } | ||
411 | } else { | ||
412 | /* Ok -- the last char was a TAB. Since they | ||
413 | * just hit TAB again, print a list of all the | ||
414 | * available choices... */ | ||
415 | if ( matches && num_matches>0 ) { | ||
416 | int i, col; | ||
417 | |||
418 | /* Go to the next line */ | ||
419 | xwrite(outputFd, "\n", 1); | ||
420 | /* Print the list of matches */ | ||
421 | for (i=0,col=0; i<num_matches; i++) { | ||
422 | char foo[17]; | ||
423 | sprintf(foo, "%-14s ", matches[i]); | ||
424 | col += xwrite(outputFd, foo, strlen(foo)); | ||
425 | if (col > 60 && matches[i+1] != NULL) { | ||
426 | xwrite(outputFd, "\n", 1); | ||
427 | col = 0; | ||
428 | } | ||
429 | } | ||
430 | /* Go to the next line */ | ||
431 | xwrite(outputFd, "\n", 1); | ||
432 | /* Rewrite the prompt */ | ||
433 | xwrite(outputFd, prompt, strlen(prompt)); | ||
434 | /* Rewrite the command */ | ||
435 | xwrite(outputFd, parsenextc, len); | ||
436 | /* Put the cursor back to where it used to be */ | ||
437 | for (cursor=len; cursor > pos; cursor--) | ||
438 | xwrite(outputFd, "\b", 1); | ||
439 | } | ||
440 | } | ||
441 | break; | ||
442 | } | 510 | } |
443 | #else | ||
444 | break; | ||
445 | #endif | ||
446 | case '\b': | ||
447 | case DEL: | ||
448 | /* Backspace */ | ||
449 | input_backspace(outputFd, &cursor, &len); | ||
450 | break; | ||
451 | case '\n': | ||
452 | /* Enter */ | ||
453 | *(parsenextc + len++ + 1) = c; | ||
454 | xwrite(outputFd, &c, 1); | ||
455 | break_out = 1; | ||
456 | break; | 511 | break; |
457 | case ESC:{ | 512 | case ESC:{ |
458 | /* escape sequence follows */ | 513 | /* escape sequence follows */ |
459 | if ((ret = read(inputFd, &c, 1)) < 1) | 514 | if ((ret = read(inputFd, &c, 1)) < 1) |
460 | return ret; | 515 | return; |
461 | 516 | ||
462 | if (c == '[') { /* 91 */ | 517 | if (c == '[') { /* 91 */ |
463 | if ((ret = read(inputFd, &c, 1)) < 1) | 518 | if ((ret = read(inputFd, &c, 1)) < 1) |
464 | return ret; | 519 | return; |
465 | 520 | ||
466 | switch (c) { | 521 | switch (c) { |
467 | case 'A': | 522 | case 'A': |
468 | /* Up Arrow -- Get previous command */ | 523 | /* Up Arrow -- Get previous command from history */ |
469 | if (hp && hp->p) { | 524 | if (hp && hp->p) { |
470 | free(hp->s); | 525 | get_previous_history(&hp, command); |
471 | hp->s = strdup(parsenextc); | 526 | goto rewrite_line; |
472 | hp = hp->p; | 527 | } else { |
473 | goto hop; | 528 | xwrite(outputFd, "\007", 1); |
474 | } | 529 | } |
475 | break; | 530 | break; |
476 | case 'B': | 531 | case 'B': |
477 | /* Down Arrow -- Get next command */ | 532 | /* Down Arrow -- Get next command in history */ |
478 | if (hp && hp->n && hp->n->s) { | 533 | if (hp && hp->n && hp->n->s) { |
479 | free(hp->s); | 534 | get_next_history(&hp, command); |
480 | hp->s = strdup(parsenextc); | 535 | goto rewrite_line; |
481 | hp = hp->n; | 536 | } else { |
482 | goto hop; | 537 | xwrite(outputFd, "\007", 1); |
483 | } | 538 | } |
484 | break; | 539 | break; |
485 | 540 | ||
486 | /* This is where we rewrite the line | 541 | /* Rewrite the line with the selected history item */ |
487 | * using the selected history item */ | 542 | rewrite_line: |
488 | hop: | 543 | /* erase old command from command line */ |
489 | len = strlen(parsenextc); | 544 | len = strlen(command)-strlen(hp->s); |
490 | 545 | while (len>0) | |
491 | /* return to begining of line */ | 546 | input_backspace(command, outputFd, &cursor, &len); |
492 | for (; cursor > 0; cursor--) | 547 | input_home(outputFd, &cursor); |
493 | xwrite(outputFd, "\b", 1); | 548 | |
494 | |||
495 | /* erase old command */ | ||
496 | for (j = 0; j < len; j++) | ||
497 | xwrite(outputFd, " ", 1); | ||
498 | |||
499 | /* return to begining of line */ | ||
500 | for (j = len; j > 0; j--) | ||
501 | xwrite(outputFd, "\b", 1); | ||
502 | |||
503 | memset(parsenextc, 0, BUFSIZ); | ||
504 | len = strlen(parsenextc); | ||
505 | /* write new command */ | 549 | /* write new command */ |
506 | strcpy(parsenextc, hp->s); | 550 | strcpy(command, hp->s); |
507 | len = strlen(hp->s); | 551 | len = strlen(hp->s); |
508 | xwrite(outputFd, parsenextc, len); | 552 | xwrite(outputFd, command, len); |
509 | cursor = len; | 553 | cursor = len; |
510 | break; | 554 | break; |
511 | case 'C': | 555 | case 'C': |
512 | /* Right Arrow -- Move forward one character */ | 556 | /* Right Arrow -- Move forward one character */ |
513 | if (cursor < len) { | 557 | input_forward(outputFd, &cursor, len); |
514 | xwrite(outputFd, "\033[C", 3); | ||
515 | cursor++; | ||
516 | } | ||
517 | break; | 558 | break; |
518 | case 'D': | 559 | case 'D': |
519 | /* Left Arrow -- Move back one character */ | 560 | /* Left Arrow -- Move back one character */ |
520 | if (cursor > 0) { | 561 | input_backward(outputFd, &cursor); |
521 | xwrite(outputFd, "\033[D", 3); | ||
522 | cursor--; | ||
523 | } | ||
524 | break; | 562 | break; |
525 | case '3': | 563 | case '3': |
526 | /* Delete */ | 564 | /* Delete */ |
527 | if (cursor != len) { | 565 | input_delete(command, outputFd, cursor, &len); |
528 | input_delete(outputFd, cursor); | ||
529 | len--; | ||
530 | } | ||
531 | break; | 566 | break; |
532 | |||
533 | //case '5': case '6': /* pgup/pgdown */ | ||
534 | |||
535 | case '7': | ||
536 | /* rxvt home */ | ||
537 | case '1': | 567 | case '1': |
538 | /* Home (Ctrl-A) */ | 568 | /* Home (Ctrl-A) */ |
539 | input_home(outputFd, &cursor); | 569 | input_home(outputFd, &cursor); |
540 | break; | 570 | break; |
541 | case '8': | ||
542 | /* rxvt END */ | ||
543 | case '4': | 571 | case '4': |
544 | /* End (Ctrl-E) */ | 572 | /* End (Ctrl-E) */ |
545 | input_end(outputFd, &cursor, len); | 573 | input_end(outputFd, &cursor, len); |
546 | break; | 574 | break; |
575 | default: | ||
576 | xwrite(outputFd, "\007", 1); | ||
547 | } | 577 | } |
548 | if (c == '1' || c == '3' || c == '4') | 578 | if (c == '1' || c == '3' || c == '4') |
549 | if ((ret = read(inputFd, &c, 1)) < 1) | 579 | if ((ret = read(inputFd, &c, 1)) < 1) |
550 | return ret; /* read 126 (~) */ | 580 | return; /* read 126 (~) */ |
551 | } | 581 | } |
552 | if (c == 'O') { | 582 | if (c == 'O') { |
553 | /* 79 */ | 583 | /* 79 */ |
554 | if ((ret = read(inputFd, &c, 1)) < 1) | 584 | if ((ret = read(inputFd, &c, 1)) < 1) |
555 | return ret; | 585 | return; |
556 | switch (c) { | 586 | switch (c) { |
557 | case 'H': | 587 | case 'H': |
558 | /* Home (xterm) */ | 588 | /* Home (xterm) */ |
@@ -562,6 +592,8 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
562 | /* End (xterm) */ | 592 | /* End (xterm) */ |
563 | input_end(outputFd, &cursor, len); | 593 | input_end(outputFd, &cursor, len); |
564 | break; | 594 | break; |
595 | default: | ||
596 | xwrite(outputFd, "\007", 1); | ||
565 | } | 597 | } |
566 | } | 598 | } |
567 | c = 0; | 599 | c = 0; |
@@ -570,8 +602,9 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
570 | 602 | ||
571 | default: /* If it's regular input, do the normal thing */ | 603 | default: /* If it's regular input, do the normal thing */ |
572 | 604 | ||
573 | if (!isprint(c)) /* Skip non-printable characters */ | 605 | if (!isprint(c)) { /* Skip non-printable characters */ |
574 | break; | 606 | break; |
607 | } | ||
575 | 608 | ||
576 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ | 609 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ |
577 | break; | 610 | break; |
@@ -579,15 +612,15 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
579 | len++; | 612 | len++; |
580 | 613 | ||
581 | if (cursor == (len - 1)) { /* Append if at the end of the line */ | 614 | if (cursor == (len - 1)) { /* Append if at the end of the line */ |
582 | *(parsenextc + cursor) = c; | 615 | *(command + cursor) = c; |
583 | } else { /* Insert otherwise */ | 616 | } else { /* Insert otherwise */ |
584 | memmove(parsenextc + cursor + 1, parsenextc + cursor, | 617 | memmove(command + cursor + 1, command + cursor, |
585 | len - cursor - 1); | 618 | len - cursor - 1); |
586 | 619 | ||
587 | *(parsenextc + cursor) = c; | 620 | *(command + cursor) = c; |
588 | 621 | ||
589 | for (j = cursor; j < len; j++) | 622 | for (j = cursor; j < len; j++) |
590 | xwrite(outputFd, parsenextc + j, 1); | 623 | xwrite(outputFd, command + j, 1); |
591 | for (; j > cursor; j--) | 624 | for (; j > cursor; j--) |
592 | xwrite(outputFd, "\033[D", 3); | 625 | xwrite(outputFd, "\033[D", 3); |
593 | } | 626 | } |
@@ -606,12 +639,12 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
606 | } | 639 | } |
607 | 640 | ||
608 | nr = len + 1; | 641 | nr = len + 1; |
609 | xioctl(inputFd, TCSETA, (void *) &old_term); | 642 | ioctl(inputFd, TCSETA, (void *) &old_term); |
610 | reset_term = 0; | 643 | reset_term = 0; |
611 | 644 | ||
612 | 645 | ||
613 | /* Handle command history log */ | 646 | /* Handle command history log */ |
614 | if (*(parsenextc)) { | 647 | if (*(command)) { |
615 | 648 | ||
616 | struct history *h = his_end; | 649 | struct history *h = his_end; |
617 | 650 | ||
@@ -621,7 +654,7 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
621 | h->n = malloc(sizeof(struct history)); | 654 | h->n = malloc(sizeof(struct history)); |
622 | 655 | ||
623 | h->p = NULL; | 656 | h->p = NULL; |
624 | h->s = strdup(parsenextc); | 657 | h->s = strdup(command); |
625 | h->n->p = h; | 658 | h->n->p = h; |
626 | h->n->n = NULL; | 659 | h->n->n = NULL; |
627 | h->n->s = NULL; | 660 | h->n->s = NULL; |
@@ -634,7 +667,7 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
634 | h->n->p = h; | 667 | h->n->p = h; |
635 | h->n->n = NULL; | 668 | h->n->n = NULL; |
636 | h->n->s = NULL; | 669 | h->n->s = NULL; |
637 | h->s = strdup(parsenextc); | 670 | h->s = strdup(command); |
638 | his_end = h->n; | 671 | his_end = h->n; |
639 | 672 | ||
640 | /* After max history, remove the oldest command */ | 673 | /* After max history, remove the oldest command */ |
@@ -652,14 +685,14 @@ extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, | |||
652 | } | 685 | } |
653 | } | 686 | } |
654 | 687 | ||
655 | return nr; | 688 | return; |
656 | } | 689 | } |
657 | 690 | ||
658 | extern void cmdedit_init(void) | 691 | extern void cmdedit_init(void) |
659 | { | 692 | { |
660 | atexit(cmdedit_reset_term); | 693 | atexit(cmdedit_reset_term); |
661 | signal(SIGINT, prepareToDie); | 694 | signal(SIGINT, clean_up_and_die); |
662 | signal(SIGQUIT, prepareToDie); | 695 | signal(SIGQUIT, clean_up_and_die); |
663 | signal(SIGTERM, prepareToDie); | 696 | signal(SIGTERM, clean_up_and_die); |
664 | } | 697 | } |
665 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ | 698 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ |
diff --git a/shell/cmdedit.h b/shell/cmdedit.h index 843a74070..0e465e50e 100644 --- a/shell/cmdedit.h +++ b/shell/cmdedit.h | |||
@@ -1,17 +1,35 @@ | |||
1 | /* | 1 | #ifndef GETLINE_H |
2 | * Termios command line History and Editting for NetBSD sh (ash) | 2 | #define GETLINE_H |
3 | * Copyright (c) 1999 | 3 | |
4 | * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu> | 4 | /* unix systems can #define POSIX to use termios, otherwise |
5 | * Etc: Dave Cinege <dcinege@psychosis.com> | 5 | * the bsd or sysv interface will be used |
6 | * Adjusted for busybox: Erik Andersen <andersee@debian.org> | ||
7 | * | ||
8 | * You may use this code as you wish, so long as the original author(s) | ||
9 | * are attributed in any redistributions of the source code. | ||
10 | * This code is 'as is' with no warranty. | ||
11 | * This code may safely be consumed by a BSD or GPL license. | ||
12 | * | ||
13 | */ | 6 | */ |
14 | 7 | ||
15 | extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]); | 8 | #ifdef __STDC__ |
16 | extern void cmdedit_init(void); | 9 | #include <stddef.h> |
10 | |||
11 | typedef size_t (*cmdedit_strwidth_proc)(char *); | ||
12 | |||
13 | void cmdedit_read_input(char* promptStr, char* command); /* read a line of input */ | ||
14 | void cmdedit_setwidth(int); /* specify width of screen */ | ||
15 | void cmdedit_histadd(char *); /* adds entries to hist */ | ||
16 | void cmdedit_strwidth(cmdedit_strwidth_proc); /* to bind cmdedit_strlen */ | ||
17 | |||
18 | extern int (*cmdedit_in_hook)(char *); | ||
19 | extern int (*cmdedit_out_hook)(char *); | ||
20 | extern int (*cmdedit_tab_hook)(char *, int, int *); | ||
21 | |||
22 | #else /* not __STDC__ */ | ||
23 | |||
24 | void cmdedit_read_input(char* promptStr, char* command); | ||
25 | void cmdedit_setwidth(); | ||
26 | void cmdedit_histadd(); | ||
27 | void cmdedit_strwidth(); | ||
28 | |||
29 | extern int (*cmdedit_in_hook)(); | ||
30 | extern int (*cmdedit_out_hook)(); | ||
31 | extern int (*cmdedit_tab_hook)(); | ||
32 | |||
33 | #endif /* __STDC__ */ | ||
17 | 34 | ||
35 | #endif /* GETLINE_H */ | ||
diff --git a/shell/lash.c b/shell/lash.c index f17097c64..44ffe968f 100644 --- a/shell/lash.c +++ b/shell/lash.c | |||
@@ -37,6 +37,9 @@ | |||
37 | #include <sys/ioctl.h> | 37 | #include <sys/ioctl.h> |
38 | #include <sys/wait.h> | 38 | #include <sys/wait.h> |
39 | #include <unistd.h> | 39 | #include <unistd.h> |
40 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
41 | #include "cmdedit.h" | ||
42 | #endif | ||
40 | 43 | ||
41 | 44 | ||
42 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | 45 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" |
@@ -132,6 +135,16 @@ static const char shell_usage[] = | |||
132 | static char cwd[1024]; | 135 | static char cwd[1024]; |
133 | static char *prompt = "# "; | 136 | static char *prompt = "# "; |
134 | 137 | ||
138 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
139 | void win_changed(int sig) | ||
140 | { | ||
141 | struct winsize win = { 0, 0 }; | ||
142 | ioctl(0, TIOCGWINSZ, &win); | ||
143 | if (win.ws_col > 0) { | ||
144 | cmdedit_setwidth( win.ws_col - 1); | ||
145 | } | ||
146 | } | ||
147 | #endif | ||
135 | 148 | ||
136 | 149 | ||
137 | /* built-in 'cd <path>' handler */ | 150 | /* built-in 'cd <path>' handler */ |
@@ -398,7 +411,7 @@ static int getCommand(FILE * source, char *command) | |||
398 | fflush(stdout); | 411 | fflush(stdout); |
399 | promptStr=(char*)malloc(sizeof(char)*(len+1)); | 412 | promptStr=(char*)malloc(sizeof(char)*(len+1)); |
400 | sprintf(promptStr, "%s %s", cwd, prompt); | 413 | sprintf(promptStr, "%s %s", cwd, prompt); |
401 | cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); | 414 | cmdedit_read_input(promptStr, command); |
402 | free( promptStr); | 415 | free( promptStr); |
403 | return 0; | 416 | return 0; |
404 | #else | 417 | #else |
@@ -696,7 +709,6 @@ static int parseCommand(char **commandPtr, struct job *job, int *isBg) | |||
696 | strcpy(job->text, *commandPtr); | 709 | strcpy(job->text, *commandPtr); |
697 | } else { | 710 | } else { |
698 | /* This leaves any trailing spaces, which is a bit sloppy */ | 711 | /* This leaves any trailing spaces, which is a bit sloppy */ |
699 | |||
700 | count = returnCommand - *commandPtr; | 712 | count = returnCommand - *commandPtr; |
701 | job->text = malloc(count + 1); | 713 | job->text = malloc(count + 1); |
702 | strncpy(job->text, *commandPtr, count); | 714 | strncpy(job->text, *commandPtr, count); |
@@ -793,14 +805,12 @@ static int runCommand(struct job newJob, struct jobSet *jobList, int inBg) | |||
793 | if (inBg) { | 805 | if (inBg) { |
794 | /* we don't wait for background jobs to return -- append it | 806 | /* we don't wait for background jobs to return -- append it |
795 | to the list of backgrounded jobs and leave it alone */ | 807 | to the list of backgrounded jobs and leave it alone */ |
796 | |||
797 | printf("[%d] %d\n", job->jobId, | 808 | printf("[%d] %d\n", job->jobId, |
798 | newJob.progs[newJob.numProgs - 1].pid); | 809 | newJob.progs[newJob.numProgs - 1].pid); |
799 | } else { | 810 | } else { |
800 | jobList->fg = job; | 811 | jobList->fg = job; |
801 | 812 | ||
802 | /* move the new process group into the foreground */ | 813 | /* move the new process group into the foreground */ |
803 | |||
804 | if (tcsetpgrp(0, newJob.pgrp)) | 814 | if (tcsetpgrp(0, newJob.pgrp)) |
805 | perror("tcsetpgrp"); | 815 | perror("tcsetpgrp"); |
806 | } | 816 | } |
@@ -938,29 +948,24 @@ int shell_main(int argc, char **argv) | |||
938 | /* initialize the cwd */ | 948 | /* initialize the cwd */ |
939 | getcwd(cwd, sizeof(cwd)); | 949 | getcwd(cwd, sizeof(cwd)); |
940 | 950 | ||
951 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
952 | signal(SIGWINCH, win_changed); | ||
953 | win_changed(0); | ||
954 | #endif | ||
941 | 955 | ||
942 | //if (argv[0] && argv[0][0] == '-') { | 956 | //if (argv[0] && argv[0][0] == '-') { |
943 | // shell_source("/etc/profile"); | 957 | // shell_source("/etc/profile"); |
944 | //} | 958 | //} |
945 | 959 | ||
946 | if (argc < 2) { | 960 | if (argc < 2) { |
947 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, | 961 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); |
948 | BB_BT); | 962 | fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); |
949 | fprintf(stdout, | ||
950 | "Enter 'help' for a list of built-in commands.\n\n"); | ||
951 | } else { | 963 | } else { |
952 | input = fopen(argv[1], "r"); | 964 | input = fopen(argv[1], "r"); |
953 | if (!input) | 965 | if (!input) { |
954 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], | 966 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], |
955 | strerror(errno)); | 967 | strerror(errno)); |
956 | // else | 968 | } |
957 | // fatalError("Got it.\n"); | ||
958 | //exit(shell_source(argv[1])); | ||
959 | |||
960 | /* Set terminal IO to canonical mode, and save old term settings. */ | ||
961 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
962 | cmdedit_init(); | ||
963 | #endif | ||
964 | } | 969 | } |
965 | 970 | ||
966 | return (busy_loop(input)); | 971 | return (busy_loop(input)); |