diff options
| author | Eric Andersen <andersen@codepoet.org> | 2001-06-21 16:38:11 +0000 |
|---|---|---|
| committer | Eric Andersen <andersen@codepoet.org> | 2001-06-21 16:38:11 +0000 |
| commit | 8a646dd2933bc37e67e1b09fd3461816e38fc677 (patch) | |
| tree | f2e436f413c07809fe5b8f21c3eb5b86e309ee11 /shell | |
| parent | ea4abff59530709ae423482220f65e37ecd1ac24 (diff) | |
| download | busybox-w32-8a646dd2933bc37e67e1b09fd3461816e38fc677.tar.gz busybox-w32-8a646dd2933bc37e67e1b09fd3461816e38fc677.tar.bz2 busybox-w32-8a646dd2933bc37e67e1b09fd3461816e38fc677.zip | |
This commit guts lash, restoring it to what it was originally intended to do,
just be a simple command line interpreter with basic pipe, redirect, and job
control. For all the more fancy things, people should use hush or ash.
-Erik
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/lash.c | 614 |
1 files changed, 32 insertions, 582 deletions
diff --git a/shell/lash.c b/shell/lash.c index 3a421b372..689c720f6 100644 --- a/shell/lash.c +++ b/shell/lash.c | |||
| @@ -25,30 +25,10 @@ | |||
| 25 | * | 25 | * |
| 26 | */ | 26 | */ |
| 27 | 27 | ||
| 28 | /* The parsing engine of this program is officially at a dead-end. | 28 | /* This shell's parsing engine is officially at a dead-end. |
| 29 | * Future work in that direction should move to the work posted | 29 | * Future work shell work should be done using hush.c |
| 30 | * at http://doolittle.faludi.com/~larry/parser.html . | ||
| 31 | * A start on the integration of that work with the rest of sh.c | ||
| 32 | * is at http://codepoet.org/sh.c . | ||
| 33 | */ | 30 | */ |
| 34 | // | 31 | |
| 35 | //This works pretty well now, and is now on by default. | ||
| 36 | #define BB_FEATURE_SH_ENVIRONMENT | ||
| 37 | // | ||
| 38 | //Backtick support has some problems, use at your own risk! | ||
| 39 | //#define BB_FEATURE_SH_BACKTICKS | ||
| 40 | // | ||
| 41 | //If, then, else, etc. support.. This should now behave basically | ||
| 42 | //like any other Bourne shell -- sortof... | ||
| 43 | #define BB_FEATURE_SH_IF_EXPRESSIONS | ||
| 44 | // | ||
| 45 | /* This is currently sortof broken, only for the brave... */ | ||
| 46 | #undef HANDLE_CONTINUATION_CHARS | ||
| 47 | // | ||
| 48 | /* This would be great -- if wordexp wouldn't strip all quoting | ||
| 49 | * out from the target strings... As is, a parser needs */ | ||
| 50 | #undef BB_FEATURE_SH_WORDEXP | ||
| 51 | // | ||
| 52 | //For debugging/development on the shell only... | 32 | //For debugging/development on the shell only... |
| 53 | //#define DEBUG_SHELL | 33 | //#define DEBUG_SHELL |
| 54 | 34 | ||
| @@ -71,16 +51,8 @@ | |||
| 71 | #include <locale.h> | 51 | #include <locale.h> |
| 72 | #endif | 52 | #endif |
| 73 | 53 | ||
| 74 | //#define BB_FEATURE_SH_WORDEXP | ||
| 75 | |||
| 76 | #ifdef BB_FEATURE_SH_WORDEXP | ||
| 77 | #include <wordexp.h> | ||
| 78 | #define expand_t wordexp_t | ||
| 79 | #undef BB_FEATURE_SH_BACKTICKS | ||
| 80 | #else | ||
| 81 | #include <glob.h> | 54 | #include <glob.h> |
| 82 | #define expand_t glob_t | 55 | #define expand_t glob_t |
| 83 | #endif | ||
| 84 | 56 | ||
| 85 | 57 | ||
| 86 | static const int MAX_READ = 128; /* size of input buffer for `read' builtin */ | 58 | static const int MAX_READ = 128; /* size of input buffer for `read' builtin */ |
| @@ -155,14 +127,6 @@ static int builtin_export(struct child_prog *cmd); | |||
| 155 | static int builtin_source(struct child_prog *cmd); | 127 | static int builtin_source(struct child_prog *cmd); |
| 156 | static int builtin_unset(struct child_prog *cmd); | 128 | static int builtin_unset(struct child_prog *cmd); |
| 157 | static int builtin_read(struct child_prog *cmd); | 129 | static int builtin_read(struct child_prog *cmd); |
| 158 | #ifdef BB_FEATURE_SH_IF_EXPRESSIONS | ||
| 159 | static int builtin_if(struct child_prog *cmd); | ||
| 160 | static int builtin_then(struct child_prog *cmd); | ||
| 161 | static int builtin_else(struct child_prog *cmd); | ||
| 162 | static int builtin_fi(struct child_prog *cmd); | ||
| 163 | /* function prototypes for shell stuff */ | ||
| 164 | static int run_command_predicate(char *cmd); | ||
| 165 | #endif | ||
| 166 | 130 | ||
| 167 | 131 | ||
| 168 | /* function prototypes for shell stuff */ | 132 | /* function prototypes for shell stuff */ |
| @@ -192,12 +156,6 @@ static struct built_in_command bltins[] = { | |||
| 192 | {"read", "Input environment variable", builtin_read}, | 156 | {"read", "Input environment variable", builtin_read}, |
| 193 | {".", "Source-in and run commands in a file", builtin_source}, | 157 | {".", "Source-in and run commands in a file", builtin_source}, |
| 194 | /* to do: add ulimit */ | 158 | /* to do: add ulimit */ |
| 195 | #ifdef BB_FEATURE_SH_IF_EXPRESSIONS | ||
| 196 | {"if", NULL, builtin_if}, | ||
| 197 | {"then", NULL, builtin_then}, | ||
| 198 | {"else", NULL, builtin_else}, | ||
| 199 | {"fi", NULL, builtin_fi}, | ||
| 200 | #endif | ||
| 201 | {NULL, NULL, NULL} | 159 | {NULL, NULL, NULL} |
| 202 | }; | 160 | }; |
| 203 | 161 | ||
| @@ -222,14 +180,7 @@ static struct jobset job_list = { NULL, NULL }; | |||
| 222 | static int argc; | 180 | static int argc; |
| 223 | static char **argv; | 181 | static char **argv; |
| 224 | static struct close_me *close_me_head; | 182 | static struct close_me *close_me_head; |
| 225 | #ifdef BB_FEATURE_SH_ENVIRONMENT | 183 | static unsigned int last_jobid; |
| 226 | static int last_bg_pid; | ||
| 227 | static int last_return_code; | ||
| 228 | static int show_x_trace; | ||
| 229 | #endif | ||
| 230 | #ifdef BB_FEATURE_SH_IF_EXPRESSIONS | ||
| 231 | static char syntax_err[]="syntax error near unexpected token"; | ||
| 232 | #endif | ||
| 233 | 184 | ||
| 234 | static char *PS1; | 185 | static char *PS1; |
| 235 | static char *PS2 = "> "; | 186 | static char *PS2 = "> "; |
| @@ -266,14 +217,6 @@ export cmd->progs[0] | |||
| 266 | source cmd->progs[0] | 217 | source cmd->progs[0] |
| 267 | unset cmd->progs[0] | 218 | unset cmd->progs[0] |
| 268 | read cmd->progs[0] | 219 | read cmd->progs[0] |
| 269 | if cmd->job_context, cmd->text | ||
| 270 | then cmd->job_context, cmd->text | ||
| 271 | else cmd->job_context, cmd->text | ||
| 272 | fi cmd->job_context | ||
| 273 | |||
| 274 | The use of cmd->text by if/then/else/fi is hopelessly hacky. | ||
| 275 | Would it work to increment cmd->progs[0]->argv and recurse, | ||
| 276 | somewhat like builtin_exec does? | ||
| 277 | 220 | ||
| 278 | I added "struct job *family;" to struct child_prog, | 221 | I added "struct job *family;" to struct child_prog, |
| 279 | and switched API to builtin_foo(struct child_prog *child); | 222 | and switched API to builtin_foo(struct child_prog *child); |
| @@ -325,31 +268,34 @@ static int builtin_exit(struct child_prog *child) | |||
| 325 | /* built-in 'fg' and 'bg' handler */ | 268 | /* built-in 'fg' and 'bg' handler */ |
| 326 | static int builtin_fg_bg(struct child_prog *child) | 269 | static int builtin_fg_bg(struct child_prog *child) |
| 327 | { | 270 | { |
| 328 | int i, jobNum; | 271 | int i, jobnum; |
| 329 | struct job *job=NULL; | 272 | struct job *job=NULL; |
| 330 | |||
| 331 | if (!child->argv[1] || child->argv[2]) { | ||
| 332 | error_msg("%s: exactly one argument is expected", | ||
| 333 | child->argv[0]); | ||
| 334 | return EXIT_FAILURE; | ||
| 335 | } | ||
| 336 | |||
| 337 | if (sscanf(child->argv[1], "%%%d", &jobNum) != 1) { | ||
| 338 | error_msg("%s: bad argument '%s'", | ||
| 339 | child->argv[0], child->argv[1]); | ||
| 340 | return EXIT_FAILURE; | ||
| 341 | } | ||
| 342 | 273 | ||
| 343 | for (job = child->family->job_list->head; job; job = job->next) { | 274 | /* If they gave us no args, assume they want the last backgrounded task */ |
| 344 | if (job->jobid == jobNum) { | 275 | if (!child->argv[1]) { |
| 345 | break; | 276 | for (job = child->family->job_list->head; job; job = job->next) { |
| 277 | if (job->jobid == last_jobid) { | ||
| 278 | break; | ||
| 279 | } | ||
| 280 | } | ||
| 281 | if (!job) { | ||
| 282 | error_msg("%s: no current job", child->argv[0]); | ||
| 283 | return EXIT_FAILURE; | ||
| 284 | } | ||
| 285 | } else { | ||
| 286 | if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { | ||
| 287 | error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]); | ||
| 288 | return EXIT_FAILURE; | ||
| 289 | } | ||
| 290 | for (job = child->family->job_list->head; job; job = job->next) { | ||
| 291 | if (job->jobid == jobnum) { | ||
| 292 | break; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | if (!job) { | ||
| 296 | error_msg("%s: %d: no such job", child->argv[0], jobnum); | ||
| 297 | return EXIT_FAILURE; | ||
| 346 | } | 298 | } |
| 347 | } | ||
| 348 | |||
| 349 | if (!job) { | ||
| 350 | error_msg("%s: unknown job %d", | ||
| 351 | child->argv[0], jobNum); | ||
| 352 | return EXIT_FAILURE; | ||
| 353 | } | 299 | } |
| 354 | 300 | ||
| 355 | if (*child->argv[0] == 'f') { | 301 | if (*child->argv[0] == 'f') { |
| @@ -485,102 +431,6 @@ static int builtin_read(struct child_prog *child) | |||
| 485 | return (res); | 431 | return (res); |
| 486 | } | 432 | } |
| 487 | 433 | ||
| 488 | #ifdef BB_FEATURE_SH_IF_EXPRESSIONS | ||
| 489 | /* Built-in handler for 'if' commands */ | ||
| 490 | static int builtin_if(struct child_prog *child) | ||
| 491 | { | ||
| 492 | struct job *cmd = child->family; | ||
| 493 | int status; | ||
| 494 | char* charptr1=cmd->text+3; /* skip over the leading 'if ' */ | ||
| 495 | |||
| 496 | /* Now run the 'if' command */ | ||
| 497 | debug_printf( "job=%p entering builtin_if ('%s')-- context=%d\n", cmd, charptr1, cmd->job_context); | ||
| 498 | status = run_command_predicate(charptr1); | ||
| 499 | debug_printf( "if test returned "); | ||
| 500 | if (status == 0) { | ||
| 501 | debug_printf( "TRUE\n"); | ||
| 502 | cmd->job_context |= IF_TRUE_CONTEXT; | ||
| 503 | } else { | ||
| 504 | debug_printf( "FALSE\n"); | ||
| 505 | cmd->job_context |= IF_FALSE_CONTEXT; | ||
| 506 | } | ||
| 507 | debug_printf("job=%p builtin_if set job context to %x\n", cmd, cmd->job_context); | ||
| 508 | shell_context++; | ||
| 509 | |||
| 510 | return status; | ||
| 511 | } | ||
| 512 | |||
| 513 | /* Built-in handler for 'then' (part of the 'if' command) */ | ||
| 514 | static int builtin_then(struct child_prog *child) | ||
| 515 | { | ||
| 516 | struct job *cmd = child->family; | ||
| 517 | char* charptr1=cmd->text+5; /* skip over the leading 'then ' */ | ||
| 518 | |||
| 519 | debug_printf( "job=%p entering builtin_then ('%s')-- context=%d\n", cmd, charptr1, cmd->job_context); | ||
| 520 | if (! (cmd->job_context & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) { | ||
| 521 | shell_context = 0; /* Reset the shell's context on an error */ | ||
| 522 | error_msg("%s `then'", syntax_err); | ||
| 523 | return EXIT_FAILURE; | ||
| 524 | } | ||
| 525 | |||
| 526 | cmd->job_context |= THEN_EXP_CONTEXT; | ||
| 527 | debug_printf("job=%p builtin_then set job context to %x\n", cmd, cmd->job_context); | ||
| 528 | |||
| 529 | /* If the if result was FALSE, skip the 'then' stuff */ | ||
| 530 | if (cmd->job_context & IF_FALSE_CONTEXT) { | ||
| 531 | return EXIT_SUCCESS; | ||
| 532 | } | ||
| 533 | |||
| 534 | /* Seems the if result was TRUE, so run the 'then' command */ | ||
| 535 | debug_printf( "'then' now running '%s'\n", charptr1); | ||
| 536 | |||
| 537 | return(run_command_predicate(charptr1)); | ||
| 538 | } | ||
| 539 | |||
| 540 | /* Built-in handler for 'else' (part of the 'if' command) */ | ||
| 541 | static int builtin_else(struct child_prog *child) | ||
| 542 | { | ||
| 543 | struct job *cmd = child->family; | ||
| 544 | char* charptr1=cmd->text+5; /* skip over the leading 'else ' */ | ||
| 545 | |||
| 546 | debug_printf( "job=%p entering builtin_else ('%s')-- context=%d\n", cmd, charptr1, cmd->job_context); | ||
| 547 | |||
| 548 | if (! (cmd->job_context & THEN_EXP_CONTEXT)) { | ||
| 549 | shell_context = 0; /* Reset the shell's context on an error */ | ||
| 550 | error_msg("%s `else'", syntax_err); | ||
| 551 | return EXIT_FAILURE; | ||
| 552 | } | ||
| 553 | /* If the if result was TRUE, skip the 'else' stuff */ | ||
| 554 | if (cmd->job_context & IF_TRUE_CONTEXT) { | ||
| 555 | return EXIT_SUCCESS; | ||
| 556 | } | ||
| 557 | |||
| 558 | cmd->job_context |= ELSE_EXP_CONTEXT; | ||
| 559 | debug_printf("job=%p builtin_else set job context to %x\n", cmd, cmd->job_context); | ||
| 560 | |||
| 561 | /* Now run the 'else' command */ | ||
| 562 | debug_printf( "'else' now running '%s'\n", charptr1); | ||
| 563 | return(run_command_predicate(charptr1)); | ||
| 564 | } | ||
| 565 | |||
| 566 | /* Built-in handler for 'fi' (part of the 'if' command) */ | ||
| 567 | static int builtin_fi(struct child_prog *child) | ||
| 568 | { | ||
| 569 | struct job *cmd = child->family; | ||
| 570 | debug_printf( "job=%p entering builtin_fi ('%s')-- context=%d\n", cmd, "", cmd->job_context); | ||
| 571 | if (! (cmd->job_context & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) { | ||
| 572 | shell_context = 0; /* Reset the shell's context on an error */ | ||
| 573 | error_msg("%s `fi'", syntax_err); | ||
| 574 | return EXIT_FAILURE; | ||
| 575 | } | ||
| 576 | /* Clear out the if and then context bits */ | ||
| 577 | cmd->job_context &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT); | ||
| 578 | debug_printf("job=%p builtin_fi set job context to %x\n", cmd, cmd->job_context); | ||
| 579 | shell_context--; | ||
| 580 | return EXIT_SUCCESS; | ||
| 581 | } | ||
| 582 | #endif | ||
| 583 | |||
| 584 | /* Built-in '.' handler (read-in and execute commands from file) */ | 434 | /* Built-in '.' handler (read-in and execute commands from file) */ |
| 585 | static int builtin_source(struct child_prog *child) | 435 | static int builtin_source(struct child_prog *child) |
| 586 | { | 436 | { |
| @@ -617,23 +467,6 @@ static int builtin_unset(struct child_prog *child) | |||
| 617 | return EXIT_SUCCESS; | 467 | return EXIT_SUCCESS; |
| 618 | } | 468 | } |
| 619 | 469 | ||
| 620 | #ifdef BB_FEATURE_SH_IF_EXPRESSIONS | ||
| 621 | /* currently used by if/then/else. | ||
| 622 | * | ||
| 623 | * Reparsing the command line for this purpose is gross, | ||
| 624 | * incorrect, and fundamentally unfixable; in particular, | ||
| 625 | * think about what happens with command substitution. | ||
| 626 | * We really need to pull out the run, wait, return status | ||
| 627 | * functionality out of busy_loop so we can child->argv++ | ||
| 628 | * and use that, without going back through parse_command. | ||
| 629 | */ | ||
| 630 | static int run_command_predicate(char *cmd) | ||
| 631 | { | ||
| 632 | local_pending_command = xstrdup(cmd); | ||
| 633 | return( busy_loop(NULL)); | ||
| 634 | } | ||
| 635 | #endif | ||
| 636 | |||
| 637 | static void mark_open(int fd) | 470 | static void mark_open(int fd) |
| 638 | { | 471 | { |
| 639 | struct close_me *new = xmalloc(sizeof(struct close_me)); | 472 | struct close_me *new = xmalloc(sizeof(struct close_me)); |
| @@ -733,6 +566,7 @@ static void checkjobs(struct jobset *j_list) | |||
| 733 | 566 | ||
| 734 | if (!job->running_progs) { | 567 | if (!job->running_progs) { |
| 735 | printf(JOB_STATUS_FORMAT, job->jobid, "Done", job->text); | 568 | printf(JOB_STATUS_FORMAT, job->jobid, "Done", job->text); |
| 569 | last_jobid=0; | ||
| 736 | remove_job(j_list, job); | 570 | remove_job(j_list, job); |
| 737 | } | 571 | } |
| 738 | } else { | 572 | } else { |
| @@ -878,73 +712,10 @@ static int get_command(FILE * source, char *command) | |||
| 878 | return 0; | 712 | return 0; |
| 879 | } | 713 | } |
| 880 | 714 | ||
| 881 | #ifdef BB_FEATURE_SH_ENVIRONMENT | ||
| 882 | static char* itoa(register int i) | ||
| 883 | { | ||
| 884 | static char a[7]; /* Max 7 ints */ | ||
| 885 | register char *b = a + sizeof(a) - 1; | ||
| 886 | int sign = (i < 0); | ||
| 887 | |||
| 888 | if (sign) | ||
| 889 | i = -i; | ||
| 890 | *b = 0; | ||
| 891 | do | ||
| 892 | { | ||
| 893 | *--b = '0' + (i % 10); | ||
| 894 | i /= 10; | ||
| 895 | } | ||
| 896 | while (i); | ||
| 897 | if (sign) | ||
| 898 | *--b = '-'; | ||
| 899 | return b; | ||
| 900 | } | ||
| 901 | #endif | ||
| 902 | |||
| 903 | #if defined BB_FEATURE_SH_ENVIRONMENT && ! defined BB_FEATURE_SH_WORDEXP | ||
| 904 | char * strsep_space( char *string, int * ix) | ||
| 905 | { | ||
| 906 | char *token, *begin; | ||
| 907 | |||
| 908 | begin = string; | ||
| 909 | |||
| 910 | /* Short circuit the trivial case */ | ||
| 911 | if ( !string || ! string[*ix]) | ||
| 912 | return NULL; | ||
| 913 | |||
| 914 | /* Find the end of the token. */ | ||
| 915 | while( string && string[*ix] && !isspace(string[*ix]) ) { | ||
| 916 | (*ix)++; | ||
| 917 | } | ||
| 918 | |||
| 919 | /* Find the end of any whitespace trailing behind | ||
| 920 | * the token and let that be part of the token */ | ||
| 921 | while( string && string[*ix] && isspace(string[*ix]) ) { | ||
| 922 | (*ix)++; | ||
| 923 | } | ||
| 924 | |||
| 925 | if (! string && *ix==0) { | ||
| 926 | /* Nothing useful was found */ | ||
| 927 | return NULL; | ||
| 928 | } | ||
| 929 | |||
| 930 | token = xmalloc(*ix+1); | ||
| 931 | token[*ix] = '\0'; | ||
| 932 | strncpy(token, string, *ix); | ||
| 933 | |||
| 934 | return token; | ||
| 935 | } | ||
| 936 | #endif | ||
| 937 | |||
| 938 | 715 | ||
| 939 | static int expand_arguments(char *command) | 716 | static int expand_arguments(char *command) |
| 940 | { | 717 | { |
| 941 | #ifdef BB_FEATURE_SH_ENVIRONMENT | ||
| 942 | expand_t expand_result; | ||
| 943 | char *src, *dst, *var; | ||
| 944 | int ix = 0; | 718 | int ix = 0; |
| 945 | int i=0, length, total_length=0, retval; | ||
| 946 | const char *out_of_space = "out of space during expansion"; | ||
| 947 | #endif | ||
| 948 | 719 | ||
| 949 | /* get rid of the terminating \n */ | 720 | /* get rid of the terminating \n */ |
| 950 | chomp(command); | 721 | chomp(command); |
| @@ -959,194 +730,6 @@ static int expand_arguments(char *command) | |||
| 959 | ix++; | 730 | ix++; |
| 960 | } | 731 | } |
| 961 | 732 | ||
| 962 | #ifdef BB_FEATURE_SH_ENVIRONMENT | ||
| 963 | |||
| 964 | |||
| 965 | #ifdef BB_FEATURE_SH_WORDEXP | ||
| 966 | /* This first part uses wordexp() which is a wonderful C lib | ||
| 967 | * function which expands nearly everything. */ | ||
| 968 | retval = wordexp (command, &expand_result, WRDE_SHOWERR); | ||
| 969 | if (retval == WRDE_NOSPACE) { | ||
| 970 | /* Mem may have been allocated... */ | ||
| 971 | wordfree (&expand_result); | ||
| 972 | error_msg(out_of_space); | ||
| 973 | return FALSE; | ||
| 974 | } | ||
| 975 | if (retval < 0) { | ||
| 976 | /* Some other error. */ | ||
| 977 | error_msg("syntax error"); | ||
| 978 | return FALSE; | ||
| 979 | } | ||
| 980 | |||
| 981 | if (expand_result.we_wordc > 0) { | ||
| 982 | /* Convert from char** (one word per string) to a simple char*, | ||
| 983 | * but don't overflow command which is BUFSIZ in length */ | ||
| 984 | *command = '\0'; | ||
| 985 | while (i < expand_result.we_wordc && total_length < BUFSIZ) { | ||
| 986 | length=strlen(expand_result.we_wordv[i])+1; | ||
| 987 | if (BUFSIZ-total_length-length <= 0) { | ||
| 988 | error_msg(out_of_space); | ||
| 989 | return FALSE; | ||
| 990 | } | ||
| 991 | strcat(command+total_length, expand_result.we_wordv[i++]); | ||
| 992 | strcat(command+total_length, " "); | ||
| 993 | total_length+=length; | ||
| 994 | } | ||
| 995 | wordfree (&expand_result); | ||
| 996 | } | ||
| 997 | #else | ||
| 998 | |||
| 999 | /* Ok. They don't have a recent glibc and they don't have uClibc. Chances | ||
| 1000 | * are about 100% they don't have wordexp(). So instead the best we can do | ||
| 1001 | * is use glob and then fixup environment variables and such ourselves. | ||
| 1002 | * This is better then nothing, but certainly not perfect */ | ||
| 1003 | |||
| 1004 | /* It turns out that glob is very stupid. We have to feed it one word at a | ||
| 1005 | * time since it can't cope with a full string. Here we convert command | ||
| 1006 | * (char*) into cmd (char**, one word per string) */ | ||
| 1007 | { | ||
| 1008 | |||
| 1009 | int flags = GLOB_NOCHECK | ||
| 1010 | #ifdef GLOB_BRACE | ||
| 1011 | | GLOB_BRACE | ||
| 1012 | #endif | ||
| 1013 | #ifdef GLOB_TILDE | ||
| 1014 | | GLOB_TILDE | ||
| 1015 | #endif | ||
| 1016 | ; | ||
| 1017 | char *tmpcmd, *cmd, *cmd_copy; | ||
| 1018 | /* We need a clean copy, so strsep can mess up the copy while | ||
| 1019 | * we write stuff into the original (in a minute) */ | ||
| 1020 | cmd = cmd_copy = strdup(command); | ||
| 1021 | *command = '\0'; | ||
| 1022 | for (ix = 0, tmpcmd = cmd; | ||
| 1023 | (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix=0) { | ||
| 1024 | if (*tmpcmd == '\0') | ||
| 1025 | break; | ||
| 1026 | /* we need to trim() the result for glob! */ | ||
| 1027 | trim(tmpcmd); | ||
| 1028 | retval = glob(tmpcmd, flags, NULL, &expand_result); | ||
| 1029 | free(tmpcmd); /* Free mem allocated by strsep_space */ | ||
| 1030 | if (retval == GLOB_NOSPACE) { | ||
| 1031 | /* Mem may have been allocated... */ | ||
| 1032 | globfree (&expand_result); | ||
| 1033 | error_msg(out_of_space); | ||
| 1034 | return FALSE; | ||
| 1035 | } else if (retval != 0) { | ||
| 1036 | /* Some other error. GLOB_NOMATCH shouldn't | ||
| 1037 | * happen because of the GLOB_NOCHECK flag in | ||
| 1038 | * the glob call. */ | ||
| 1039 | error_msg("syntax error"); | ||
| 1040 | return FALSE; | ||
| 1041 | } else { | ||
| 1042 | /* Convert from char** (one word per string) to a simple char*, | ||
| 1043 | * but don't overflow command which is BUFSIZ in length */ | ||
| 1044 | for (i=0; i < expand_result.gl_pathc; i++) { | ||
| 1045 | length=strlen(expand_result.gl_pathv[i]); | ||
| 1046 | if (total_length+length+1 >= BUFSIZ) { | ||
| 1047 | error_msg(out_of_space); | ||
| 1048 | return FALSE; | ||
| 1049 | } | ||
| 1050 | strcat(command+total_length, " "); | ||
| 1051 | total_length+=1; | ||
| 1052 | strcat(command+total_length, expand_result.gl_pathv[i]); | ||
| 1053 | total_length+=length; | ||
| 1054 | } | ||
| 1055 | globfree (&expand_result); | ||
| 1056 | } | ||
| 1057 | } | ||
| 1058 | free(cmd_copy); | ||
| 1059 | trim(command); | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | #endif | ||
| 1063 | |||
| 1064 | /* Now do the shell variable substitutions which | ||
| 1065 | * wordexp can't do for us, namely $? and $! */ | ||
| 1066 | src = command; | ||
| 1067 | while((dst = strchr(src,'$')) != NULL){ | ||
| 1068 | var = NULL; | ||
| 1069 | switch(*(dst+1)) { | ||
| 1070 | case '?': | ||
| 1071 | var = itoa(last_return_code); | ||
| 1072 | break; | ||
| 1073 | case '!': | ||
| 1074 | if (last_bg_pid==-1) | ||
| 1075 | *(var)='\0'; | ||
| 1076 | else | ||
| 1077 | var = itoa(last_bg_pid); | ||
| 1078 | break; | ||
| 1079 | /* Everything else like $$, $#, $[0-9], etc. should all be | ||
| 1080 | * expanded by wordexp(), so we can in theory skip that stuff | ||
| 1081 | * here, but just to be on the safe side (i.e., since uClibc | ||
| 1082 | * wordexp doesn't do this stuff yet), lets leave it in for | ||
| 1083 | * now. */ | ||
| 1084 | case '$': | ||
| 1085 | var = itoa(getpid()); | ||
| 1086 | break; | ||
| 1087 | case '#': | ||
| 1088 | var = itoa(argc-1); | ||
| 1089 | break; | ||
| 1090 | case '0':case '1':case '2':case '3':case '4': | ||
| 1091 | case '5':case '6':case '7':case '8':case '9': | ||
| 1092 | { | ||
| 1093 | int ixx=*(dst + 1)-48; | ||
| 1094 | if (ixx >= argc) { | ||
| 1095 | var='\0'; | ||
| 1096 | } else { | ||
| 1097 | var = argv[ixx]; | ||
| 1098 | } | ||
| 1099 | } | ||
| 1100 | break; | ||
| 1101 | |||
| 1102 | } | ||
| 1103 | if (var) { | ||
| 1104 | /* a single character construction was found, and | ||
| 1105 | * already handled in the case statement */ | ||
| 1106 | src=dst+2; | ||
| 1107 | } else { | ||
| 1108 | /* Looks like an environment variable */ | ||
| 1109 | char delim_hold; | ||
| 1110 | int num_skip_chars=0; | ||
| 1111 | int dstlen = strlen(dst); | ||
| 1112 | /* Is this a ${foo} type variable? */ | ||
| 1113 | if (dstlen >=2 && *(dst+1) == '{') { | ||
| 1114 | src=strchr(dst+1, '}'); | ||
| 1115 | num_skip_chars=1; | ||
| 1116 | } else { | ||
| 1117 | src=dst+1; | ||
| 1118 | while(isalnum(*src) || *src=='_') src++; | ||
| 1119 | } | ||
| 1120 | if (src == NULL) { | ||
| 1121 | src = dst+dstlen; | ||
| 1122 | } | ||
| 1123 | delim_hold=*src; | ||
| 1124 | *src='\0'; /* temporary */ | ||
| 1125 | var = getenv(dst + 1 + num_skip_chars); | ||
| 1126 | *src=delim_hold; | ||
| 1127 | src += num_skip_chars; | ||
| 1128 | } | ||
| 1129 | if (var == NULL) { | ||
| 1130 | /* Seems we got an un-expandable variable. So delete it. */ | ||
| 1131 | var = ""; | ||
| 1132 | } | ||
| 1133 | { | ||
| 1134 | int subst_len = strlen(var); | ||
| 1135 | int trail_len = strlen(src); | ||
| 1136 | if (dst+subst_len+trail_len >= command+BUFSIZ) { | ||
| 1137 | error_msg(out_of_space); | ||
| 1138 | return FALSE; | ||
| 1139 | } | ||
| 1140 | /* Move stuff to the end of the string to accommodate | ||
| 1141 | * filling the created gap with the new stuff */ | ||
| 1142 | memmove(dst+subst_len, src, trail_len+1); | ||
| 1143 | /* Now copy in the new stuff */ | ||
| 1144 | memcpy(dst, var, subst_len); | ||
| 1145 | src = dst+subst_len; | ||
| 1146 | } | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | #endif | ||
| 1150 | return TRUE; | 733 | return TRUE; |
| 1151 | } | 734 | } |
| 1152 | 735 | ||
| @@ -1357,118 +940,12 @@ static int parse_command(char **command_ptr, struct job *job, int *inbg) | |||
| 1357 | return_command = *command_ptr + (src - *command_ptr) + 1; | 940 | return_command = *command_ptr + (src - *command_ptr) + 1; |
| 1358 | break; | 941 | break; |
| 1359 | 942 | ||
| 1360 | #ifdef BB_FEATURE_SH_BACKTICKS | ||
| 1361 | case '`': | ||
| 1362 | /* Exec a backtick-ed command */ | ||
| 1363 | /* Besides any previous brokenness, I have not | ||
| 1364 | * updated backtick handling for close_me support. | ||
| 1365 | * I don't know if it needs it or not. -- LRD */ | ||
| 1366 | { | ||
| 1367 | char* charptr1=NULL, *charptr2; | ||
| 1368 | char* ptr=NULL; | ||
| 1369 | struct job *newjob; | ||
| 1370 | struct jobset njob_list = { NULL, NULL }; | ||
| 1371 | int pipefd[2]; | ||
| 1372 | int size; | ||
| 1373 | |||
| 1374 | ptr=strchr(++src, '`'); | ||
| 1375 | if (ptr==NULL) { | ||
| 1376 | fprintf(stderr, "Unmatched '`' in command\n"); | ||
| 1377 | free_job(job); | ||
| 1378 | return 1; | ||
| 1379 | } | ||
| 1380 | |||
| 1381 | /* Make some space to hold just the backticked command */ | ||
| 1382 | charptr1 = charptr2 = xmalloc(1+ptr-src); | ||
| 1383 | memcpy(charptr1, src, ptr-src); | ||
| 1384 | charptr1[ptr-src] = '\0'; | ||
| 1385 | newjob = xmalloc(sizeof(struct job)); | ||
| 1386 | newjob->job_list = &njob_list; | ||
| 1387 | /* Now parse and run the backticked command */ | ||
| 1388 | if (!parse_command(&charptr1, newjob, inbg) | ||
| 1389 | && newjob->num_progs) { | ||
| 1390 | pipe(pipefd); | ||
| 1391 | run_command(newjob, 0, pipefd); | ||
| 1392 | } | ||
| 1393 | checkjobs(job->job_list); | ||
| 1394 | free_job(newjob); /* doesn't actually free newjob, | ||
| 1395 | looks like a memory leak */ | ||
| 1396 | free(charptr2); | ||
| 1397 | |||
| 1398 | /* Make a copy of any stuff left over in the command | ||
| 1399 | * line after the second backtick */ | ||
| 1400 | charptr2 = xmalloc(strlen(ptr)+1); | ||
| 1401 | memcpy(charptr2, ptr+1, strlen(ptr)); | ||
| 1402 | |||
| 1403 | |||
| 1404 | /* Copy the output from the backtick-ed command into the | ||
| 1405 | * command line, making extra room as needed */ | ||
| 1406 | --src; | ||
| 1407 | charptr1 = xmalloc(BUFSIZ); | ||
| 1408 | while ( (size=full_read(pipefd[0], charptr1, BUFSIZ-1)) >0) { | ||
| 1409 | int newsize=src - *command_ptr + size + 1 + strlen(charptr2); | ||
| 1410 | if (newsize > BUFSIZ) { | ||
| 1411 | *command_ptr=xrealloc(*command_ptr, newsize); | ||
| 1412 | } | ||
| 1413 | memcpy(src, charptr1, size); | ||
| 1414 | src+=size; | ||
| 1415 | } | ||
| 1416 | free(charptr1); | ||
| 1417 | close(pipefd[0]); | ||
| 1418 | if (*(src-1)=='\n') | ||
| 1419 | --src; | ||
| 1420 | |||
| 1421 | /* Now paste into the *command_ptr all the stuff | ||
| 1422 | * leftover after the second backtick */ | ||
| 1423 | memcpy(src, charptr2, strlen(charptr2)+1); | ||
| 1424 | free(charptr2); | ||
| 1425 | |||
| 1426 | /* Now recursively call parse_command to deal with the new | ||
| 1427 | * and improved version of the command line with the backtick | ||
| 1428 | * results expanded in place... */ | ||
| 1429 | { | ||
| 1430 | struct jobset *jl=job->job_list; | ||
| 1431 | free_job(job); | ||
| 1432 | job->job_list = jl; | ||
| 1433 | } | ||
| 1434 | return(parse_command(command_ptr, job, inbg)); | ||
| 1435 | } | ||
| 1436 | break; | ||
| 1437 | #endif // BB_FEATURE_SH_BACKTICKS | ||
| 1438 | |||
| 1439 | case '\\': | 943 | case '\\': |
| 1440 | src++; | 944 | src++; |
| 1441 | if (!*src) { | 945 | if (!*src) { |
| 1442 | /* This is currently a little broken... */ | ||
| 1443 | #ifdef HANDLE_CONTINUATION_CHARS | ||
| 1444 | /* They fed us a continuation char, so continue reading stuff | ||
| 1445 | * on the next line, then tack that onto the end of the current | ||
| 1446 | * command */ | ||
| 1447 | char *command; | ||
| 1448 | int newsize; | ||
| 1449 | printf("erik: found a continue char at EOL...\n"); | ||
| 1450 | command = (char *) xcalloc(BUFSIZ, sizeof(char)); | ||
| 1451 | if (get_command(input, command)) { | ||
| 1452 | error_msg("character expected after \\"); | ||
| 1453 | free(command); | ||
| 1454 | free_job(job); | ||
| 1455 | return 1; | ||
| 1456 | } | ||
| 1457 | newsize = strlen(*command_ptr) + strlen(command) + 2; | ||
| 1458 | if (newsize > BUFSIZ) { | ||
| 1459 | printf("erik: doing realloc\n"); | ||
| 1460 | *command_ptr=xrealloc(*command_ptr, newsize); | ||
| 1461 | } | ||
| 1462 | printf("erik: A: *command_ptr='%s'\n", *command_ptr); | ||
| 1463 | memcpy(--src, command, strlen(command)); | ||
| 1464 | printf("erik: B: *command_ptr='%s'\n", *command_ptr); | ||
| 1465 | free(command); | ||
| 1466 | break; | ||
| 1467 | #else | ||
| 1468 | error_msg("character expected after \\"); | 946 | error_msg("character expected after \\"); |
| 1469 | free_job(job); | 947 | free_job(job); |
| 1470 | return 1; | 948 | return 1; |
| 1471 | #endif | ||
| 1472 | } | 949 | } |
| 1473 | if (*src == '*' || *src == '[' || *src == ']' | 950 | if (*src == '*' || *src == '[' || *src == ']' |
| 1474 | || *src == '?') *buf++ = '\\'; | 951 | || *src == '?') *buf++ = '\\'; |
| @@ -1598,9 +1075,7 @@ static void insert_job(struct job *newjob, int inbg) | |||
| 1598 | to the list of backgrounded thejobs and leave it alone */ | 1075 | to the list of backgrounded thejobs and leave it alone */ |
| 1599 | printf("[%d] %d\n", thejob->jobid, | 1076 | printf("[%d] %d\n", thejob->jobid, |
| 1600 | newjob->progs[newjob->num_progs - 1].pid); | 1077 | newjob->progs[newjob->num_progs - 1].pid); |
| 1601 | #ifdef BB_FEATURE_SH_ENVIRONMENT | 1078 | last_jobid = newjob->jobid; |
| 1602 | last_bg_pid=newjob->progs[newjob->num_progs - 1].pid; | ||
| 1603 | #endif | ||
| 1604 | } else { | 1079 | } else { |
| 1605 | newjob->job_list->fg = thejob; | 1080 | newjob->job_list->fg = thejob; |
| 1606 | 1081 | ||
| @@ -1635,17 +1110,6 @@ static int run_command(struct job *newjob, int inbg, int outpipe[2]) | |||
| 1635 | } | 1110 | } |
| 1636 | } | 1111 | } |
| 1637 | 1112 | ||
| 1638 | #ifdef BB_FEATURE_SH_ENVIRONMENT | ||
| 1639 | if (show_x_trace==TRUE) { | ||
| 1640 | int j; | ||
| 1641 | fputc('+', stderr); | ||
| 1642 | for (j = 0; child->argv[j]; j++) { | ||
| 1643 | fputc(' ', stderr); | ||
| 1644 | fputs(child->argv[j], stderr); | ||
| 1645 | } | ||
| 1646 | fputc('\n', stderr); | ||
| 1647 | } | ||
| 1648 | #endif | ||
| 1649 | 1113 | ||
| 1650 | /* Check if the command matches any non-forking builtins, | 1114 | /* Check if the command matches any non-forking builtins, |
| 1651 | * but only if this is a simple command. | 1115 | * but only if this is a simple command. |
| @@ -1782,11 +1246,6 @@ static int busy_loop(FILE * input) | |||
| 1782 | job_list.fg->running_progs--; | 1246 | job_list.fg->running_progs--; |
| 1783 | job_list.fg->progs[i].pid = 0; | 1247 | job_list.fg->progs[i].pid = 0; |
| 1784 | 1248 | ||
| 1785 | #ifdef BB_FEATURE_SH_ENVIRONMENT | ||
| 1786 | last_return_code=WEXITSTATUS(status); | ||
| 1787 | debug_printf("'%s' exited -- return code %d\n", | ||
| 1788 | job_list.fg->text, last_return_code); | ||
| 1789 | #endif | ||
| 1790 | if (!job_list.fg->running_progs) { | 1249 | if (!job_list.fg->running_progs) { |
| 1791 | /* child exited */ | 1250 | /* child exited */ |
| 1792 | remove_job(&job_list, job_list.fg); | 1251 | remove_job(&job_list, job_list.fg); |
| @@ -1850,16 +1309,12 @@ int shell_main(int argc_l, char **argv_l) | |||
| 1850 | argv = argv_l; | 1309 | argv = argv_l; |
| 1851 | 1310 | ||
| 1852 | /* These variables need re-initializing when recursing */ | 1311 | /* These variables need re-initializing when recursing */ |
| 1312 | last_jobid = 0; | ||
| 1853 | shell_context = 0; | 1313 | shell_context = 0; |
| 1854 | local_pending_command = NULL; | 1314 | local_pending_command = NULL; |
| 1855 | close_me_head = NULL; | 1315 | close_me_head = NULL; |
| 1856 | job_list.head = NULL; | 1316 | job_list.head = NULL; |
| 1857 | job_list.fg = NULL; | 1317 | job_list.fg = NULL; |
| 1858 | #ifdef BB_FEATURE_SH_ENVIRONMENT | ||
| 1859 | last_bg_pid=1; | ||
| 1860 | last_return_code=1; | ||
| 1861 | show_x_trace=FALSE; | ||
| 1862 | #endif | ||
| 1863 | 1318 | ||
| 1864 | if (argv[0] && argv[0][0] == '-') { | 1319 | if (argv[0] && argv[0][0] == '-') { |
| 1865 | FILE *prof_input; | 1320 | FILE *prof_input; |
| @@ -1886,11 +1341,6 @@ int shell_main(int argc_l, char **argv_l) | |||
| 1886 | optind++; | 1341 | optind++; |
| 1887 | argv = argv+optind; | 1342 | argv = argv+optind; |
| 1888 | break; | 1343 | break; |
| 1889 | #ifdef BB_FEATURE_SH_ENVIRONMENT | ||
| 1890 | case 'x': | ||
| 1891 | show_x_trace = TRUE; | ||
| 1892 | break; | ||
| 1893 | #endif | ||
| 1894 | case 'i': | 1344 | case 'i': |
| 1895 | interactive = TRUE; | 1345 | interactive = TRUE; |
| 1896 | break; | 1346 | break; |
