aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Andersen <andersen@codepoet.org>2000-03-19 05:28:55 +0000
committerErik Andersen <andersen@codepoet.org>2000-03-19 05:28:55 +0000
commitc7c634bd88c57d910c6089de7f0091ca4e3d1843 (patch)
treedf2b1c18c27729603880dd797ee6bd51d192d9cd
parent6c41c448982827ef5bfac9bac14bc9e509f45bc4 (diff)
downloadbusybox-w32-c7c634bd88c57d910c6089de7f0091ca4e3d1843.tar.gz
busybox-w32-c7c634bd88c57d910c6089de7f0091ca4e3d1843.tar.bz2
busybox-w32-c7c634bd88c57d910c6089de7f0091ca4e3d1843.zip
Some more stuff.
-Erik
-rw-r--r--cmdedit.c919
-rw-r--r--cmdedit.h2
-rw-r--r--internal.h6
-rw-r--r--lash.c21
-rw-r--r--sh.c21
-rw-r--r--shell/cmdedit.c919
-rw-r--r--shell/cmdedit.h2
-rw-r--r--shell/lash.c21
8 files changed, 984 insertions, 927 deletions
diff --git a/cmdedit.c b/cmdedit.c
index 9e162c6ee..8a7a5fb03 100644
--- a/cmdedit.c
+++ b/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 */
55static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */ 54static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */
56 55
57static int history_counter = 0; /* Number of commands in history list */ 56static int history_counter = 0; /* Number of commands in history list */
58static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ 57static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */
59char *parsenextc; /* copy of parsefile->nextc */ 58char *parsenextc; /* copy of parsefile->nextc */
60 59
61struct history { 60struct 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. */
69int xwrite(int fd, char *buf, int nbytes) 68int 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. */
95int xioctl(int fd, unsigned long request, char *arg) 94int 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
103void cmdedit_reset_term(void) 103void 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
109void prepareToDie(int sig) 109void 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
116void input_home(int outputFd, int *cursor) 116void 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
125void input_delete(int outputFd, int cursor) 125void 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
145void input_end(int outputFd, int *cursor, int len) 145void 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
154void input_backspace(int outputFd, int *cursor, int *len) 154void 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
180char **username_completion_matches( char* matchBuf) 180char** 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}
185char **command_completion_matches( char* matchBuf) 187char** 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
190char **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 */
214extern int cmdedit_read_input(int inputFd, int outputFd, 221extern 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 // }
336printf("\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 }
360printf("\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
590extern void cmdedit_init(void) 611extern 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/cmdedit.h b/cmdedit.h
index e776543d6..843a74070 100644
--- a/cmdedit.h
+++ b/cmdedit.h
@@ -12,6 +12,6 @@
12 * 12 *
13 */ 13 */
14 14
15extern int cmdedit_read_input(int inputFd, int outputFd, char command[BUFSIZ]); 15extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]);
16extern void cmdedit_init(void); 16extern 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);
216extern long getNum (const char *cp); 216extern long getNum (const char *cp);
217extern pid_t findPidByName( char* pidName); 217extern pid_t findPidByName( char* pidName);
218extern void *xmalloc (size_t size); 218extern void *xmalloc (size_t size);
219#ifdef BB_FEATURE_SH_COMMAND_EDITING
220#include <stdio.h>
221extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]);
222extern void cmdedit_init(void);
223#endif
224
219#if defined BB_INIT || defined BB_SYSLOGD 225#if defined BB_INIT || defined BB_SYSLOGD
220extern int device_open(char *device, int mode); 226extern int device_open(char *device, int mode);
221#endif 227#endif
diff --git a/lash.c b/lash.c
index 068159697..8c1503ff4 100644
--- a/lash.c
+++ b/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)
396static int getCommand(FILE * source, char *command) 391static 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/sh.c b/sh.c
index 068159697..8c1503ff4 100644
--- a/sh.c
+++ b/sh.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)
396static int getCommand(FILE * source, char *command) 391static 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 */
55static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */ 54static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */
56 55
57static int history_counter = 0; /* Number of commands in history list */ 56static int history_counter = 0; /* Number of commands in history list */
58static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ 57static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */
59char *parsenextc; /* copy of parsefile->nextc */ 58char *parsenextc; /* copy of parsefile->nextc */
60 59
61struct history { 60struct 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. */
69int xwrite(int fd, char *buf, int nbytes) 68int 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. */
95int xioctl(int fd, unsigned long request, char *arg) 94int 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
103void cmdedit_reset_term(void) 103void 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
109void prepareToDie(int sig) 109void 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
116void input_home(int outputFd, int *cursor) 116void 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
125void input_delete(int outputFd, int cursor) 125void 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
145void input_end(int outputFd, int *cursor, int len) 145void 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
154void input_backspace(int outputFd, int *cursor, int *len) 154void 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
180char **username_completion_matches( char* matchBuf) 180char** 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}
185char **command_completion_matches( char* matchBuf) 187char** 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
190char **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 */
214extern int cmdedit_read_input(int inputFd, int outputFd, 221extern 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 // }
336printf("\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 }
360printf("\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
590extern void cmdedit_init(void) 611extern 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
15extern int cmdedit_read_input(int inputFd, int outputFd, char command[BUFSIZ]); 15extern int cmdedit_read_input(char* prompt, int inputFd, int outputFd, char command[BUFSIZ]);
16extern void cmdedit_init(void); 16extern 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)
396static int getCommand(FILE * source, char *command) 391static 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