diff options
author | Erik Andersen <andersen@codepoet.org> | 2000-03-19 05:28:55 +0000 |
---|---|---|
committer | Erik Andersen <andersen@codepoet.org> | 2000-03-19 05:28:55 +0000 |
commit | c7c634bd88c57d910c6089de7f0091ca4e3d1843 (patch) | |
tree | df2b1c18c27729603880dd797ee6bd51d192d9cd | |
parent | 6c41c448982827ef5bfac9bac14bc9e509f45bc4 (diff) | |
download | busybox-w32-c7c634bd88c57d910c6089de7f0091ca4e3d1843.tar.gz busybox-w32-c7c634bd88c57d910c6089de7f0091ca4e3d1843.tar.bz2 busybox-w32-c7c634bd88c57d910c6089de7f0091ca4e3d1843.zip |
Some more stuff.
-Erik
-rw-r--r-- | cmdedit.c | 919 | ||||
-rw-r--r-- | cmdedit.h | 2 | ||||
-rw-r--r-- | internal.h | 6 | ||||
-rw-r--r-- | lash.c | 21 | ||||
-rw-r--r-- | sh.c | 21 | ||||
-rw-r--r-- | shell/cmdedit.c | 919 | ||||
-rw-r--r-- | shell/cmdedit.h | 2 | ||||
-rw-r--r-- | shell/lash.c | 21 |
8 files changed, 984 insertions, 927 deletions
@@ -1,3 +1,4 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
1 | /* | 2 | /* |
2 | * Termios command line History and Editting for NetBSD sh (ash) | 3 | * Termios command line History and Editting for NetBSD sh (ash) |
3 | * Copyright (c) 1999 | 4 | * Copyright (c) 1999 |
@@ -40,10 +41,8 @@ | |||
40 | #include <ctype.h> | 41 | #include <ctype.h> |
41 | #include <signal.h> | 42 | #include <signal.h> |
42 | 43 | ||
43 | #include "cmdedit.h" | ||
44 | 44 | ||
45 | 45 | #define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */ | |
46 | #define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */ | ||
47 | 46 | ||
48 | #define ESC 27 | 47 | #define ESC 27 |
49 | #define DEL 127 | 48 | #define DEL 127 |
@@ -55,142 +54,150 @@ static struct history *his_end = NULL; /* Last element in command line list */ | |||
55 | 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 */ |
56 | 55 | ||
57 | static int history_counter = 0; /* Number of commands in history list */ | 56 | static int history_counter = 0; /* Number of commands in history list */ |
58 | static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ | 57 | static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ |
59 | char *parsenextc; /* copy of parsefile->nextc */ | 58 | char *parsenextc; /* copy of parsefile->nextc */ |
60 | 59 | ||
61 | struct history { | 60 | struct history { |
62 | char *s; | 61 | char *s; |
63 | struct history *p; | 62 | struct history *p; |
64 | struct history *n; | 63 | struct history *n; |
65 | }; | 64 | }; |
66 | 65 | ||
67 | 66 | ||
68 | /* Version of write which resumes after a signal is caught. */ | 67 | /* Version of write which resumes after a signal is caught. */ |
69 | int xwrite(int fd, char *buf, int nbytes) | 68 | int xwrite(int fd, char *buf, int nbytes) |
70 | { | 69 | { |
71 | int ntry; | 70 | int ntry; |
72 | int i; | 71 | int i; |
73 | int n; | 72 | int n; |
74 | 73 | ||
75 | n = nbytes; | 74 | n = nbytes; |
76 | ntry = 0; | 75 | ntry = 0; |
77 | for (;;) { | 76 | for (;;) { |
78 | i = write(fd, buf, n); | 77 | i = write(fd, buf, n); |
79 | if (i > 0) { | 78 | if (i > 0) { |
80 | if ((n -= i) <= 0) | 79 | if ((n -= i) <= 0) |
81 | return nbytes; | 80 | return nbytes; |
82 | buf += i; | 81 | buf += i; |
83 | ntry = 0; | 82 | ntry = 0; |
84 | } else if (i == 0) { | 83 | } else if (i == 0) { |
85 | if (++ntry > 10) | 84 | if (++ntry > 10) |
86 | return nbytes - n; | 85 | return nbytes - n; |
87 | } else if (errno != EINTR) { | 86 | } else if (errno != EINTR) { |
88 | return -1; | 87 | return -1; |
88 | } | ||
89 | } | 89 | } |
90 | } | ||
91 | } | 90 | } |
92 | 91 | ||
93 | 92 | ||
94 | /* Version of ioctl that retries after a signal is caught. */ | 93 | /* Version of ioctl that retries after a signal is caught. */ |
95 | int xioctl(int fd, unsigned long request, char *arg) | 94 | int xioctl(int fd, unsigned long request, char *arg) |
96 | { | 95 | { |
97 | int i; | 96 | int i; |
98 | while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); | 97 | |
99 | return i; | 98 | while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); |
99 | return i; | ||
100 | } | 100 | } |
101 | 101 | ||
102 | 102 | ||
103 | void cmdedit_reset_term(void) | 103 | void cmdedit_reset_term(void) |
104 | { | 104 | { |
105 | if (reset_term) | 105 | if (reset_term) |
106 | xioctl(fileno(stdin), TCSETA, (void *) &old_term); | 106 | xioctl(fileno(stdin), TCSETA, (void *) &old_term); |
107 | } | 107 | } |
108 | 108 | ||
109 | void prepareToDie(int sig) | 109 | void prepareToDie(int sig) |
110 | { | 110 | { |
111 | cmdedit_reset_term(); | 111 | cmdedit_reset_term(); |
112 | fprintf(stdout, "\n"); | 112 | fprintf(stdout, "\n"); |
113 | exit(TRUE); | 113 | exit(TRUE); |
114 | } | 114 | } |
115 | 115 | ||
116 | void input_home(int outputFd, int *cursor) | 116 | void input_home(int outputFd, int *cursor) |
117 | { /* Command line input routines */ | 117 | { /* Command line input routines */ |
118 | while (*cursor > 0) { | 118 | while (*cursor > 0) { |
119 | xwrite(outputFd, "\b", 1); | 119 | xwrite(outputFd, "\b", 1); |
120 | --*cursor; | 120 | --*cursor; |
121 | } | 121 | } |
122 | } | 122 | } |
123 | 123 | ||
124 | 124 | ||
125 | void input_delete(int outputFd, int cursor) | 125 | void input_delete(int outputFd, int cursor) |
126 | { | 126 | { |
127 | int j = 0; | 127 | int j = 0; |
128 | 128 | ||
129 | memmove(parsenextc + cursor, parsenextc + cursor + 1, | 129 | memmove(parsenextc + cursor, parsenextc + cursor + 1, |
130 | BUFSIZ - cursor - 1); | 130 | BUFSIZ - cursor - 1); |
131 | for (j = cursor; j < (BUFSIZ - 1); j++) { | 131 | for (j = cursor; j < (BUFSIZ - 1); j++) { |
132 | if (!*(parsenextc + j)) | 132 | if (!*(parsenextc + j)) |
133 | break; | 133 | break; |
134 | else | 134 | else |
135 | xwrite(outputFd, (parsenextc + j), 1); | 135 | xwrite(outputFd, (parsenextc + j), 1); |
136 | } | 136 | } |
137 | 137 | ||
138 | xwrite(outputFd, " \b", 2); | 138 | xwrite(outputFd, " \b", 2); |
139 | 139 | ||
140 | while (j-- > cursor) | 140 | while (j-- > cursor) |
141 | xwrite(outputFd, "\b", 1); | 141 | xwrite(outputFd, "\b", 1); |
142 | } | 142 | } |
143 | 143 | ||
144 | 144 | ||
145 | void input_end(int outputFd, int *cursor, int len) | 145 | void input_end(int outputFd, int *cursor, int len) |
146 | { | 146 | { |
147 | while (*cursor < len) { | 147 | while (*cursor < len) { |
148 | xwrite(outputFd, "\033[C", 3); | 148 | xwrite(outputFd, "\033[C", 3); |
149 | ++*cursor; | 149 | ++*cursor; |
150 | } | 150 | } |
151 | } | 151 | } |
152 | 152 | ||
153 | 153 | ||
154 | void input_backspace(int outputFd, int *cursor, int *len) | 154 | void input_backspace(int outputFd, int *cursor, int *len) |
155 | { | 155 | { |
156 | int j = 0; | 156 | int j = 0; |
157 | 157 | ||
158 | if (*cursor > 0) { | 158 | if (*cursor > 0) { |
159 | xwrite(outputFd, "\b \b", 3); | 159 | xwrite(outputFd, "\b \b", 3); |
160 | --*cursor; | 160 | --*cursor; |
161 | memmove(parsenextc + *cursor, parsenextc + *cursor + 1, | 161 | memmove(parsenextc + *cursor, parsenextc + *cursor + 1, |
162 | BUFSIZ - *cursor + 1); | 162 | BUFSIZ - *cursor + 1); |
163 | 163 | ||
164 | for (j = *cursor; j < (BUFSIZ - 1); j++) { | 164 | for (j = *cursor; j < (BUFSIZ - 1); j++) { |
165 | if (!*(parsenextc + j)) | 165 | if (!*(parsenextc + j)) |
166 | break; | 166 | break; |
167 | else | 167 | else |
168 | xwrite(outputFd, (parsenextc + j), 1); | 168 | xwrite(outputFd, (parsenextc + j), 1); |
169 | } | 169 | } |
170 | 170 | ||
171 | xwrite(outputFd, " \b", 2); | 171 | xwrite(outputFd, " \b", 2); |
172 | 172 | ||
173 | while (j-- > *cursor) | 173 | while (j-- > *cursor) |
174 | xwrite(outputFd, "\b", 1); | 174 | xwrite(outputFd, "\b", 1); |
175 | 175 | ||
176 | --*len; | 176 | --*len; |
177 | } | 177 | } |
178 | } | 178 | } |
179 | 179 | ||
180 | char **username_completion_matches( char* matchBuf) | 180 | char** username_completion_matches(char* command, int *num_matches) |
181 | { | 181 | { |
182 | fprintf(stderr, "\nin username_completion_matches\n"); | 182 | char **matches = (char **) NULL; |
183 | return ( (char**) NULL); | 183 | *num_matches=0; |
184 | fprintf(stderr, "\nin username_completion_matches\n"); | ||
185 | return (matches); | ||
184 | } | 186 | } |
185 | char **command_completion_matches( char* matchBuf) | 187 | char** find_path_executable_n_cwd_matches(char* command, int *num_matches) |
186 | { | 188 | { |
187 | fprintf(stderr, "\nin command_completion_matches\n"); | 189 | char **matches = (char **) NULL; |
188 | return ( (char**) NULL); | 190 | matches = malloc(sizeof(char*)*100); |
189 | } | 191 | |
190 | char **directory_completion_matches( char* matchBuf) | 192 | matches[0] = malloc(sizeof(char)*50); |
191 | { | 193 | matches[1] = malloc(sizeof(char)*50); |
192 | fprintf(stderr, "\nin directory_completion_matches\n"); | 194 | |
193 | return ( (char**) NULL); | 195 | sprintf(matches[0], "Hello"); |
196 | sprintf(matches[1], "Howdy"); | ||
197 | *num_matches=2; | ||
198 | |||
199 | // fprintf(stderr, "\nin find_path_executable_n_cwd_matches\n"); | ||
200 | return (matches); | ||
194 | } | 201 | } |
195 | 202 | ||
196 | /* | 203 | /* |
@@ -211,387 +218,401 @@ char **directory_completion_matches( char* matchBuf) | |||
211 | * TODO: implement TAB command completion. :) | 218 | * TODO: implement TAB command completion. :) |
212 | * | 219 | * |
213 | */ | 220 | */ |
214 | extern int cmdedit_read_input(int inputFd, int outputFd, | 221 | extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, |
215 | char command[BUFSIZ]) | 222 | char command[BUFSIZ]) |
216 | { | 223 | { |
217 | 224 | ||
218 | int nr = 0; | 225 | int nr = 0; |
219 | int len = 0; | 226 | int len = 0; |
220 | int j = 0; | 227 | int j = 0; |
221 | int cursor = 0; | 228 | int cursor = 0; |
222 | int break_out = 0; | 229 | int break_out = 0; |
223 | int ret = 0; | 230 | int ret = 0; |
224 | int lastWasTab = FALSE; | 231 | int lastWasTab = FALSE; |
225 | char **matches = (char **)NULL; | 232 | char c = 0; |
226 | char c = 0; | 233 | struct history *hp = his_end; |
227 | struct history *hp = his_end; | 234 | |
228 | 235 | memset(command, 0, sizeof(command)); | |
229 | memset(command, 0, sizeof(command)); | 236 | parsenextc = command; |
230 | parsenextc = command; | 237 | if (!reset_term) { |
231 | if (!reset_term) { | 238 | xioctl(inputFd, TCGETA, (void *) &old_term); |
232 | xioctl(inputFd, TCGETA, (void *) &old_term); | 239 | memcpy(&new_term, &old_term, sizeof(struct termio)); |
233 | memcpy(&new_term, &old_term, sizeof(struct termio)); | 240 | |
234 | new_term.c_cc[VMIN] = 1; | 241 | new_term.c_cc[VMIN] = 1; |
235 | new_term.c_cc[VTIME] = 0; | 242 | new_term.c_cc[VTIME] = 0; |
236 | new_term.c_lflag &= ~ICANON; /* unbuffered input */ | 243 | new_term.c_lflag &= ~ICANON; /* unbuffered input */ |
237 | new_term.c_lflag &= ~ECHO; | 244 | new_term.c_lflag &= ~ECHO; |
238 | xioctl(inputFd, TCSETA, (void *) &new_term); | 245 | xioctl(inputFd, TCSETA, (void *) &new_term); |
239 | reset_term = 1; | 246 | reset_term = 1; |
240 | } else { | 247 | } else { |
241 | xioctl(inputFd, TCSETA, (void *) &new_term); | 248 | xioctl(inputFd, TCSETA, (void *) &new_term); |
242 | } | 249 | } |
243 | |||
244 | memset(parsenextc, 0, BUFSIZ); | ||
245 | |||
246 | while (1) { | ||
247 | |||
248 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
249 | return ret; | ||
250 | |||
251 | switch (c) { | ||
252 | case 1: | ||
253 | /* Control-a -- Beginning of line */ | ||
254 | input_home(outputFd, &cursor); | ||
255 | case 5: | ||
256 | /* Control-e -- End of line */ | ||
257 | input_end(outputFd, &cursor, len); | ||
258 | break; | ||
259 | case 2: | ||
260 | /* Control-b -- Move back one character */ | ||
261 | if (cursor > 0) { | ||
262 | xwrite(outputFd, "\033[D", 3); | ||
263 | cursor--; | ||
264 | } | ||
265 | break; | ||
266 | case 6: | ||
267 | /* Control-f -- Move forward one character */ | ||
268 | if (cursor < len) { | ||
269 | xwrite(outputFd, "\033[C", 3); | ||
270 | cursor++; | ||
271 | } | ||
272 | break; | ||
273 | case 4: | ||
274 | /* Control-d -- Delete one character */ | ||
275 | if (cursor != len) { | ||
276 | input_delete(outputFd, cursor); | ||
277 | len--; | ||
278 | } else if (len == 0) { | ||
279 | prepareToDie(0); | ||
280 | exit(0); | ||
281 | } | ||
282 | break; | ||
283 | case 14: | ||
284 | /* Control-n -- Get next command */ | ||
285 | if (hp && hp->n && hp->n->s) { | ||
286 | free( hp->s); | ||
287 | hp->s = strdup(parsenextc); | ||
288 | hp = hp->n; | ||
289 | goto hop; | ||
290 | } | ||
291 | break; | ||
292 | case 16: | ||
293 | /* Control-p -- Get previous command */ | ||
294 | if (hp && hp->p) { | ||
295 | free( hp->s); | ||
296 | hp->s = strdup(parsenextc); | ||
297 | hp = hp->p; | ||
298 | goto hop; | ||
299 | } | ||
300 | break; | ||
301 | case '\t': | ||
302 | { | ||
303 | /* Do TAB completion */ | ||
304 | int in_command_position=0, ti=len-1; | ||
305 | |||
306 | if (lastWasTab == FALSE) { | ||
307 | char *tmp; | ||
308 | char *matchBuf; | ||
309 | |||
310 | if (matches) { | ||
311 | free(matches); | ||
312 | matches = (char **)NULL; | ||
313 | } | ||
314 | |||
315 | matchBuf = (char *) calloc(BUFSIZ, sizeof(char)); | ||
316 | |||
317 | /* Make a local copy of the string -- up | ||
318 | * to the the position of the cursor */ | ||
319 | strcpy( matchBuf, parsenextc); | ||
320 | matchBuf[cursor+1] = '\0'; | ||
321 | |||
322 | fprintf(stderr, "matchBuf='%s'\n", matchBuf); | ||
323 | |||
324 | /* skip leading white space */ | ||
325 | tmp = matchBuf; | ||
326 | while (*tmp && isspace(*tmp)) { | ||
327 | (tmp)++; | ||
328 | ti++; | ||
329 | } | ||
330 | |||
331 | /* Determine if this is a command word or not */ | ||
332 | //while ((ti > -1) && (whitespace (matchBuf[ti]))) { | ||
333 | //printf("\nti=%d\n", ti); | ||
334 | // ti--; | ||
335 | // } | ||
336 | printf("\nti=%d\n", ti); | ||
337 | |||
338 | if (ti < 0) { | ||
339 | in_command_position++; | ||
340 | } else if (member(matchBuf[ti], ";|&{(`")) { | ||
341 | int this_char, prev_char; | ||
342 | in_command_position++; | ||
343 | /* Handle the two character tokens `>&', `<&', and `>|'. | ||
344 | We are not in a command position after one of these. */ | ||
345 | this_char = matchBuf[ti]; | ||
346 | prev_char = matchBuf[ti - 1]; | ||
347 | |||
348 | if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) || | ||
349 | (this_char == '|' && prev_char == '>')) { | ||
350 | in_command_position = 0; | ||
351 | } | ||
352 | /* For now, do not bother with catching quoted | ||
353 | * expressions and marking them as not in command | ||
354 | * positions. Some other day. Or not. | ||
355 | */ | ||
356 | //else if (char_is_quoted (matchBuf, ti)) { | ||
357 | // in_command_position = 0; | ||
358 | //} | ||
359 | } | ||
360 | printf("\nin_command_position=%d\n", in_command_position); | ||
361 | /* If the word starts in `~', and there is no slash in the word, | ||
362 | * then try completing this word as a username. */ | ||
363 | if (*matchBuf == '~' && !strchr (matchBuf, '/')) | ||
364 | matches = username_completion_matches(matchBuf); | ||
365 | |||
366 | /* If this word is in a command position, then complete over possible | ||
367 | * command names, including aliases, built-ins, and executables. */ | ||
368 | if (!matches && in_command_position) { | ||
369 | matches = command_completion_matches(matchBuf); | ||
370 | |||
371 | /* If we are attempting command completion and nothing matches, | ||
372 | * then try and match directories as a last resort... */ | ||
373 | if (!matches) | ||
374 | matches = directory_completion_matches(matchBuf); | ||
375 | } | ||
376 | } else { | ||
377 | printf("\nprinting match list\n"); | ||
378 | } | ||
379 | /* Rewrite the whole line (for debugging) */ | ||
380 | for (; cursor > 0; cursor--) | ||
381 | xwrite(outputFd, "\b", 1); | ||
382 | len = strlen(parsenextc); | ||
383 | xwrite(outputFd, parsenextc, len); | ||
384 | cursor = len; | ||
385 | break; | ||
386 | } | ||
387 | case '\b': | ||
388 | case DEL: | ||
389 | /* Backspace */ | ||
390 | input_backspace(outputFd, &cursor, &len); | ||
391 | break; | ||
392 | case '\n': | ||
393 | /* Enter */ | ||
394 | *(parsenextc + len++ + 1) = c; | ||
395 | xwrite(outputFd, &c, 1); | ||
396 | break_out = 1; | ||
397 | break; | ||
398 | case ESC: { | ||
399 | /* escape sequence follows */ | ||
400 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
401 | return ret; | ||
402 | |||
403 | if (c == '[') { /* 91 */ | ||
404 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
405 | return ret; | ||
406 | |||
407 | switch (c) { | ||
408 | case 'A': | ||
409 | /* Up Arrow -- Get previous command */ | ||
410 | if (hp && hp->p) { | ||
411 | free( hp->s); | ||
412 | hp->s = strdup(parsenextc); | ||
413 | hp = hp->p; | ||
414 | goto hop; | ||
415 | } | ||
416 | break; | ||
417 | case 'B': | ||
418 | /* Down Arrow -- Get next command */ | ||
419 | if (hp && hp->n && hp->n->s) { | ||
420 | free( hp->s); | ||
421 | hp->s = strdup(parsenextc); | ||
422 | hp = hp->n; | ||
423 | goto hop; | ||
424 | } | ||
425 | break; | ||
426 | |||
427 | /* This is where we rewrite the line | ||
428 | * using the selected history item */ | ||
429 | hop: | ||
430 | len = strlen(parsenextc); | ||
431 | |||
432 | /* return to begining of line */ | ||
433 | for (; cursor > 0; cursor--) | ||
434 | xwrite(outputFd, "\b", 1); | ||
435 | xwrite(outputFd, parsenextc, len); | ||
436 | 250 | ||
437 | /* erase old command */ | 251 | memset(parsenextc, 0, BUFSIZ); |
438 | for (j = 0; j < len; j++) | ||
439 | xwrite(outputFd, " ", 1); | ||
440 | 252 | ||
441 | /* return to begining of line */ | 253 | while (1) { |
442 | for (j = len; j > 0; j--) | ||
443 | xwrite(outputFd, "\b", 1); | ||
444 | 254 | ||
445 | memset(parsenextc, 0, BUFSIZ); | ||
446 | /* write new command */ | ||
447 | strcpy(parsenextc, hp->s); | ||
448 | len = strlen(hp->s); | ||
449 | xwrite(outputFd, parsenextc, len); | ||
450 | cursor = len; | ||
451 | break; | ||
452 | case 'C': | ||
453 | /* Right Arrow -- Move forward one character */ | ||
454 | if (cursor < len) { | ||
455 | xwrite(outputFd, "\033[C", 3); | ||
456 | cursor++; | ||
457 | } | ||
458 | break; | ||
459 | case 'D': | ||
460 | /* Left Arrow -- Move back one character */ | ||
461 | if (cursor > 0) { | ||
462 | xwrite(outputFd, "\033[D", 3); | ||
463 | cursor--; | ||
464 | } | ||
465 | break; | ||
466 | case '3': | ||
467 | /* Delete */ | ||
468 | if (cursor != len) { | ||
469 | input_delete(outputFd, cursor); | ||
470 | len--; | ||
471 | } | ||
472 | break; | ||
473 | case '1': | ||
474 | /* Home (Ctrl-A) */ | ||
475 | input_home(outputFd, &cursor); | ||
476 | break; | ||
477 | case '4': | ||
478 | /* End (Ctrl-E) */ | ||
479 | input_end(outputFd, &cursor, len); | ||
480 | break; | ||
481 | } | ||
482 | if (c == '1' || c == '3' || c == '4') | ||
483 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
484 | return ret; /* read 126 (~) */ | ||
485 | } | ||
486 | if (c == 'O') { | ||
487 | /* 79 */ | ||
488 | if ((ret = read(inputFd, &c, 1)) < 1) | 255 | if ((ret = read(inputFd, &c, 1)) < 1) |
489 | return ret; | 256 | return ret; |
257 | |||
490 | switch (c) { | 258 | switch (c) { |
491 | case 'H': | 259 | case 1: |
492 | /* Home (xterm) */ | 260 | /* Control-a -- Beginning of line */ |
493 | input_home(outputFd, &cursor); | 261 | input_home(outputFd, &cursor); |
494 | break; | 262 | case 5: |
495 | case 'F': | 263 | /* Control-e -- End of line */ |
496 | /* End (xterm) */ | 264 | input_end(outputFd, &cursor, len); |
497 | input_end(outputFd, &cursor, len); | 265 | break; |
498 | break; | 266 | case 2: |
499 | } | 267 | /* Control-b -- Move back one character */ |
500 | } | 268 | if (cursor > 0) { |
501 | c = 0; | 269 | xwrite(outputFd, "\033[D", 3); |
502 | break; | 270 | cursor--; |
503 | } | 271 | } |
272 | break; | ||
273 | case 6: | ||
274 | /* Control-f -- Move forward one character */ | ||
275 | if (cursor < len) { | ||
276 | xwrite(outputFd, "\033[C", 3); | ||
277 | cursor++; | ||
278 | } | ||
279 | break; | ||
280 | case 4: | ||
281 | /* Control-d -- Delete one character */ | ||
282 | if (cursor != len) { | ||
283 | input_delete(outputFd, cursor); | ||
284 | len--; | ||
285 | } else if (len == 0) { | ||
286 | prepareToDie(0); | ||
287 | exit(0); | ||
288 | } | ||
289 | break; | ||
290 | case 14: | ||
291 | /* Control-n -- Get next command */ | ||
292 | if (hp && hp->n && hp->n->s) { | ||
293 | free(hp->s); | ||
294 | hp->s = strdup(parsenextc); | ||
295 | hp = hp->n; | ||
296 | goto hop; | ||
297 | } | ||
298 | break; | ||
299 | case 16: | ||
300 | /* Control-p -- Get previous command */ | ||
301 | if (hp && hp->p) { | ||
302 | free(hp->s); | ||
303 | hp->s = strdup(parsenextc); | ||
304 | hp = hp->p; | ||
305 | goto hop; | ||
306 | } | ||
307 | break; | ||
308 | case '\t': | ||
309 | { | ||
310 | /* Do TAB completion */ | ||
311 | static int num_matches=0; | ||
312 | static char **matches = (char **) NULL; | ||
313 | int pos = cursor; | ||
314 | |||
315 | |||
316 | if (lastWasTab == FALSE) { | ||
317 | char *tmp, *tmp1, *matchBuf; | ||
318 | |||
319 | /* For now, we will not bother with trying to distinguish | ||
320 | * whether the cursor is in/at a command extression -- we | ||
321 | * will always try all possable matches. If you don't like | ||
322 | * that, feel free to fix it. | ||
323 | */ | ||
324 | |||
325 | /* Make a local copy of the string -- up | ||
326 | * to the the position of the cursor */ | ||
327 | matchBuf = (char *) calloc(BUFSIZ, sizeof(char)); | ||
328 | strncpy(matchBuf, parsenextc, cursor); | ||
329 | tmp=matchBuf; | ||
330 | |||
331 | /* skip past any command seperator tokens */ | ||
332 | while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { | ||
333 | tmp=++tmp1; | ||
334 | /* skip any leading white space */ | ||
335 | while (*tmp && isspace(*tmp)) | ||
336 | ++tmp; | ||
337 | } | ||
338 | |||
339 | /* skip any leading white space */ | ||
340 | while (*tmp && isspace(*tmp)) | ||
341 | ++tmp; | ||
342 | |||
343 | /* Free up any memory already allocated */ | ||
344 | if (matches) { | ||
345 | free(matches); | ||
346 | matches = (char **) NULL; | ||
347 | } | ||
348 | |||
349 | /* If the word starts in `~', and there is no slash in the word, | ||
350 | * then try completing this word as a username. */ | ||
351 | if (*tmp == '~' && !strchr(tmp, '/')) | ||
352 | matches = username_completion_matches(tmp, &num_matches); | ||
353 | |||
354 | /* Try to match any executable in our patch, and everything | ||
355 | * in the current working directory that matches. | ||
356 | */ | ||
357 | if (!matches) | ||
358 | matches = find_path_executable_n_cwd_matches(tmp, &num_matches); | ||
359 | } else { | ||
360 | if ( matches && num_matches>0 ) { | ||
361 | int i, col; | ||
362 | |||
363 | fprintf(stderr, "\nTabbing...\n"); | ||
364 | |||
365 | /* Make a list of the matches */ | ||
366 | col += xwrite(outputFd, "\n", 1); | ||
367 | for (i=0,col=0; i<num_matches; i++) { | ||
368 | col += xwrite(outputFd, prompt, strlen(matches[i])); | ||
369 | if (col > 60 && matches[i+1] != NULL) { | ||
370 | xwrite(outputFd, "\n", 1); | ||
371 | col = 0; | ||
372 | } | ||
373 | } | ||
374 | xwrite(outputFd, "\n", 1); | ||
375 | |||
376 | len+=strlen(prompt); | ||
377 | fprintf(stderr, "len=%d\n", len); | ||
378 | |||
379 | /* Move to the beginning of the line */ | ||
380 | input_home(outputFd, &len); | ||
381 | |||
382 | /* erase everything */ | ||
383 | for (j = 0; j < len; j++) | ||
384 | xwrite(outputFd, " ", 1); | ||
385 | |||
386 | /* return to begining of line */ | ||
387 | input_home(outputFd, &cursor); | ||
388 | |||
389 | /* Rewrite the prompt) */ | ||
390 | xwrite(outputFd, prompt, strlen(prompt)); | ||
391 | |||
392 | /* Rewrite the command */ | ||
393 | len = strlen(parsenextc); | ||
394 | xwrite(outputFd, parsenextc, len); | ||
395 | |||
396 | /* Move back to where the cursor used to be */ | ||
397 | for (cursor=pos; cursor > 0; cursor--) | ||
398 | xwrite(outputFd, "\b", 1); | ||
399 | cursor = pos; | ||
400 | |||
401 | //fprintf(stderr, "\nprompt='%s'\n", prompt); | ||
402 | } | ||
403 | } | ||
404 | break; | ||
405 | } | ||
406 | case '\b': | ||
407 | case DEL: | ||
408 | /* Backspace */ | ||
409 | input_backspace(outputFd, &cursor, &len); | ||
410 | break; | ||
411 | case '\n': | ||
412 | /* Enter */ | ||
413 | *(parsenextc + len++ + 1) = c; | ||
414 | xwrite(outputFd, &c, 1); | ||
415 | break_out = 1; | ||
416 | break; | ||
417 | case ESC:{ | ||
418 | /* escape sequence follows */ | ||
419 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
420 | return ret; | ||
421 | |||
422 | if (c == '[') { /* 91 */ | ||
423 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
424 | return ret; | ||
425 | |||
426 | switch (c) { | ||
427 | case 'A': | ||
428 | /* Up Arrow -- Get previous command */ | ||
429 | if (hp && hp->p) { | ||
430 | free(hp->s); | ||
431 | hp->s = strdup(parsenextc); | ||
432 | hp = hp->p; | ||
433 | goto hop; | ||
434 | } | ||
435 | break; | ||
436 | case 'B': | ||
437 | /* Down Arrow -- Get next command */ | ||
438 | if (hp && hp->n && hp->n->s) { | ||
439 | free(hp->s); | ||
440 | hp->s = strdup(parsenextc); | ||
441 | hp = hp->n; | ||
442 | goto hop; | ||
443 | } | ||
444 | break; | ||
445 | |||
446 | /* This is where we rewrite the line | ||
447 | * using the selected history item */ | ||
448 | hop: | ||
449 | len = strlen(parsenextc); | ||
450 | |||
451 | /* return to begining of line */ | ||
452 | for (; cursor > 0; cursor--) | ||
453 | xwrite(outputFd, "\b", 1); | ||
454 | xwrite(outputFd, parsenextc, len); | ||
455 | |||
456 | /* erase old command */ | ||
457 | for (j = 0; j < len; j++) | ||
458 | xwrite(outputFd, " ", 1); | ||
459 | |||
460 | /* return to begining of line */ | ||
461 | for (j = len; j > 0; j--) | ||
462 | xwrite(outputFd, "\b", 1); | ||
463 | |||
464 | memset(parsenextc, 0, BUFSIZ); | ||
465 | /* write new command */ | ||
466 | strcpy(parsenextc, hp->s); | ||
467 | len = strlen(hp->s); | ||
468 | xwrite(outputFd, parsenextc, len); | ||
469 | cursor = len; | ||
470 | break; | ||
471 | case 'C': | ||
472 | /* Right Arrow -- Move forward one character */ | ||
473 | if (cursor < len) { | ||
474 | xwrite(outputFd, "\033[C", 3); | ||
475 | cursor++; | ||
476 | } | ||
477 | break; | ||
478 | case 'D': | ||
479 | /* Left Arrow -- Move back one character */ | ||
480 | if (cursor > 0) { | ||
481 | xwrite(outputFd, "\033[D", 3); | ||
482 | cursor--; | ||
483 | } | ||
484 | break; | ||
485 | case '3': | ||
486 | /* Delete */ | ||
487 | if (cursor != len) { | ||
488 | input_delete(outputFd, cursor); | ||
489 | len--; | ||
490 | } | ||
491 | break; | ||
492 | case '1': | ||
493 | /* Home (Ctrl-A) */ | ||
494 | input_home(outputFd, &cursor); | ||
495 | break; | ||
496 | case '4': | ||
497 | /* End (Ctrl-E) */ | ||
498 | input_end(outputFd, &cursor, len); | ||
499 | break; | ||
500 | } | ||
501 | if (c == '1' || c == '3' || c == '4') | ||
502 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
503 | return ret; /* read 126 (~) */ | ||
504 | } | ||
505 | if (c == 'O') { | ||
506 | /* 79 */ | ||
507 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
508 | return ret; | ||
509 | switch (c) { | ||
510 | case 'H': | ||
511 | /* Home (xterm) */ | ||
512 | input_home(outputFd, &cursor); | ||
513 | break; | ||
514 | case 'F': | ||
515 | /* End (xterm) */ | ||
516 | input_end(outputFd, &cursor, len); | ||
517 | break; | ||
518 | } | ||
519 | } | ||
520 | c = 0; | ||
521 | break; | ||
522 | } | ||
523 | |||
524 | default: /* If it's regular input, do the normal thing */ | ||
525 | |||
526 | if (!isprint(c)) /* Skip non-printable characters */ | ||
527 | break; | ||
528 | |||
529 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ | ||
530 | break; | ||
531 | |||
532 | len++; | ||
533 | |||
534 | if (cursor == (len - 1)) { /* Append if at the end of the line */ | ||
535 | *(parsenextc + cursor) = c; | ||
536 | } else { /* Insert otherwise */ | ||
537 | memmove(parsenextc + cursor + 1, parsenextc + cursor, | ||
538 | len - cursor - 1); | ||
539 | |||
540 | *(parsenextc + cursor) = c; | ||
541 | |||
542 | for (j = cursor; j < len; j++) | ||
543 | xwrite(outputFd, parsenextc + j, 1); | ||
544 | for (; j > cursor; j--) | ||
545 | xwrite(outputFd, "\033[D", 3); | ||
546 | } | ||
504 | 547 | ||
505 | default: /* If it's regular input, do the normal thing */ | 548 | cursor++; |
549 | xwrite(outputFd, &c, 1); | ||
550 | break; | ||
551 | } | ||
552 | if (c == '\t') | ||
553 | lastWasTab = TRUE; | ||
554 | else | ||
555 | lastWasTab = FALSE; | ||
506 | 556 | ||
507 | if (!isprint(c)) /* Skip non-printable characters */ | 557 | if (break_out) /* Enter is the command terminator, no more input. */ |
508 | break; | 558 | break; |
559 | } | ||
509 | 560 | ||
510 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ | 561 | nr = len + 1; |
511 | break; | 562 | xioctl(inputFd, TCSETA, (void *) &old_term); |
563 | reset_term = 0; | ||
512 | 564 | ||
513 | len++; | ||
514 | 565 | ||
515 | if (cursor == (len - 1)) { /* Append if at the end of the line */ | 566 | /* Handle command history log */ |
516 | *(parsenextc + cursor) = c; | 567 | if (*(parsenextc)) { |
517 | } else { /* Insert otherwise */ | ||
518 | memmove(parsenextc + cursor + 1, parsenextc + cursor, | ||
519 | len - cursor - 1); | ||
520 | 568 | ||
521 | *(parsenextc + cursor) = c; | 569 | struct history *h = his_end; |
522 | 570 | ||
523 | for (j = cursor; j < len; j++) | 571 | if (!h) { |
524 | xwrite(outputFd, parsenextc + j, 1); | 572 | /* No previous history */ |
525 | for (; j > cursor; j--) | 573 | h = his_front = malloc(sizeof(struct history)); |
526 | xwrite(outputFd, "\033[D", 3); | 574 | h->n = malloc(sizeof(struct history)); |
527 | } | ||
528 | 575 | ||
529 | cursor++; | 576 | h->p = NULL; |
530 | xwrite(outputFd, &c, 1); | 577 | h->s = strdup(parsenextc); |
531 | break; | 578 | h->n->p = h; |
532 | } | 579 | h->n->n = NULL; |
533 | if (c=='\t') | 580 | h->n->s = NULL; |
534 | lastWasTab = TRUE; | 581 | his_end = h->n; |
535 | else | 582 | history_counter++; |
536 | lastWasTab = FALSE; | 583 | } else { |
537 | 584 | /* Add a new history command */ | |
538 | if (break_out) /* Enter is the command terminator, no more input. */ | 585 | h->n = malloc(sizeof(struct history)); |
539 | break; | 586 | |
540 | } | 587 | h->n->p = h; |
541 | 588 | h->n->n = NULL; | |
542 | nr = len + 1; | 589 | h->n->s = NULL; |
543 | xioctl(inputFd, TCSETA, (void *) &old_term); | 590 | h->s = strdup(parsenextc); |
544 | reset_term = 0; | 591 | his_end = h->n; |
545 | 592 | ||
546 | 593 | /* After max history, remove the oldest command */ | |
547 | /* Handle command history log */ | 594 | if (history_counter >= MAX_HISTORY) { |
548 | if (*(parsenextc)) { | 595 | |
549 | 596 | struct history *p = his_front->n; | |
550 | struct history *h = his_end; | 597 | |
551 | 598 | p->p = NULL; | |
552 | if (!h) { | 599 | free(his_front->s); |
553 | /* No previous history */ | 600 | free(his_front); |
554 | h = his_front = malloc(sizeof(struct history)); | 601 | his_front = p; |
555 | h->n = malloc(sizeof(struct history)); | 602 | } else { |
556 | h->p = NULL; | 603 | history_counter++; |
557 | h->s = strdup(parsenextc); | 604 | } |
558 | h->n->p = h; | 605 | } |
559 | h->n->n = NULL; | ||
560 | h->n->s = NULL; | ||
561 | his_end = h->n; | ||
562 | history_counter++; | ||
563 | } else { | ||
564 | /* Add a new history command */ | ||
565 | h->n = malloc(sizeof(struct history)); | ||
566 | h->n->p = h; | ||
567 | h->n->n = NULL; | ||
568 | h->n->s = NULL; | ||
569 | h->s = strdup(parsenextc); | ||
570 | his_end = h->n; | ||
571 | |||
572 | /* After max history, remove the oldest command */ | ||
573 | if (history_counter >= MAX_HISTORY) { | ||
574 | |||
575 | struct history *p = his_front->n; | ||
576 | |||
577 | p->p = NULL; | ||
578 | free(his_front->s); | ||
579 | free(his_front); | ||
580 | his_front = p; | ||
581 | } else { | ||
582 | history_counter++; | ||
583 | } | ||
584 | } | 606 | } |
585 | } | ||
586 | 607 | ||
587 | return nr; | 608 | return nr; |
588 | } | 609 | } |
589 | 610 | ||
590 | extern void cmdedit_init(void) | 611 | extern void cmdedit_init(void) |
591 | { | 612 | { |
592 | atexit(cmdedit_reset_term); | 613 | atexit(cmdedit_reset_term); |
593 | signal(SIGINT, prepareToDie); | 614 | signal(SIGINT, prepareToDie); |
594 | signal(SIGQUIT, prepareToDie); | 615 | signal(SIGQUIT, prepareToDie); |
595 | signal(SIGTERM, prepareToDie); | 616 | signal(SIGTERM, prepareToDie); |
596 | } | 617 | } |
597 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ | 618 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ |
@@ -12,6 +12,6 @@ | |||
12 | * | 12 | * |
13 | */ | 13 | */ |
14 | 14 | ||
15 | extern int cmdedit_read_input(int inputFd, int outputFd, char command[BUFSIZ]); | 15 | extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]); |
16 | extern void cmdedit_init(void); | 16 | extern void cmdedit_init(void); |
17 | 17 | ||
diff --git a/internal.h b/internal.h index c91d3b19d..353ec8ca3 100644 --- a/internal.h +++ b/internal.h | |||
@@ -216,6 +216,12 @@ extern int check_wildcard_match(const char* text, const char* pattern); | |||
216 | extern long getNum (const char *cp); | 216 | extern long getNum (const char *cp); |
217 | extern pid_t findPidByName( char* pidName); | 217 | extern pid_t findPidByName( char* pidName); |
218 | extern void *xmalloc (size_t size); | 218 | extern void *xmalloc (size_t size); |
219 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
220 | #include <stdio.h> | ||
221 | extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]); | ||
222 | extern void cmdedit_init(void); | ||
223 | #endif | ||
224 | |||
219 | #if defined BB_INIT || defined BB_SYSLOGD | 225 | #if defined BB_INIT || defined BB_SYSLOGD |
220 | extern int device_open(char *device, int mode); | 226 | extern int device_open(char *device, int mode); |
221 | #endif | 227 | #endif |
@@ -39,10 +39,6 @@ | |||
39 | #include <unistd.h> | 39 | #include <unistd.h> |
40 | 40 | ||
41 | 41 | ||
42 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
43 | #include "cmdedit.h" | ||
44 | #endif | ||
45 | |||
46 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | 42 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" |
47 | 43 | ||
48 | 44 | ||
@@ -123,8 +119,7 @@ static struct builtInCommand bltins[] = { | |||
123 | {"export", "Set environment variable", "export [VAR=value]", shell_export}, | 119 | {"export", "Set environment variable", "export [VAR=value]", shell_export}, |
124 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, | 120 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, |
125 | 121 | ||
126 | {".", "Source-in and run commands in a file", ". filename", | 122 | {".", "Source-in and run commands in a file", ". filename", shell_source}, |
127 | shell_source}, | ||
128 | {"help", "List shell built-in commands", "help", shell_help}, | 123 | {"help", "List shell built-in commands", "help", shell_help}, |
129 | {NULL, NULL, NULL, NULL} | 124 | {NULL, NULL, NULL, NULL} |
130 | }; | 125 | }; |
@@ -396,11 +391,19 @@ static void checkJobs(struct jobSet *jobList) | |||
396 | static int getCommand(FILE * source, char *command) | 391 | static int getCommand(FILE * source, char *command) |
397 | { | 392 | { |
398 | if (source == stdin) { | 393 | if (source == stdin) { |
399 | fprintf(stdout, "BBSHELL %s %s", cwd, prompt); | ||
400 | fflush(stdout); | ||
401 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | 394 | #ifdef BB_FEATURE_SH_COMMAND_EDITING |
402 | cmdedit_read_input(fileno(stdin), fileno(stdout), command); | 395 | int len; |
396 | char *promptStr; | ||
397 | len=fprintf(stdout, "BBSHELL %s %s", cwd, prompt); | ||
398 | fflush(stdout); | ||
399 | promptStr=(char*)malloc(sizeof(char)*(len+1)); | ||
400 | sprintf(promptStr, "BBSHELL %s %s", cwd, prompt); | ||
401 | cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); | ||
402 | free( promptStr); | ||
403 | return 0; | 403 | return 0; |
404 | #else | ||
405 | fprintf(stdout, "%s %s", cwd, prompt); | ||
406 | fflush(stdout); | ||
404 | #endif | 407 | #endif |
405 | } | 408 | } |
406 | 409 | ||
@@ -39,10 +39,6 @@ | |||
39 | #include <unistd.h> | 39 | #include <unistd.h> |
40 | 40 | ||
41 | 41 | ||
42 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
43 | #include "cmdedit.h" | ||
44 | #endif | ||
45 | |||
46 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | 42 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" |
47 | 43 | ||
48 | 44 | ||
@@ -123,8 +119,7 @@ static struct builtInCommand bltins[] = { | |||
123 | {"export", "Set environment variable", "export [VAR=value]", shell_export}, | 119 | {"export", "Set environment variable", "export [VAR=value]", shell_export}, |
124 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, | 120 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, |
125 | 121 | ||
126 | {".", "Source-in and run commands in a file", ". filename", | 122 | {".", "Source-in and run commands in a file", ". filename", shell_source}, |
127 | shell_source}, | ||
128 | {"help", "List shell built-in commands", "help", shell_help}, | 123 | {"help", "List shell built-in commands", "help", shell_help}, |
129 | {NULL, NULL, NULL, NULL} | 124 | {NULL, NULL, NULL, NULL} |
130 | }; | 125 | }; |
@@ -396,11 +391,19 @@ static void checkJobs(struct jobSet *jobList) | |||
396 | static int getCommand(FILE * source, char *command) | 391 | static int getCommand(FILE * source, char *command) |
397 | { | 392 | { |
398 | if (source == stdin) { | 393 | if (source == stdin) { |
399 | fprintf(stdout, "BBSHELL %s %s", cwd, prompt); | ||
400 | fflush(stdout); | ||
401 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | 394 | #ifdef BB_FEATURE_SH_COMMAND_EDITING |
402 | cmdedit_read_input(fileno(stdin), fileno(stdout), command); | 395 | int len; |
396 | char *promptStr; | ||
397 | len=fprintf(stdout, "BBSHELL %s %s", cwd, prompt); | ||
398 | fflush(stdout); | ||
399 | promptStr=(char*)malloc(sizeof(char)*(len+1)); | ||
400 | sprintf(promptStr, "BBSHELL %s %s", cwd, prompt); | ||
401 | cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); | ||
402 | free( promptStr); | ||
403 | return 0; | 403 | return 0; |
404 | #else | ||
405 | fprintf(stdout, "%s %s", cwd, prompt); | ||
406 | fflush(stdout); | ||
404 | #endif | 407 | #endif |
405 | } | 408 | } |
406 | 409 | ||
diff --git a/shell/cmdedit.c b/shell/cmdedit.c index 9e162c6ee..8a7a5fb03 100644 --- a/shell/cmdedit.c +++ b/shell/cmdedit.c | |||
@@ -1,3 +1,4 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
1 | /* | 2 | /* |
2 | * Termios command line History and Editting for NetBSD sh (ash) | 3 | * Termios command line History and Editting for NetBSD sh (ash) |
3 | * Copyright (c) 1999 | 4 | * Copyright (c) 1999 |
@@ -40,10 +41,8 @@ | |||
40 | #include <ctype.h> | 41 | #include <ctype.h> |
41 | #include <signal.h> | 42 | #include <signal.h> |
42 | 43 | ||
43 | #include "cmdedit.h" | ||
44 | 44 | ||
45 | 45 | #define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */ | |
46 | #define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */ | ||
47 | 46 | ||
48 | #define ESC 27 | 47 | #define ESC 27 |
49 | #define DEL 127 | 48 | #define DEL 127 |
@@ -55,142 +54,150 @@ static struct history *his_end = NULL; /* Last element in command line list */ | |||
55 | 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 */ |
56 | 55 | ||
57 | static int history_counter = 0; /* Number of commands in history list */ | 56 | static int history_counter = 0; /* Number of commands in history list */ |
58 | static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ | 57 | static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ |
59 | char *parsenextc; /* copy of parsefile->nextc */ | 58 | char *parsenextc; /* copy of parsefile->nextc */ |
60 | 59 | ||
61 | struct history { | 60 | struct history { |
62 | char *s; | 61 | char *s; |
63 | struct history *p; | 62 | struct history *p; |
64 | struct history *n; | 63 | struct history *n; |
65 | }; | 64 | }; |
66 | 65 | ||
67 | 66 | ||
68 | /* Version of write which resumes after a signal is caught. */ | 67 | /* Version of write which resumes after a signal is caught. */ |
69 | int xwrite(int fd, char *buf, int nbytes) | 68 | int xwrite(int fd, char *buf, int nbytes) |
70 | { | 69 | { |
71 | int ntry; | 70 | int ntry; |
72 | int i; | 71 | int i; |
73 | int n; | 72 | int n; |
74 | 73 | ||
75 | n = nbytes; | 74 | n = nbytes; |
76 | ntry = 0; | 75 | ntry = 0; |
77 | for (;;) { | 76 | for (;;) { |
78 | i = write(fd, buf, n); | 77 | i = write(fd, buf, n); |
79 | if (i > 0) { | 78 | if (i > 0) { |
80 | if ((n -= i) <= 0) | 79 | if ((n -= i) <= 0) |
81 | return nbytes; | 80 | return nbytes; |
82 | buf += i; | 81 | buf += i; |
83 | ntry = 0; | 82 | ntry = 0; |
84 | } else if (i == 0) { | 83 | } else if (i == 0) { |
85 | if (++ntry > 10) | 84 | if (++ntry > 10) |
86 | return nbytes - n; | 85 | return nbytes - n; |
87 | } else if (errno != EINTR) { | 86 | } else if (errno != EINTR) { |
88 | return -1; | 87 | return -1; |
88 | } | ||
89 | } | 89 | } |
90 | } | ||
91 | } | 90 | } |
92 | 91 | ||
93 | 92 | ||
94 | /* Version of ioctl that retries after a signal is caught. */ | 93 | /* Version of ioctl that retries after a signal is caught. */ |
95 | int xioctl(int fd, unsigned long request, char *arg) | 94 | int xioctl(int fd, unsigned long request, char *arg) |
96 | { | 95 | { |
97 | int i; | 96 | int i; |
98 | while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); | 97 | |
99 | return i; | 98 | while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); |
99 | return i; | ||
100 | } | 100 | } |
101 | 101 | ||
102 | 102 | ||
103 | void cmdedit_reset_term(void) | 103 | void cmdedit_reset_term(void) |
104 | { | 104 | { |
105 | if (reset_term) | 105 | if (reset_term) |
106 | xioctl(fileno(stdin), TCSETA, (void *) &old_term); | 106 | xioctl(fileno(stdin), TCSETA, (void *) &old_term); |
107 | } | 107 | } |
108 | 108 | ||
109 | void prepareToDie(int sig) | 109 | void prepareToDie(int sig) |
110 | { | 110 | { |
111 | cmdedit_reset_term(); | 111 | cmdedit_reset_term(); |
112 | fprintf(stdout, "\n"); | 112 | fprintf(stdout, "\n"); |
113 | exit(TRUE); | 113 | exit(TRUE); |
114 | } | 114 | } |
115 | 115 | ||
116 | void input_home(int outputFd, int *cursor) | 116 | void input_home(int outputFd, int *cursor) |
117 | { /* Command line input routines */ | 117 | { /* Command line input routines */ |
118 | while (*cursor > 0) { | 118 | while (*cursor > 0) { |
119 | xwrite(outputFd, "\b", 1); | 119 | xwrite(outputFd, "\b", 1); |
120 | --*cursor; | 120 | --*cursor; |
121 | } | 121 | } |
122 | } | 122 | } |
123 | 123 | ||
124 | 124 | ||
125 | void input_delete(int outputFd, int cursor) | 125 | void input_delete(int outputFd, int cursor) |
126 | { | 126 | { |
127 | int j = 0; | 127 | int j = 0; |
128 | 128 | ||
129 | memmove(parsenextc + cursor, parsenextc + cursor + 1, | 129 | memmove(parsenextc + cursor, parsenextc + cursor + 1, |
130 | BUFSIZ - cursor - 1); | 130 | BUFSIZ - cursor - 1); |
131 | for (j = cursor; j < (BUFSIZ - 1); j++) { | 131 | for (j = cursor; j < (BUFSIZ - 1); j++) { |
132 | if (!*(parsenextc + j)) | 132 | if (!*(parsenextc + j)) |
133 | break; | 133 | break; |
134 | else | 134 | else |
135 | xwrite(outputFd, (parsenextc + j), 1); | 135 | xwrite(outputFd, (parsenextc + j), 1); |
136 | } | 136 | } |
137 | 137 | ||
138 | xwrite(outputFd, " \b", 2); | 138 | xwrite(outputFd, " \b", 2); |
139 | 139 | ||
140 | while (j-- > cursor) | 140 | while (j-- > cursor) |
141 | xwrite(outputFd, "\b", 1); | 141 | xwrite(outputFd, "\b", 1); |
142 | } | 142 | } |
143 | 143 | ||
144 | 144 | ||
145 | void input_end(int outputFd, int *cursor, int len) | 145 | void input_end(int outputFd, int *cursor, int len) |
146 | { | 146 | { |
147 | while (*cursor < len) { | 147 | while (*cursor < len) { |
148 | xwrite(outputFd, "\033[C", 3); | 148 | xwrite(outputFd, "\033[C", 3); |
149 | ++*cursor; | 149 | ++*cursor; |
150 | } | 150 | } |
151 | } | 151 | } |
152 | 152 | ||
153 | 153 | ||
154 | void input_backspace(int outputFd, int *cursor, int *len) | 154 | void input_backspace(int outputFd, int *cursor, int *len) |
155 | { | 155 | { |
156 | int j = 0; | 156 | int j = 0; |
157 | 157 | ||
158 | if (*cursor > 0) { | 158 | if (*cursor > 0) { |
159 | xwrite(outputFd, "\b \b", 3); | 159 | xwrite(outputFd, "\b \b", 3); |
160 | --*cursor; | 160 | --*cursor; |
161 | memmove(parsenextc + *cursor, parsenextc + *cursor + 1, | 161 | memmove(parsenextc + *cursor, parsenextc + *cursor + 1, |
162 | BUFSIZ - *cursor + 1); | 162 | BUFSIZ - *cursor + 1); |
163 | 163 | ||
164 | for (j = *cursor; j < (BUFSIZ - 1); j++) { | 164 | for (j = *cursor; j < (BUFSIZ - 1); j++) { |
165 | if (!*(parsenextc + j)) | 165 | if (!*(parsenextc + j)) |
166 | break; | 166 | break; |
167 | else | 167 | else |
168 | xwrite(outputFd, (parsenextc + j), 1); | 168 | xwrite(outputFd, (parsenextc + j), 1); |
169 | } | 169 | } |
170 | 170 | ||
171 | xwrite(outputFd, " \b", 2); | 171 | xwrite(outputFd, " \b", 2); |
172 | 172 | ||
173 | while (j-- > *cursor) | 173 | while (j-- > *cursor) |
174 | xwrite(outputFd, "\b", 1); | 174 | xwrite(outputFd, "\b", 1); |
175 | 175 | ||
176 | --*len; | 176 | --*len; |
177 | } | 177 | } |
178 | } | 178 | } |
179 | 179 | ||
180 | char **username_completion_matches( char* matchBuf) | 180 | char** username_completion_matches(char* command, int *num_matches) |
181 | { | 181 | { |
182 | fprintf(stderr, "\nin username_completion_matches\n"); | 182 | char **matches = (char **) NULL; |
183 | return ( (char**) NULL); | 183 | *num_matches=0; |
184 | fprintf(stderr, "\nin username_completion_matches\n"); | ||
185 | return (matches); | ||
184 | } | 186 | } |
185 | char **command_completion_matches( char* matchBuf) | 187 | char** find_path_executable_n_cwd_matches(char* command, int *num_matches) |
186 | { | 188 | { |
187 | fprintf(stderr, "\nin command_completion_matches\n"); | 189 | char **matches = (char **) NULL; |
188 | return ( (char**) NULL); | 190 | matches = malloc(sizeof(char*)*100); |
189 | } | 191 | |
190 | char **directory_completion_matches( char* matchBuf) | 192 | matches[0] = malloc(sizeof(char)*50); |
191 | { | 193 | matches[1] = malloc(sizeof(char)*50); |
192 | fprintf(stderr, "\nin directory_completion_matches\n"); | 194 | |
193 | return ( (char**) NULL); | 195 | sprintf(matches[0], "Hello"); |
196 | sprintf(matches[1], "Howdy"); | ||
197 | *num_matches=2; | ||
198 | |||
199 | // fprintf(stderr, "\nin find_path_executable_n_cwd_matches\n"); | ||
200 | return (matches); | ||
194 | } | 201 | } |
195 | 202 | ||
196 | /* | 203 | /* |
@@ -211,387 +218,401 @@ char **directory_completion_matches( char* matchBuf) | |||
211 | * TODO: implement TAB command completion. :) | 218 | * TODO: implement TAB command completion. :) |
212 | * | 219 | * |
213 | */ | 220 | */ |
214 | extern int cmdedit_read_input(int inputFd, int outputFd, | 221 | extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, |
215 | char command[BUFSIZ]) | 222 | char command[BUFSIZ]) |
216 | { | 223 | { |
217 | 224 | ||
218 | int nr = 0; | 225 | int nr = 0; |
219 | int len = 0; | 226 | int len = 0; |
220 | int j = 0; | 227 | int j = 0; |
221 | int cursor = 0; | 228 | int cursor = 0; |
222 | int break_out = 0; | 229 | int break_out = 0; |
223 | int ret = 0; | 230 | int ret = 0; |
224 | int lastWasTab = FALSE; | 231 | int lastWasTab = FALSE; |
225 | char **matches = (char **)NULL; | 232 | char c = 0; |
226 | char c = 0; | 233 | struct history *hp = his_end; |
227 | struct history *hp = his_end; | 234 | |
228 | 235 | memset(command, 0, sizeof(command)); | |
229 | memset(command, 0, sizeof(command)); | 236 | parsenextc = command; |
230 | parsenextc = command; | 237 | if (!reset_term) { |
231 | if (!reset_term) { | 238 | xioctl(inputFd, TCGETA, (void *) &old_term); |
232 | xioctl(inputFd, TCGETA, (void *) &old_term); | 239 | memcpy(&new_term, &old_term, sizeof(struct termio)); |
233 | memcpy(&new_term, &old_term, sizeof(struct termio)); | 240 | |
234 | new_term.c_cc[VMIN] = 1; | 241 | new_term.c_cc[VMIN] = 1; |
235 | new_term.c_cc[VTIME] = 0; | 242 | new_term.c_cc[VTIME] = 0; |
236 | new_term.c_lflag &= ~ICANON; /* unbuffered input */ | 243 | new_term.c_lflag &= ~ICANON; /* unbuffered input */ |
237 | new_term.c_lflag &= ~ECHO; | 244 | new_term.c_lflag &= ~ECHO; |
238 | xioctl(inputFd, TCSETA, (void *) &new_term); | 245 | xioctl(inputFd, TCSETA, (void *) &new_term); |
239 | reset_term = 1; | 246 | reset_term = 1; |
240 | } else { | 247 | } else { |
241 | xioctl(inputFd, TCSETA, (void *) &new_term); | 248 | xioctl(inputFd, TCSETA, (void *) &new_term); |
242 | } | 249 | } |
243 | |||
244 | memset(parsenextc, 0, BUFSIZ); | ||
245 | |||
246 | while (1) { | ||
247 | |||
248 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
249 | return ret; | ||
250 | |||
251 | switch (c) { | ||
252 | case 1: | ||
253 | /* Control-a -- Beginning of line */ | ||
254 | input_home(outputFd, &cursor); | ||
255 | case 5: | ||
256 | /* Control-e -- End of line */ | ||
257 | input_end(outputFd, &cursor, len); | ||
258 | break; | ||
259 | case 2: | ||
260 | /* Control-b -- Move back one character */ | ||
261 | if (cursor > 0) { | ||
262 | xwrite(outputFd, "\033[D", 3); | ||
263 | cursor--; | ||
264 | } | ||
265 | break; | ||
266 | case 6: | ||
267 | /* Control-f -- Move forward one character */ | ||
268 | if (cursor < len) { | ||
269 | xwrite(outputFd, "\033[C", 3); | ||
270 | cursor++; | ||
271 | } | ||
272 | break; | ||
273 | case 4: | ||
274 | /* Control-d -- Delete one character */ | ||
275 | if (cursor != len) { | ||
276 | input_delete(outputFd, cursor); | ||
277 | len--; | ||
278 | } else if (len == 0) { | ||
279 | prepareToDie(0); | ||
280 | exit(0); | ||
281 | } | ||
282 | break; | ||
283 | case 14: | ||
284 | /* Control-n -- Get next command */ | ||
285 | if (hp && hp->n && hp->n->s) { | ||
286 | free( hp->s); | ||
287 | hp->s = strdup(parsenextc); | ||
288 | hp = hp->n; | ||
289 | goto hop; | ||
290 | } | ||
291 | break; | ||
292 | case 16: | ||
293 | /* Control-p -- Get previous command */ | ||
294 | if (hp && hp->p) { | ||
295 | free( hp->s); | ||
296 | hp->s = strdup(parsenextc); | ||
297 | hp = hp->p; | ||
298 | goto hop; | ||
299 | } | ||
300 | break; | ||
301 | case '\t': | ||
302 | { | ||
303 | /* Do TAB completion */ | ||
304 | int in_command_position=0, ti=len-1; | ||
305 | |||
306 | if (lastWasTab == FALSE) { | ||
307 | char *tmp; | ||
308 | char *matchBuf; | ||
309 | |||
310 | if (matches) { | ||
311 | free(matches); | ||
312 | matches = (char **)NULL; | ||
313 | } | ||
314 | |||
315 | matchBuf = (char *) calloc(BUFSIZ, sizeof(char)); | ||
316 | |||
317 | /* Make a local copy of the string -- up | ||
318 | * to the the position of the cursor */ | ||
319 | strcpy( matchBuf, parsenextc); | ||
320 | matchBuf[cursor+1] = '\0'; | ||
321 | |||
322 | fprintf(stderr, "matchBuf='%s'\n", matchBuf); | ||
323 | |||
324 | /* skip leading white space */ | ||
325 | tmp = matchBuf; | ||
326 | while (*tmp && isspace(*tmp)) { | ||
327 | (tmp)++; | ||
328 | ti++; | ||
329 | } | ||
330 | |||
331 | /* Determine if this is a command word or not */ | ||
332 | //while ((ti > -1) && (whitespace (matchBuf[ti]))) { | ||
333 | //printf("\nti=%d\n", ti); | ||
334 | // ti--; | ||
335 | // } | ||
336 | printf("\nti=%d\n", ti); | ||
337 | |||
338 | if (ti < 0) { | ||
339 | in_command_position++; | ||
340 | } else if (member(matchBuf[ti], ";|&{(`")) { | ||
341 | int this_char, prev_char; | ||
342 | in_command_position++; | ||
343 | /* Handle the two character tokens `>&', `<&', and `>|'. | ||
344 | We are not in a command position after one of these. */ | ||
345 | this_char = matchBuf[ti]; | ||
346 | prev_char = matchBuf[ti - 1]; | ||
347 | |||
348 | if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) || | ||
349 | (this_char == '|' && prev_char == '>')) { | ||
350 | in_command_position = 0; | ||
351 | } | ||
352 | /* For now, do not bother with catching quoted | ||
353 | * expressions and marking them as not in command | ||
354 | * positions. Some other day. Or not. | ||
355 | */ | ||
356 | //else if (char_is_quoted (matchBuf, ti)) { | ||
357 | // in_command_position = 0; | ||
358 | //} | ||
359 | } | ||
360 | printf("\nin_command_position=%d\n", in_command_position); | ||
361 | /* If the word starts in `~', and there is no slash in the word, | ||
362 | * then try completing this word as a username. */ | ||
363 | if (*matchBuf == '~' && !strchr (matchBuf, '/')) | ||
364 | matches = username_completion_matches(matchBuf); | ||
365 | |||
366 | /* If this word is in a command position, then complete over possible | ||
367 | * command names, including aliases, built-ins, and executables. */ | ||
368 | if (!matches && in_command_position) { | ||
369 | matches = command_completion_matches(matchBuf); | ||
370 | |||
371 | /* If we are attempting command completion and nothing matches, | ||
372 | * then try and match directories as a last resort... */ | ||
373 | if (!matches) | ||
374 | matches = directory_completion_matches(matchBuf); | ||
375 | } | ||
376 | } else { | ||
377 | printf("\nprinting match list\n"); | ||
378 | } | ||
379 | /* Rewrite the whole line (for debugging) */ | ||
380 | for (; cursor > 0; cursor--) | ||
381 | xwrite(outputFd, "\b", 1); | ||
382 | len = strlen(parsenextc); | ||
383 | xwrite(outputFd, parsenextc, len); | ||
384 | cursor = len; | ||
385 | break; | ||
386 | } | ||
387 | case '\b': | ||
388 | case DEL: | ||
389 | /* Backspace */ | ||
390 | input_backspace(outputFd, &cursor, &len); | ||
391 | break; | ||
392 | case '\n': | ||
393 | /* Enter */ | ||
394 | *(parsenextc + len++ + 1) = c; | ||
395 | xwrite(outputFd, &c, 1); | ||
396 | break_out = 1; | ||
397 | break; | ||
398 | case ESC: { | ||
399 | /* escape sequence follows */ | ||
400 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
401 | return ret; | ||
402 | |||
403 | if (c == '[') { /* 91 */ | ||
404 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
405 | return ret; | ||
406 | |||
407 | switch (c) { | ||
408 | case 'A': | ||
409 | /* Up Arrow -- Get previous command */ | ||
410 | if (hp && hp->p) { | ||
411 | free( hp->s); | ||
412 | hp->s = strdup(parsenextc); | ||
413 | hp = hp->p; | ||
414 | goto hop; | ||
415 | } | ||
416 | break; | ||
417 | case 'B': | ||
418 | /* Down Arrow -- Get next command */ | ||
419 | if (hp && hp->n && hp->n->s) { | ||
420 | free( hp->s); | ||
421 | hp->s = strdup(parsenextc); | ||
422 | hp = hp->n; | ||
423 | goto hop; | ||
424 | } | ||
425 | break; | ||
426 | |||
427 | /* This is where we rewrite the line | ||
428 | * using the selected history item */ | ||
429 | hop: | ||
430 | len = strlen(parsenextc); | ||
431 | |||
432 | /* return to begining of line */ | ||
433 | for (; cursor > 0; cursor--) | ||
434 | xwrite(outputFd, "\b", 1); | ||
435 | xwrite(outputFd, parsenextc, len); | ||
436 | 250 | ||
437 | /* erase old command */ | 251 | memset(parsenextc, 0, BUFSIZ); |
438 | for (j = 0; j < len; j++) | ||
439 | xwrite(outputFd, " ", 1); | ||
440 | 252 | ||
441 | /* return to begining of line */ | 253 | while (1) { |
442 | for (j = len; j > 0; j--) | ||
443 | xwrite(outputFd, "\b", 1); | ||
444 | 254 | ||
445 | memset(parsenextc, 0, BUFSIZ); | ||
446 | /* write new command */ | ||
447 | strcpy(parsenextc, hp->s); | ||
448 | len = strlen(hp->s); | ||
449 | xwrite(outputFd, parsenextc, len); | ||
450 | cursor = len; | ||
451 | break; | ||
452 | case 'C': | ||
453 | /* Right Arrow -- Move forward one character */ | ||
454 | if (cursor < len) { | ||
455 | xwrite(outputFd, "\033[C", 3); | ||
456 | cursor++; | ||
457 | } | ||
458 | break; | ||
459 | case 'D': | ||
460 | /* Left Arrow -- Move back one character */ | ||
461 | if (cursor > 0) { | ||
462 | xwrite(outputFd, "\033[D", 3); | ||
463 | cursor--; | ||
464 | } | ||
465 | break; | ||
466 | case '3': | ||
467 | /* Delete */ | ||
468 | if (cursor != len) { | ||
469 | input_delete(outputFd, cursor); | ||
470 | len--; | ||
471 | } | ||
472 | break; | ||
473 | case '1': | ||
474 | /* Home (Ctrl-A) */ | ||
475 | input_home(outputFd, &cursor); | ||
476 | break; | ||
477 | case '4': | ||
478 | /* End (Ctrl-E) */ | ||
479 | input_end(outputFd, &cursor, len); | ||
480 | break; | ||
481 | } | ||
482 | if (c == '1' || c == '3' || c == '4') | ||
483 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
484 | return ret; /* read 126 (~) */ | ||
485 | } | ||
486 | if (c == 'O') { | ||
487 | /* 79 */ | ||
488 | if ((ret = read(inputFd, &c, 1)) < 1) | 255 | if ((ret = read(inputFd, &c, 1)) < 1) |
489 | return ret; | 256 | return ret; |
257 | |||
490 | switch (c) { | 258 | switch (c) { |
491 | case 'H': | 259 | case 1: |
492 | /* Home (xterm) */ | 260 | /* Control-a -- Beginning of line */ |
493 | input_home(outputFd, &cursor); | 261 | input_home(outputFd, &cursor); |
494 | break; | 262 | case 5: |
495 | case 'F': | 263 | /* Control-e -- End of line */ |
496 | /* End (xterm) */ | 264 | input_end(outputFd, &cursor, len); |
497 | input_end(outputFd, &cursor, len); | 265 | break; |
498 | break; | 266 | case 2: |
499 | } | 267 | /* Control-b -- Move back one character */ |
500 | } | 268 | if (cursor > 0) { |
501 | c = 0; | 269 | xwrite(outputFd, "\033[D", 3); |
502 | break; | 270 | cursor--; |
503 | } | 271 | } |
272 | break; | ||
273 | case 6: | ||
274 | /* Control-f -- Move forward one character */ | ||
275 | if (cursor < len) { | ||
276 | xwrite(outputFd, "\033[C", 3); | ||
277 | cursor++; | ||
278 | } | ||
279 | break; | ||
280 | case 4: | ||
281 | /* Control-d -- Delete one character */ | ||
282 | if (cursor != len) { | ||
283 | input_delete(outputFd, cursor); | ||
284 | len--; | ||
285 | } else if (len == 0) { | ||
286 | prepareToDie(0); | ||
287 | exit(0); | ||
288 | } | ||
289 | break; | ||
290 | case 14: | ||
291 | /* Control-n -- Get next command */ | ||
292 | if (hp && hp->n && hp->n->s) { | ||
293 | free(hp->s); | ||
294 | hp->s = strdup(parsenextc); | ||
295 | hp = hp->n; | ||
296 | goto hop; | ||
297 | } | ||
298 | break; | ||
299 | case 16: | ||
300 | /* Control-p -- Get previous command */ | ||
301 | if (hp && hp->p) { | ||
302 | free(hp->s); | ||
303 | hp->s = strdup(parsenextc); | ||
304 | hp = hp->p; | ||
305 | goto hop; | ||
306 | } | ||
307 | break; | ||
308 | case '\t': | ||
309 | { | ||
310 | /* Do TAB completion */ | ||
311 | static int num_matches=0; | ||
312 | static char **matches = (char **) NULL; | ||
313 | int pos = cursor; | ||
314 | |||
315 | |||
316 | if (lastWasTab == FALSE) { | ||
317 | char *tmp, *tmp1, *matchBuf; | ||
318 | |||
319 | /* For now, we will not bother with trying to distinguish | ||
320 | * whether the cursor is in/at a command extression -- we | ||
321 | * will always try all possable matches. If you don't like | ||
322 | * that, feel free to fix it. | ||
323 | */ | ||
324 | |||
325 | /* Make a local copy of the string -- up | ||
326 | * to the the position of the cursor */ | ||
327 | matchBuf = (char *) calloc(BUFSIZ, sizeof(char)); | ||
328 | strncpy(matchBuf, parsenextc, cursor); | ||
329 | tmp=matchBuf; | ||
330 | |||
331 | /* skip past any command seperator tokens */ | ||
332 | while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) { | ||
333 | tmp=++tmp1; | ||
334 | /* skip any leading white space */ | ||
335 | while (*tmp && isspace(*tmp)) | ||
336 | ++tmp; | ||
337 | } | ||
338 | |||
339 | /* skip any leading white space */ | ||
340 | while (*tmp && isspace(*tmp)) | ||
341 | ++tmp; | ||
342 | |||
343 | /* Free up any memory already allocated */ | ||
344 | if (matches) { | ||
345 | free(matches); | ||
346 | matches = (char **) NULL; | ||
347 | } | ||
348 | |||
349 | /* If the word starts in `~', and there is no slash in the word, | ||
350 | * then try completing this word as a username. */ | ||
351 | if (*tmp == '~' && !strchr(tmp, '/')) | ||
352 | matches = username_completion_matches(tmp, &num_matches); | ||
353 | |||
354 | /* Try to match any executable in our patch, and everything | ||
355 | * in the current working directory that matches. | ||
356 | */ | ||
357 | if (!matches) | ||
358 | matches = find_path_executable_n_cwd_matches(tmp, &num_matches); | ||
359 | } else { | ||
360 | if ( matches && num_matches>0 ) { | ||
361 | int i, col; | ||
362 | |||
363 | fprintf(stderr, "\nTabbing...\n"); | ||
364 | |||
365 | /* Make a list of the matches */ | ||
366 | col += xwrite(outputFd, "\n", 1); | ||
367 | for (i=0,col=0; i<num_matches; i++) { | ||
368 | col += xwrite(outputFd, prompt, strlen(matches[i])); | ||
369 | if (col > 60 && matches[i+1] != NULL) { | ||
370 | xwrite(outputFd, "\n", 1); | ||
371 | col = 0; | ||
372 | } | ||
373 | } | ||
374 | xwrite(outputFd, "\n", 1); | ||
375 | |||
376 | len+=strlen(prompt); | ||
377 | fprintf(stderr, "len=%d\n", len); | ||
378 | |||
379 | /* Move to the beginning of the line */ | ||
380 | input_home(outputFd, &len); | ||
381 | |||
382 | /* erase everything */ | ||
383 | for (j = 0; j < len; j++) | ||
384 | xwrite(outputFd, " ", 1); | ||
385 | |||
386 | /* return to begining of line */ | ||
387 | input_home(outputFd, &cursor); | ||
388 | |||
389 | /* Rewrite the prompt) */ | ||
390 | xwrite(outputFd, prompt, strlen(prompt)); | ||
391 | |||
392 | /* Rewrite the command */ | ||
393 | len = strlen(parsenextc); | ||
394 | xwrite(outputFd, parsenextc, len); | ||
395 | |||
396 | /* Move back to where the cursor used to be */ | ||
397 | for (cursor=pos; cursor > 0; cursor--) | ||
398 | xwrite(outputFd, "\b", 1); | ||
399 | cursor = pos; | ||
400 | |||
401 | //fprintf(stderr, "\nprompt='%s'\n", prompt); | ||
402 | } | ||
403 | } | ||
404 | break; | ||
405 | } | ||
406 | case '\b': | ||
407 | case DEL: | ||
408 | /* Backspace */ | ||
409 | input_backspace(outputFd, &cursor, &len); | ||
410 | break; | ||
411 | case '\n': | ||
412 | /* Enter */ | ||
413 | *(parsenextc + len++ + 1) = c; | ||
414 | xwrite(outputFd, &c, 1); | ||
415 | break_out = 1; | ||
416 | break; | ||
417 | case ESC:{ | ||
418 | /* escape sequence follows */ | ||
419 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
420 | return ret; | ||
421 | |||
422 | if (c == '[') { /* 91 */ | ||
423 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
424 | return ret; | ||
425 | |||
426 | switch (c) { | ||
427 | case 'A': | ||
428 | /* Up Arrow -- Get previous command */ | ||
429 | if (hp && hp->p) { | ||
430 | free(hp->s); | ||
431 | hp->s = strdup(parsenextc); | ||
432 | hp = hp->p; | ||
433 | goto hop; | ||
434 | } | ||
435 | break; | ||
436 | case 'B': | ||
437 | /* Down Arrow -- Get next command */ | ||
438 | if (hp && hp->n && hp->n->s) { | ||
439 | free(hp->s); | ||
440 | hp->s = strdup(parsenextc); | ||
441 | hp = hp->n; | ||
442 | goto hop; | ||
443 | } | ||
444 | break; | ||
445 | |||
446 | /* This is where we rewrite the line | ||
447 | * using the selected history item */ | ||
448 | hop: | ||
449 | len = strlen(parsenextc); | ||
450 | |||
451 | /* return to begining of line */ | ||
452 | for (; cursor > 0; cursor--) | ||
453 | xwrite(outputFd, "\b", 1); | ||
454 | xwrite(outputFd, parsenextc, len); | ||
455 | |||
456 | /* erase old command */ | ||
457 | for (j = 0; j < len; j++) | ||
458 | xwrite(outputFd, " ", 1); | ||
459 | |||
460 | /* return to begining of line */ | ||
461 | for (j = len; j > 0; j--) | ||
462 | xwrite(outputFd, "\b", 1); | ||
463 | |||
464 | memset(parsenextc, 0, BUFSIZ); | ||
465 | /* write new command */ | ||
466 | strcpy(parsenextc, hp->s); | ||
467 | len = strlen(hp->s); | ||
468 | xwrite(outputFd, parsenextc, len); | ||
469 | cursor = len; | ||
470 | break; | ||
471 | case 'C': | ||
472 | /* Right Arrow -- Move forward one character */ | ||
473 | if (cursor < len) { | ||
474 | xwrite(outputFd, "\033[C", 3); | ||
475 | cursor++; | ||
476 | } | ||
477 | break; | ||
478 | case 'D': | ||
479 | /* Left Arrow -- Move back one character */ | ||
480 | if (cursor > 0) { | ||
481 | xwrite(outputFd, "\033[D", 3); | ||
482 | cursor--; | ||
483 | } | ||
484 | break; | ||
485 | case '3': | ||
486 | /* Delete */ | ||
487 | if (cursor != len) { | ||
488 | input_delete(outputFd, cursor); | ||
489 | len--; | ||
490 | } | ||
491 | break; | ||
492 | case '1': | ||
493 | /* Home (Ctrl-A) */ | ||
494 | input_home(outputFd, &cursor); | ||
495 | break; | ||
496 | case '4': | ||
497 | /* End (Ctrl-E) */ | ||
498 | input_end(outputFd, &cursor, len); | ||
499 | break; | ||
500 | } | ||
501 | if (c == '1' || c == '3' || c == '4') | ||
502 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
503 | return ret; /* read 126 (~) */ | ||
504 | } | ||
505 | if (c == 'O') { | ||
506 | /* 79 */ | ||
507 | if ((ret = read(inputFd, &c, 1)) < 1) | ||
508 | return ret; | ||
509 | switch (c) { | ||
510 | case 'H': | ||
511 | /* Home (xterm) */ | ||
512 | input_home(outputFd, &cursor); | ||
513 | break; | ||
514 | case 'F': | ||
515 | /* End (xterm) */ | ||
516 | input_end(outputFd, &cursor, len); | ||
517 | break; | ||
518 | } | ||
519 | } | ||
520 | c = 0; | ||
521 | break; | ||
522 | } | ||
523 | |||
524 | default: /* If it's regular input, do the normal thing */ | ||
525 | |||
526 | if (!isprint(c)) /* Skip non-printable characters */ | ||
527 | break; | ||
528 | |||
529 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ | ||
530 | break; | ||
531 | |||
532 | len++; | ||
533 | |||
534 | if (cursor == (len - 1)) { /* Append if at the end of the line */ | ||
535 | *(parsenextc + cursor) = c; | ||
536 | } else { /* Insert otherwise */ | ||
537 | memmove(parsenextc + cursor + 1, parsenextc + cursor, | ||
538 | len - cursor - 1); | ||
539 | |||
540 | *(parsenextc + cursor) = c; | ||
541 | |||
542 | for (j = cursor; j < len; j++) | ||
543 | xwrite(outputFd, parsenextc + j, 1); | ||
544 | for (; j > cursor; j--) | ||
545 | xwrite(outputFd, "\033[D", 3); | ||
546 | } | ||
504 | 547 | ||
505 | default: /* If it's regular input, do the normal thing */ | 548 | cursor++; |
549 | xwrite(outputFd, &c, 1); | ||
550 | break; | ||
551 | } | ||
552 | if (c == '\t') | ||
553 | lastWasTab = TRUE; | ||
554 | else | ||
555 | lastWasTab = FALSE; | ||
506 | 556 | ||
507 | if (!isprint(c)) /* Skip non-printable characters */ | 557 | if (break_out) /* Enter is the command terminator, no more input. */ |
508 | break; | 558 | break; |
559 | } | ||
509 | 560 | ||
510 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ | 561 | nr = len + 1; |
511 | break; | 562 | xioctl(inputFd, TCSETA, (void *) &old_term); |
563 | reset_term = 0; | ||
512 | 564 | ||
513 | len++; | ||
514 | 565 | ||
515 | if (cursor == (len - 1)) { /* Append if at the end of the line */ | 566 | /* Handle command history log */ |
516 | *(parsenextc + cursor) = c; | 567 | if (*(parsenextc)) { |
517 | } else { /* Insert otherwise */ | ||
518 | memmove(parsenextc + cursor + 1, parsenextc + cursor, | ||
519 | len - cursor - 1); | ||
520 | 568 | ||
521 | *(parsenextc + cursor) = c; | 569 | struct history *h = his_end; |
522 | 570 | ||
523 | for (j = cursor; j < len; j++) | 571 | if (!h) { |
524 | xwrite(outputFd, parsenextc + j, 1); | 572 | /* No previous history */ |
525 | for (; j > cursor; j--) | 573 | h = his_front = malloc(sizeof(struct history)); |
526 | xwrite(outputFd, "\033[D", 3); | 574 | h->n = malloc(sizeof(struct history)); |
527 | } | ||
528 | 575 | ||
529 | cursor++; | 576 | h->p = NULL; |
530 | xwrite(outputFd, &c, 1); | 577 | h->s = strdup(parsenextc); |
531 | break; | 578 | h->n->p = h; |
532 | } | 579 | h->n->n = NULL; |
533 | if (c=='\t') | 580 | h->n->s = NULL; |
534 | lastWasTab = TRUE; | 581 | his_end = h->n; |
535 | else | 582 | history_counter++; |
536 | lastWasTab = FALSE; | 583 | } else { |
537 | 584 | /* Add a new history command */ | |
538 | if (break_out) /* Enter is the command terminator, no more input. */ | 585 | h->n = malloc(sizeof(struct history)); |
539 | break; | 586 | |
540 | } | 587 | h->n->p = h; |
541 | 588 | h->n->n = NULL; | |
542 | nr = len + 1; | 589 | h->n->s = NULL; |
543 | xioctl(inputFd, TCSETA, (void *) &old_term); | 590 | h->s = strdup(parsenextc); |
544 | reset_term = 0; | 591 | his_end = h->n; |
545 | 592 | ||
546 | 593 | /* After max history, remove the oldest command */ | |
547 | /* Handle command history log */ | 594 | if (history_counter >= MAX_HISTORY) { |
548 | if (*(parsenextc)) { | 595 | |
549 | 596 | struct history *p = his_front->n; | |
550 | struct history *h = his_end; | 597 | |
551 | 598 | p->p = NULL; | |
552 | if (!h) { | 599 | free(his_front->s); |
553 | /* No previous history */ | 600 | free(his_front); |
554 | h = his_front = malloc(sizeof(struct history)); | 601 | his_front = p; |
555 | h->n = malloc(sizeof(struct history)); | 602 | } else { |
556 | h->p = NULL; | 603 | history_counter++; |
557 | h->s = strdup(parsenextc); | 604 | } |
558 | h->n->p = h; | 605 | } |
559 | h->n->n = NULL; | ||
560 | h->n->s = NULL; | ||
561 | his_end = h->n; | ||
562 | history_counter++; | ||
563 | } else { | ||
564 | /* Add a new history command */ | ||
565 | h->n = malloc(sizeof(struct history)); | ||
566 | h->n->p = h; | ||
567 | h->n->n = NULL; | ||
568 | h->n->s = NULL; | ||
569 | h->s = strdup(parsenextc); | ||
570 | his_end = h->n; | ||
571 | |||
572 | /* After max history, remove the oldest command */ | ||
573 | if (history_counter >= MAX_HISTORY) { | ||
574 | |||
575 | struct history *p = his_front->n; | ||
576 | |||
577 | p->p = NULL; | ||
578 | free(his_front->s); | ||
579 | free(his_front); | ||
580 | his_front = p; | ||
581 | } else { | ||
582 | history_counter++; | ||
583 | } | ||
584 | } | 606 | } |
585 | } | ||
586 | 607 | ||
587 | return nr; | 608 | return nr; |
588 | } | 609 | } |
589 | 610 | ||
590 | extern void cmdedit_init(void) | 611 | extern void cmdedit_init(void) |
591 | { | 612 | { |
592 | atexit(cmdedit_reset_term); | 613 | atexit(cmdedit_reset_term); |
593 | signal(SIGINT, prepareToDie); | 614 | signal(SIGINT, prepareToDie); |
594 | signal(SIGQUIT, prepareToDie); | 615 | signal(SIGQUIT, prepareToDie); |
595 | signal(SIGTERM, prepareToDie); | 616 | signal(SIGTERM, prepareToDie); |
596 | } | 617 | } |
597 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ | 618 | #endif /* BB_FEATURE_SH_COMMAND_EDITING */ |
diff --git a/shell/cmdedit.h b/shell/cmdedit.h index e776543d6..843a74070 100644 --- a/shell/cmdedit.h +++ b/shell/cmdedit.h | |||
@@ -12,6 +12,6 @@ | |||
12 | * | 12 | * |
13 | */ | 13 | */ |
14 | 14 | ||
15 | extern int cmdedit_read_input(int inputFd, int outputFd, char command[BUFSIZ]); | 15 | extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]); |
16 | extern void cmdedit_init(void); | 16 | extern void cmdedit_init(void); |
17 | 17 | ||
diff --git a/shell/lash.c b/shell/lash.c index 068159697..8c1503ff4 100644 --- a/shell/lash.c +++ b/shell/lash.c | |||
@@ -39,10 +39,6 @@ | |||
39 | #include <unistd.h> | 39 | #include <unistd.h> |
40 | 40 | ||
41 | 41 | ||
42 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
43 | #include "cmdedit.h" | ||
44 | #endif | ||
45 | |||
46 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | 42 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" |
47 | 43 | ||
48 | 44 | ||
@@ -123,8 +119,7 @@ static struct builtInCommand bltins[] = { | |||
123 | {"export", "Set environment variable", "export [VAR=value]", shell_export}, | 119 | {"export", "Set environment variable", "export [VAR=value]", shell_export}, |
124 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, | 120 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, |
125 | 121 | ||
126 | {".", "Source-in and run commands in a file", ". filename", | 122 | {".", "Source-in and run commands in a file", ". filename", shell_source}, |
127 | shell_source}, | ||
128 | {"help", "List shell built-in commands", "help", shell_help}, | 123 | {"help", "List shell built-in commands", "help", shell_help}, |
129 | {NULL, NULL, NULL, NULL} | 124 | {NULL, NULL, NULL, NULL} |
130 | }; | 125 | }; |
@@ -396,11 +391,19 @@ static void checkJobs(struct jobSet *jobList) | |||
396 | static int getCommand(FILE * source, char *command) | 391 | static int getCommand(FILE * source, char *command) |
397 | { | 392 | { |
398 | if (source == stdin) { | 393 | if (source == stdin) { |
399 | fprintf(stdout, "BBSHELL %s %s", cwd, prompt); | ||
400 | fflush(stdout); | ||
401 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | 394 | #ifdef BB_FEATURE_SH_COMMAND_EDITING |
402 | cmdedit_read_input(fileno(stdin), fileno(stdout), command); | 395 | int len; |
396 | char *promptStr; | ||
397 | len=fprintf(stdout, "BBSHELL %s %s", cwd, prompt); | ||
398 | fflush(stdout); | ||
399 | promptStr=(char*)malloc(sizeof(char)*(len+1)); | ||
400 | sprintf(promptStr, "BBSHELL %s %s", cwd, prompt); | ||
401 | cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); | ||
402 | free( promptStr); | ||
403 | return 0; | 403 | return 0; |
404 | #else | ||
405 | fprintf(stdout, "%s %s", cwd, prompt); | ||
406 | fflush(stdout); | ||
404 | #endif | 407 | #endif |
405 | } | 408 | } |
406 | 409 | ||