diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-22 14:23:34 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-22 14:23:34 +0000 |
commit | 424f79b48f8a28da687c11f98927e3bd6ca805cf (patch) | |
tree | 64e84105bd7e70ce96e8d576410e5a2d496f9d97 | |
parent | e1300f6fc7a16e7c91f666192e66798eb40965be (diff) | |
download | busybox-w32-424f79b48f8a28da687c11f98927e3bd6ca805cf.tar.gz busybox-w32-424f79b48f8a28da687c11f98927e3bd6ca805cf.tar.bz2 busybox-w32-424f79b48f8a28da687c11f98927e3bd6ca805cf.zip |
hush: rearrange functions to reduce amount of forward references.
Minimal code changes.
-rw-r--r-- | shell/hush.c | 1848 |
1 files changed, 904 insertions, 944 deletions
diff --git a/shell/hush.c b/shell/hush.c index 9c0cd7c8e..0e43b63e7 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -1,9 +1,9 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | 1 | /* vi: set sw=4 ts=4: */ |
2 | /* | 2 | /* |
3 | * sh.c -- a prototype Bourne shell grammar parser | 3 | * A prototype Bourne shell grammar parser. |
4 | * Intended to follow the original Thompson and Ritchie | 4 | * Intended to follow the original Thompson and Ritchie |
5 | * "small and simple is beautiful" philosophy, which | 5 | * "small and simple is beautiful" philosophy, which |
6 | * incidentally is a good match to today's BusyBox. | 6 | * incidentally is a good match to today's BusyBox. |
7 | * | 7 | * |
8 | * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org> | 8 | * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org> |
9 | * | 9 | * |
@@ -22,8 +22,8 @@ | |||
22 | * Other credits: | 22 | * Other credits: |
23 | * o_addchr() derived from similar w_addchar function in glibc-2.2. | 23 | * o_addchr() derived from similar w_addchar function in glibc-2.2. |
24 | * setup_redirect(), redirect_opt_num(), and big chunks of main() | 24 | * setup_redirect(), redirect_opt_num(), and big chunks of main() |
25 | * and many builtins derived from contributions by Erik Andersen | 25 | * and many builtins derived from contributions by Erik Andersen. |
26 | * miscellaneous bugfixes from Matt Kraai. | 26 | * Miscellaneous bugfixes from Matt Kraai. |
27 | * | 27 | * |
28 | * There are two big (and related) architecture differences between | 28 | * There are two big (and related) architecture differences between |
29 | * this parser and the lash parser. One is that this version is | 29 | * this parser and the lash parser. One is that this version is |
@@ -55,19 +55,17 @@ | |||
55 | * change { and } from special chars to reserved words | 55 | * change { and } from special chars to reserved words |
56 | * builtins: return, trap, ulimit | 56 | * builtins: return, trap, ulimit |
57 | * test magic exec with redirection only | 57 | * test magic exec with redirection only |
58 | * check setting of global_argc and global_argv | ||
59 | * follow IFS rules more precisely, including update semantics | 58 | * follow IFS rules more precisely, including update semantics |
60 | * figure out what to do with backslash-newline | 59 | * figure out what to do with backslash-newline |
61 | * propagate syntax errors, die on resource errors? | 60 | * propagate syntax errors, die on resource errors? |
62 | * continuation lines, both explicit and implicit - done? | 61 | * continuation lines, both explicit and implicit - done? |
63 | * memory leak finding and plugging - done? | ||
64 | * maybe change charmap[] to use 2-bit entries | 62 | * maybe change charmap[] to use 2-bit entries |
65 | * | 63 | * |
66 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | 64 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
67 | */ | 65 | */ |
68 | 66 | ||
69 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ | 67 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ |
70 | //TODO: pull in some .h and find out do we have SINGLE_APPLET_MAIN? | 68 | //TODO: pull in some .h and find out whether we have SINGLE_APPLET_MAIN? |
71 | //#include "applet_tables.h" doesn't work | 69 | //#include "applet_tables.h" doesn't work |
72 | #include <glob.h> | 70 | #include <glob.h> |
73 | /* #include <dmalloc.h> */ | 71 | /* #include <dmalloc.h> */ |
@@ -75,7 +73,7 @@ | |||
75 | #include <fnmatch.h> | 73 | #include <fnmatch.h> |
76 | #endif | 74 | #endif |
77 | 75 | ||
78 | #define HUSH_VER_STR "0.91" | 76 | #define HUSH_VER_STR "0.92" |
79 | 77 | ||
80 | #if defined SINGLE_APPLET_MAIN | 78 | #if defined SINGLE_APPLET_MAIN |
81 | /* STANDALONE does not make sense, and won't compile */ | 79 | /* STANDALONE does not make sense, and won't compile */ |
@@ -108,6 +106,16 @@ | |||
108 | #define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0 | 106 | #define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0 |
109 | #endif | 107 | #endif |
110 | 108 | ||
109 | /* Do we support ANY keywords? */ | ||
110 | #if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE | ||
111 | #define HAS_KEYWORDS 1 | ||
112 | #define IF_HAS_KEYWORDS(...) __VA_ARGS__ | ||
113 | #define IF_HAS_NO_KEYWORDS(...) | ||
114 | #else | ||
115 | #define HAS_KEYWORDS 0 | ||
116 | #define IF_HAS_KEYWORDS(...) | ||
117 | #define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__ | ||
118 | #endif | ||
111 | 119 | ||
112 | /* Keep unconditionally on for now */ | 120 | /* Keep unconditionally on for now */ |
113 | #define HUSH_DEBUG 1 | 121 | #define HUSH_DEBUG 1 |
@@ -237,17 +245,9 @@ void xxfree(void *ptr) | |||
237 | #endif | 245 | #endif |
238 | 246 | ||
239 | 247 | ||
240 | /* Do we support ANY keywords? */ | 248 | static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="HUSH_VER_STR; |
241 | #if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE | ||
242 | #define HAS_KEYWORDS 1 | ||
243 | #define IF_HAS_KEYWORDS(...) __VA_ARGS__ | ||
244 | #define IF_HAS_NO_KEYWORDS(...) | ||
245 | #else | ||
246 | #define HAS_KEYWORDS 0 | ||
247 | #define IF_HAS_KEYWORDS(...) | ||
248 | #define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__ | ||
249 | #endif | ||
250 | 249 | ||
250 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | ||
251 | 251 | ||
252 | #define SPECIAL_VAR_SYMBOL 3 | 252 | #define SPECIAL_VAR_SYMBOL 3 |
253 | #define PARSEFLAG_EXIT_FROM_LOOP 1 | 253 | #define PARSEFLAG_EXIT_FROM_LOOP 1 |
@@ -324,7 +324,7 @@ struct command { | |||
324 | pid_t pid; /* 0 if exited */ | 324 | pid_t pid; /* 0 if exited */ |
325 | int assignment_cnt; /* how many argv[i] are assignments? */ | 325 | int assignment_cnt; /* how many argv[i] are assignments? */ |
326 | smallint is_stopped; /* is the command currently running? */ | 326 | smallint is_stopped; /* is the command currently running? */ |
327 | smallint grp_type; | 327 | smallint grp_type; /* GRP_xxx */ |
328 | struct pipe *group; /* if non-NULL, this "prog" is {} group, | 328 | struct pipe *group; /* if non-NULL, this "prog" is {} group, |
329 | * subshell, or a compound statement */ | 329 | * subshell, or a compound statement */ |
330 | char **argv; /* command name and arguments */ | 330 | char **argv; /* command name and arguments */ |
@@ -442,7 +442,6 @@ enum { | |||
442 | 442 | ||
443 | 443 | ||
444 | /* "Globals" within this file */ | 444 | /* "Globals" within this file */ |
445 | |||
446 | /* Sorted roughly by size (smaller offsets == smaller code) */ | 445 | /* Sorted roughly by size (smaller offsets == smaller code) */ |
447 | struct globals { | 446 | struct globals { |
448 | #if ENABLE_HUSH_INTERACTIVE | 447 | #if ENABLE_HUSH_INTERACTIVE |
@@ -503,7 +502,93 @@ struct globals { | |||
503 | } while (0) | 502 | } while (0) |
504 | 503 | ||
505 | 504 | ||
506 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | 505 | /* Function prototypes for builtins */ |
506 | static int builtin_cd(char **argv); | ||
507 | static int builtin_echo(char **argv); | ||
508 | static int builtin_eval(char **argv); | ||
509 | static int builtin_exec(char **argv); | ||
510 | static int builtin_exit(char **argv); | ||
511 | static int builtin_export(char **argv); | ||
512 | #if ENABLE_HUSH_JOB | ||
513 | static int builtin_fg_bg(char **argv); | ||
514 | static int builtin_jobs(char **argv); | ||
515 | #endif | ||
516 | #if ENABLE_HUSH_HELP | ||
517 | static int builtin_help(char **argv); | ||
518 | #endif | ||
519 | static int builtin_pwd(char **argv); | ||
520 | static int builtin_read(char **argv); | ||
521 | static int builtin_test(char **argv); | ||
522 | static int builtin_true(char **argv); | ||
523 | static int builtin_set(char **argv); | ||
524 | static int builtin_shift(char **argv); | ||
525 | static int builtin_source(char **argv); | ||
526 | static int builtin_umask(char **argv); | ||
527 | static int builtin_unset(char **argv); | ||
528 | #if ENABLE_HUSH_LOOPS | ||
529 | static int builtin_break(char **argv); | ||
530 | static int builtin_continue(char **argv); | ||
531 | #endif | ||
532 | //static int builtin_not_written(char **argv); | ||
533 | |||
534 | /* Table of built-in functions. They can be forked or not, depending on | ||
535 | * context: within pipes, they fork. As simple commands, they do not. | ||
536 | * When used in non-forking context, they can change global variables | ||
537 | * in the parent shell process. If forked, of course they cannot. | ||
538 | * For example, 'unset foo | whatever' will parse and run, but foo will | ||
539 | * still be set at the end. */ | ||
540 | struct built_in_command { | ||
541 | const char *cmd; | ||
542 | int (*function)(char **argv); | ||
543 | #if ENABLE_HUSH_HELP | ||
544 | const char *descr; | ||
545 | #define BLTIN(cmd, func, help) { cmd, func, help } | ||
546 | #else | ||
547 | #define BLTIN(cmd, func, help) { cmd, func } | ||
548 | #endif | ||
549 | }; | ||
550 | |||
551 | /* For now, echo and test are unconditionally enabled. | ||
552 | * Maybe make it configurable? */ | ||
553 | static const struct built_in_command bltins[] = { | ||
554 | BLTIN("." , builtin_source, "Run commands in a file"), | ||
555 | BLTIN(":" , builtin_true, "No-op"), | ||
556 | BLTIN("[" , builtin_test, "Test condition"), | ||
557 | BLTIN("[[" , builtin_test, "Test condition"), | ||
558 | #if ENABLE_HUSH_JOB | ||
559 | BLTIN("bg" , builtin_fg_bg, "Resume a job in the background"), | ||
560 | #endif | ||
561 | #if ENABLE_HUSH_LOOPS | ||
562 | BLTIN("break" , builtin_break, "Exit from a loop"), | ||
563 | #endif | ||
564 | BLTIN("cd" , builtin_cd, "Change directory"), | ||
565 | #if ENABLE_HUSH_LOOPS | ||
566 | BLTIN("continue", builtin_continue, "Start new loop iteration"), | ||
567 | #endif | ||
568 | BLTIN("echo" , builtin_echo, "Write to stdout"), | ||
569 | BLTIN("eval" , builtin_eval, "Construct and run shell command"), | ||
570 | BLTIN("exec" , builtin_exec, "Execute command, don't return to shell"), | ||
571 | BLTIN("exit" , builtin_exit, "Exit"), | ||
572 | BLTIN("export", builtin_export, "Set environment variable"), | ||
573 | #if ENABLE_HUSH_JOB | ||
574 | BLTIN("fg" , builtin_fg_bg, "Bring job into the foreground"), | ||
575 | BLTIN("jobs" , builtin_jobs, "List active jobs"), | ||
576 | #endif | ||
577 | BLTIN("pwd" , builtin_pwd, "Print current directory"), | ||
578 | BLTIN("read" , builtin_read, "Input environment variable"), | ||
579 | // BLTIN("return", builtin_not_written, "Return from a function"), | ||
580 | BLTIN("set" , builtin_set, "Set/unset shell local variables"), | ||
581 | BLTIN("shift" , builtin_shift, "Shift positional parameters"), | ||
582 | // BLTIN("trap" , builtin_not_written, "Trap signals"), | ||
583 | BLTIN("test" , builtin_test, "Test condition"), | ||
584 | // BLTIN("ulimit", builtin_not_written, "Control resource limits"), | ||
585 | BLTIN("umask" , builtin_umask, "Set file creation mask"), | ||
586 | BLTIN("unset" , builtin_unset, "Unset environment variable"), | ||
587 | #if ENABLE_HUSH_HELP | ||
588 | BLTIN("help" , builtin_help, "List shell built-in commands"), | ||
589 | #endif | ||
590 | }; | ||
591 | |||
507 | 592 | ||
508 | #if 1 | 593 | #if 1 |
509 | /* Normal */ | 594 | /* Normal */ |
@@ -520,7 +605,6 @@ static void syntax(const char *msg) | |||
520 | bb_error_msg_and_die(msg ? "%s: %s" : "syntax error", "syntax error", msg); | 605 | bb_error_msg_and_die(msg ? "%s: %s" : "syntax error", "syntax error", msg); |
521 | #endif | 606 | #endif |
522 | } | 607 | } |
523 | |||
524 | #else | 608 | #else |
525 | /* Debug */ | 609 | /* Debug */ |
526 | static void syntax_lineno(int line) | 610 | static void syntax_lineno(int line) |
@@ -536,84 +620,6 @@ static void syntax_lineno(int line) | |||
536 | #define syntax(str) syntax_lineno(__LINE__) | 620 | #define syntax(str) syntax_lineno(__LINE__) |
537 | #endif | 621 | #endif |
538 | 622 | ||
539 | /* Index of subroutines: */ | ||
540 | /* in_str manipulations: */ | ||
541 | static int static_get(struct in_str *i); | ||
542 | static int static_peek(struct in_str *i); | ||
543 | static int file_get(struct in_str *i); | ||
544 | static int file_peek(struct in_str *i); | ||
545 | static void setup_file_in_str(struct in_str *i, FILE *f); | ||
546 | static void setup_string_in_str(struct in_str *i, const char *s); | ||
547 | /* "run" the final data structures: */ | ||
548 | #if !defined(DEBUG_CLEAN) | ||
549 | #define free_pipe_list(head, indent) free_pipe_list(head) | ||
550 | #define free_pipe(pi, indent) free_pipe(pi) | ||
551 | #endif | ||
552 | static int free_pipe_list(struct pipe *head, int indent); | ||
553 | static int free_pipe(struct pipe *pi, int indent); | ||
554 | /* really run the final data structures: */ | ||
555 | typedef struct nommu_save_t { | ||
556 | char **new_env; | ||
557 | char **old_env; | ||
558 | char **argv; | ||
559 | } nommu_save_t; | ||
560 | #if BB_MMU | ||
561 | #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ | ||
562 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) | ||
563 | #define pseudo_exec(nommu_save, command, argv_expanded) \ | ||
564 | pseudo_exec(command, argv_expanded) | ||
565 | #endif | ||
566 | static void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignment_cnt, char **argv_expanded) NORETURN; | ||
567 | static void pseudo_exec(nommu_save_t *nommu_save, struct command *command, char **argv_expanded) NORETURN; | ||
568 | static int setup_redirects(struct command *prog, int squirrel[]); | ||
569 | static int run_list(struct pipe *pi); | ||
570 | static int run_pipe(struct pipe *pi); | ||
571 | /* data structure manipulation: */ | ||
572 | static int setup_redirect(struct parse_context *ctx, int fd, redir_type style, struct in_str *input); | ||
573 | static void initialize_context(struct parse_context *ctx); | ||
574 | static int done_word(o_string *dest, struct parse_context *ctx); | ||
575 | static int done_command(struct parse_context *ctx); | ||
576 | static void done_pipe(struct parse_context *ctx, pipe_style type); | ||
577 | /* primary string parsing: */ | ||
578 | static int redirect_dup_num(struct in_str *input); | ||
579 | static int redirect_opt_num(o_string *o); | ||
580 | #if ENABLE_HUSH_TICK | ||
581 | static int process_command_subs(o_string *dest, | ||
582 | struct in_str *input, const char *subst_end); | ||
583 | #endif | ||
584 | static int parse_group(o_string *dest, struct parse_context *ctx, struct in_str *input, int ch); | ||
585 | static const char *lookup_param(const char *src); | ||
586 | static int handle_dollar(o_string *dest, | ||
587 | struct in_str *input); | ||
588 | static int parse_stream(o_string *dest, struct parse_context *ctx, struct in_str *input0, const char *end_trigger); | ||
589 | /* setup: */ | ||
590 | static int parse_and_run_stream(struct in_str *inp, int parse_flag); | ||
591 | static int parse_and_run_string(const char *s, int parse_flag); | ||
592 | static int parse_and_run_file(FILE *f); | ||
593 | /* job management: */ | ||
594 | static int checkjobs(struct pipe* fg_pipe); | ||
595 | #if ENABLE_HUSH_JOB | ||
596 | static int checkjobs_and_fg_shell(struct pipe* fg_pipe); | ||
597 | static void insert_bg_job(struct pipe *pi); | ||
598 | static void remove_bg_job(struct pipe *pi); | ||
599 | static void delete_finished_bg_job(struct pipe *pi); | ||
600 | #else | ||
601 | int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */ | ||
602 | #endif | ||
603 | /* local variable support */ | ||
604 | static char **expand_strvec_to_strvec(char **argv); | ||
605 | /* used for eval */ | ||
606 | static char *expand_strvec_to_string(char **argv); | ||
607 | /* used for expansion of right hand of assignments */ | ||
608 | static char *expand_string_to_string(const char *str); | ||
609 | static struct variable *get_local_var(const char *name); | ||
610 | static int set_local_var(char *str, int flg_export); | ||
611 | static void unset_local_var(const char *name); | ||
612 | |||
613 | |||
614 | static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="HUSH_VER_STR; | ||
615 | |||
616 | |||
617 | static int glob_needed(const char *s) | 623 | static int glob_needed(const char *s) |
618 | { | 624 | { |
619 | while (*s) { | 625 | while (*s) { |
@@ -747,94 +753,6 @@ static void free_strings(char **strings) | |||
747 | } | 753 | } |
748 | 754 | ||
749 | 755 | ||
750 | /* Function prototypes for builtins */ | ||
751 | static int builtin_cd(char **argv); | ||
752 | static int builtin_echo(char **argv); | ||
753 | static int builtin_eval(char **argv); | ||
754 | static int builtin_exec(char **argv); | ||
755 | static int builtin_exit(char **argv); | ||
756 | static int builtin_export(char **argv); | ||
757 | #if ENABLE_HUSH_JOB | ||
758 | static int builtin_fg_bg(char **argv); | ||
759 | static int builtin_jobs(char **argv); | ||
760 | #endif | ||
761 | #if ENABLE_HUSH_HELP | ||
762 | static int builtin_help(char **argv); | ||
763 | #endif | ||
764 | static int builtin_pwd(char **argv); | ||
765 | static int builtin_read(char **argv); | ||
766 | static int builtin_test(char **argv); | ||
767 | static int builtin_true(char **argv); | ||
768 | static int builtin_set(char **argv); | ||
769 | static int builtin_shift(char **argv); | ||
770 | static int builtin_source(char **argv); | ||
771 | static int builtin_umask(char **argv); | ||
772 | static int builtin_unset(char **argv); | ||
773 | #if ENABLE_HUSH_LOOPS | ||
774 | static int builtin_break(char **argv); | ||
775 | static int builtin_continue(char **argv); | ||
776 | #endif | ||
777 | //static int builtin_not_written(char **argv); | ||
778 | |||
779 | /* Table of built-in functions. They can be forked or not, depending on | ||
780 | * context: within pipes, they fork. As simple commands, they do not. | ||
781 | * When used in non-forking context, they can change global variables | ||
782 | * in the parent shell process. If forked, of course they cannot. | ||
783 | * For example, 'unset foo | whatever' will parse and run, but foo will | ||
784 | * still be set at the end. */ | ||
785 | struct built_in_command { | ||
786 | const char *cmd; | ||
787 | int (*function)(char **argv); | ||
788 | #if ENABLE_HUSH_HELP | ||
789 | const char *descr; | ||
790 | #define BLTIN(cmd, func, help) { cmd, func, help } | ||
791 | #else | ||
792 | #define BLTIN(cmd, func, help) { cmd, func } | ||
793 | #endif | ||
794 | }; | ||
795 | |||
796 | /* For now, echo and test are unconditionally enabled. | ||
797 | * Maybe make it configurable? */ | ||
798 | static const struct built_in_command bltins[] = { | ||
799 | BLTIN("." , builtin_source, "Run commands in a file"), | ||
800 | BLTIN(":" , builtin_true, "No-op"), | ||
801 | BLTIN("[" , builtin_test, "Test condition"), | ||
802 | BLTIN("[[" , builtin_test, "Test condition"), | ||
803 | #if ENABLE_HUSH_JOB | ||
804 | BLTIN("bg" , builtin_fg_bg, "Resume a job in the background"), | ||
805 | #endif | ||
806 | #if ENABLE_HUSH_LOOPS | ||
807 | BLTIN("break" , builtin_break, "Exit from a loop"), | ||
808 | #endif | ||
809 | BLTIN("cd" , builtin_cd, "Change directory"), | ||
810 | #if ENABLE_HUSH_LOOPS | ||
811 | BLTIN("continue", builtin_continue, "Start new loop iteration"), | ||
812 | #endif | ||
813 | BLTIN("echo" , builtin_echo, "Write to stdout"), | ||
814 | BLTIN("eval" , builtin_eval, "Construct and run shell command"), | ||
815 | BLTIN("exec" , builtin_exec, "Execute command, don't return to shell"), | ||
816 | BLTIN("exit" , builtin_exit, "Exit"), | ||
817 | BLTIN("export", builtin_export, "Set environment variable"), | ||
818 | #if ENABLE_HUSH_JOB | ||
819 | BLTIN("fg" , builtin_fg_bg, "Bring job into the foreground"), | ||
820 | BLTIN("jobs" , builtin_jobs, "List active jobs"), | ||
821 | #endif | ||
822 | BLTIN("pwd" , builtin_pwd, "Print current directory"), | ||
823 | BLTIN("read" , builtin_read, "Input environment variable"), | ||
824 | // BLTIN("return", builtin_not_written, "Return from a function"), | ||
825 | BLTIN("set" , builtin_set, "Set/unset shell local variables"), | ||
826 | BLTIN("shift" , builtin_shift, "Shift positional parameters"), | ||
827 | // BLTIN("trap" , builtin_not_written, "Trap signals"), | ||
828 | BLTIN("test" , builtin_test, "Test condition"), | ||
829 | // BLTIN("ulimit", builtin_not_written, "Control resource limits"), | ||
830 | BLTIN("umask" , builtin_umask, "Set file creation mask"), | ||
831 | BLTIN("unset" , builtin_unset, "Unset environment variable"), | ||
832 | #if ENABLE_HUSH_HELP | ||
833 | BLTIN("help" , builtin_help, "List shell built-in commands"), | ||
834 | #endif | ||
835 | }; | ||
836 | |||
837 | |||
838 | /* Signals are grouped, we handle them in batches */ | 756 | /* Signals are grouped, we handle them in batches */ |
839 | static void set_misc_sighandler(void (*handler)(int)) | 757 | static void set_misc_sighandler(void (*handler)(int)) |
840 | { | 758 | { |
@@ -961,8 +879,10 @@ static void hush_exit(int exitcode) | |||
961 | 879 | ||
962 | static const char *set_cwd(void) | 880 | static const char *set_cwd(void) |
963 | { | 881 | { |
882 | /* xrealloc_getcwd_or_warn(arg) calls free(arg), | ||
883 | * we must not try to free(bb_msg_unknown) */ | ||
964 | if (G.cwd == bb_msg_unknown) | 884 | if (G.cwd == bb_msg_unknown) |
965 | G.cwd = NULL; /* xrealloc_getcwd_or_warn(arg) calls free(arg)! */ | 885 | G.cwd = NULL; |
966 | G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd); | 886 | G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd); |
967 | if (!G.cwd) | 887 | if (!G.cwd) |
968 | G.cwd = bb_msg_unknown; | 888 | G.cwd = bb_msg_unknown; |
@@ -970,6 +890,295 @@ static const char *set_cwd(void) | |||
970 | } | 890 | } |
971 | 891 | ||
972 | 892 | ||
893 | /* Get/check local shell variables */ | ||
894 | static struct variable *get_local_var(const char *name) | ||
895 | { | ||
896 | struct variable *cur; | ||
897 | int len; | ||
898 | |||
899 | if (!name) | ||
900 | return NULL; | ||
901 | len = strlen(name); | ||
902 | for (cur = G.top_var; cur; cur = cur->next) { | ||
903 | if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=') | ||
904 | return cur; | ||
905 | } | ||
906 | return NULL; | ||
907 | } | ||
908 | |||
909 | /* Basically useful version until someone wants to get fancier, | ||
910 | * see the bash man page under "Parameter Expansion" */ | ||
911 | static const char *lookup_param(const char *src) | ||
912 | { | ||
913 | struct variable *var = get_local_var(src); | ||
914 | if (var) | ||
915 | return strchr(var->varstr, '=') + 1; | ||
916 | return NULL; | ||
917 | } | ||
918 | |||
919 | /* str holds "NAME=VAL" and is expected to be malloced. | ||
920 | * We take ownership of it. */ | ||
921 | static int set_local_var(char *str, int flg_export) | ||
922 | { | ||
923 | struct variable *cur; | ||
924 | char *value; | ||
925 | int name_len; | ||
926 | |||
927 | value = strchr(str, '='); | ||
928 | if (!value) { /* not expected to ever happen? */ | ||
929 | free(str); | ||
930 | return -1; | ||
931 | } | ||
932 | |||
933 | name_len = value - str + 1; /* including '=' */ | ||
934 | cur = G.top_var; /* cannot be NULL (we have HUSH_VERSION and it's RO) */ | ||
935 | while (1) { | ||
936 | if (strncmp(cur->varstr, str, name_len) != 0) { | ||
937 | if (!cur->next) { | ||
938 | /* Bail out. Note that now cur points | ||
939 | * to last var in linked list */ | ||
940 | break; | ||
941 | } | ||
942 | cur = cur->next; | ||
943 | continue; | ||
944 | } | ||
945 | /* We found an existing var with this name */ | ||
946 | *value = '\0'; | ||
947 | if (cur->flg_read_only) { | ||
948 | bb_error_msg("%s: readonly variable", str); | ||
949 | free(str); | ||
950 | return -1; | ||
951 | } | ||
952 | debug_printf_env("%s: unsetenv '%s'\n", __func__, str); | ||
953 | unsetenv(str); /* just in case */ | ||
954 | *value = '='; | ||
955 | if (strcmp(cur->varstr, str) == 0) { | ||
956 | free_and_exp: | ||
957 | free(str); | ||
958 | goto exp; | ||
959 | } | ||
960 | if (cur->max_len >= strlen(str)) { | ||
961 | /* This one is from startup env, reuse space */ | ||
962 | strcpy(cur->varstr, str); | ||
963 | goto free_and_exp; | ||
964 | } | ||
965 | /* max_len == 0 signifies "malloced" var, which we can | ||
966 | * (and has to) free */ | ||
967 | if (!cur->max_len) | ||
968 | free(cur->varstr); | ||
969 | cur->max_len = 0; | ||
970 | goto set_str_and_exp; | ||
971 | } | ||
972 | |||
973 | /* Not found - create next variable struct */ | ||
974 | cur->next = xzalloc(sizeof(*cur)); | ||
975 | cur = cur->next; | ||
976 | |||
977 | set_str_and_exp: | ||
978 | cur->varstr = str; | ||
979 | exp: | ||
980 | if (flg_export) | ||
981 | cur->flg_export = 1; | ||
982 | if (cur->flg_export) { | ||
983 | debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); | ||
984 | return putenv(cur->varstr); | ||
985 | } | ||
986 | return 0; | ||
987 | } | ||
988 | |||
989 | static void unset_local_var(const char *name) | ||
990 | { | ||
991 | struct variable *cur; | ||
992 | struct variable *prev = prev; /* for gcc */ | ||
993 | int name_len; | ||
994 | |||
995 | if (!name) | ||
996 | return; | ||
997 | name_len = strlen(name); | ||
998 | cur = G.top_var; | ||
999 | while (cur) { | ||
1000 | if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { | ||
1001 | if (cur->flg_read_only) { | ||
1002 | bb_error_msg("%s: readonly variable", name); | ||
1003 | return; | ||
1004 | } | ||
1005 | /* prev is ok to use here because 1st variable, HUSH_VERSION, | ||
1006 | * is ro, and we cannot reach this code on the 1st pass */ | ||
1007 | prev->next = cur->next; | ||
1008 | debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr); | ||
1009 | bb_unsetenv(cur->varstr); | ||
1010 | if (!cur->max_len) | ||
1011 | free(cur->varstr); | ||
1012 | free(cur); | ||
1013 | return; | ||
1014 | } | ||
1015 | prev = cur; | ||
1016 | cur = cur->next; | ||
1017 | } | ||
1018 | } | ||
1019 | |||
1020 | |||
1021 | /* | ||
1022 | * in_str support | ||
1023 | */ | ||
1024 | static int static_get(struct in_str *i) | ||
1025 | { | ||
1026 | int ch = *i->p++; | ||
1027 | if (ch == '\0') return EOF; | ||
1028 | return ch; | ||
1029 | } | ||
1030 | |||
1031 | static int static_peek(struct in_str *i) | ||
1032 | { | ||
1033 | return *i->p; | ||
1034 | } | ||
1035 | |||
1036 | #if ENABLE_HUSH_INTERACTIVE | ||
1037 | |||
1038 | #if ENABLE_FEATURE_EDITING | ||
1039 | static void cmdedit_set_initial_prompt(void) | ||
1040 | { | ||
1041 | #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT | ||
1042 | G.PS1 = NULL; | ||
1043 | #else | ||
1044 | G.PS1 = getenv("PS1"); | ||
1045 | if (G.PS1 == NULL) | ||
1046 | G.PS1 = "\\w \\$ "; | ||
1047 | #endif | ||
1048 | } | ||
1049 | #endif /* EDITING */ | ||
1050 | |||
1051 | static const char* setup_prompt_string(int promptmode) | ||
1052 | { | ||
1053 | const char *prompt_str; | ||
1054 | debug_printf("setup_prompt_string %d ", promptmode); | ||
1055 | #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT | ||
1056 | /* Set up the prompt */ | ||
1057 | if (promptmode == 0) { /* PS1 */ | ||
1058 | free((char*)G.PS1); | ||
1059 | G.PS1 = xasprintf("%s %c ", G.cwd, (geteuid() != 0) ? '$' : '#'); | ||
1060 | prompt_str = G.PS1; | ||
1061 | } else { | ||
1062 | prompt_str = G.PS2; | ||
1063 | } | ||
1064 | #else | ||
1065 | prompt_str = (promptmode == 0) ? G.PS1 : G.PS2; | ||
1066 | #endif | ||
1067 | debug_printf("result '%s'\n", prompt_str); | ||
1068 | return prompt_str; | ||
1069 | } | ||
1070 | |||
1071 | static void get_user_input(struct in_str *i) | ||
1072 | { | ||
1073 | int r; | ||
1074 | const char *prompt_str; | ||
1075 | |||
1076 | prompt_str = setup_prompt_string(i->promptmode); | ||
1077 | #if ENABLE_FEATURE_EDITING | ||
1078 | /* Enable command line editing only while a command line | ||
1079 | * is actually being read */ | ||
1080 | do { | ||
1081 | r = read_line_input(prompt_str, G.user_input_buf, BUFSIZ-1, G.line_input_state); | ||
1082 | } while (r == 0); /* repeat if Ctrl-C */ | ||
1083 | i->eof_flag = (r < 0); | ||
1084 | if (i->eof_flag) { /* EOF/error detected */ | ||
1085 | G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ | ||
1086 | G.user_input_buf[1] = '\0'; | ||
1087 | } | ||
1088 | #else | ||
1089 | fputs(prompt_str, stdout); | ||
1090 | fflush(stdout); | ||
1091 | G.user_input_buf[0] = r = fgetc(i->file); | ||
1092 | /*G.user_input_buf[1] = '\0'; - already is and never changed */ | ||
1093 | i->eof_flag = (r == EOF); | ||
1094 | #endif | ||
1095 | i->p = G.user_input_buf; | ||
1096 | } | ||
1097 | |||
1098 | #endif /* INTERACTIVE */ | ||
1099 | |||
1100 | /* This is the magic location that prints prompts | ||
1101 | * and gets data back from the user */ | ||
1102 | static int file_get(struct in_str *i) | ||
1103 | { | ||
1104 | int ch; | ||
1105 | |||
1106 | /* If there is data waiting, eat it up */ | ||
1107 | if (i->p && *i->p) { | ||
1108 | #if ENABLE_HUSH_INTERACTIVE | ||
1109 | take_cached: | ||
1110 | #endif | ||
1111 | ch = *i->p++; | ||
1112 | if (i->eof_flag && !*i->p) | ||
1113 | ch = EOF; | ||
1114 | } else { | ||
1115 | /* need to double check i->file because we might be doing something | ||
1116 | * more complicated by now, like sourcing or substituting. */ | ||
1117 | #if ENABLE_HUSH_INTERACTIVE | ||
1118 | if (G.interactive_fd && i->promptme && i->file == stdin) { | ||
1119 | do { | ||
1120 | get_user_input(i); | ||
1121 | } while (!*i->p); /* need non-empty line */ | ||
1122 | i->promptmode = 1; /* PS2 */ | ||
1123 | i->promptme = 0; | ||
1124 | goto take_cached; | ||
1125 | } | ||
1126 | #endif | ||
1127 | ch = fgetc(i->file); | ||
1128 | } | ||
1129 | debug_printf("file_get: got a '%c' %d\n", ch, ch); | ||
1130 | #if ENABLE_HUSH_INTERACTIVE | ||
1131 | if (ch == '\n') | ||
1132 | i->promptme = 1; | ||
1133 | #endif | ||
1134 | return ch; | ||
1135 | } | ||
1136 | |||
1137 | /* All the callers guarantee this routine will never be | ||
1138 | * used right after a newline, so prompting is not needed. | ||
1139 | */ | ||
1140 | static int file_peek(struct in_str *i) | ||
1141 | { | ||
1142 | int ch; | ||
1143 | if (i->p && *i->p) { | ||
1144 | if (i->eof_flag && !i->p[1]) | ||
1145 | return EOF; | ||
1146 | return *i->p; | ||
1147 | } | ||
1148 | ch = fgetc(i->file); | ||
1149 | i->eof_flag = (ch == EOF); | ||
1150 | i->peek_buf[0] = ch; | ||
1151 | i->peek_buf[1] = '\0'; | ||
1152 | i->p = i->peek_buf; | ||
1153 | debug_printf("file_peek: got a '%c' %d\n", *i->p, *i->p); | ||
1154 | return ch; | ||
1155 | } | ||
1156 | |||
1157 | static void setup_file_in_str(struct in_str *i, FILE *f) | ||
1158 | { | ||
1159 | i->peek = file_peek; | ||
1160 | i->get = file_get; | ||
1161 | #if ENABLE_HUSH_INTERACTIVE | ||
1162 | i->promptme = 1; | ||
1163 | i->promptmode = 0; /* PS1 */ | ||
1164 | #endif | ||
1165 | i->file = f; | ||
1166 | i->p = NULL; | ||
1167 | } | ||
1168 | |||
1169 | static void setup_string_in_str(struct in_str *i, const char *s) | ||
1170 | { | ||
1171 | i->peek = static_peek; | ||
1172 | i->get = static_get; | ||
1173 | #if ENABLE_HUSH_INTERACTIVE | ||
1174 | i->promptme = 1; | ||
1175 | i->promptmode = 0; /* PS1 */ | ||
1176 | #endif | ||
1177 | i->p = s; | ||
1178 | i->eof_flag = 0; | ||
1179 | } | ||
1180 | |||
1181 | |||
973 | /* | 1182 | /* |
974 | * o_string support | 1183 | * o_string support |
975 | */ | 1184 | */ |
@@ -1252,164 +1461,300 @@ static char **o_finalize_list(o_string *o, int n) | |||
1252 | } | 1461 | } |
1253 | 1462 | ||
1254 | 1463 | ||
1255 | /* | 1464 | /* expand_strvec_to_strvec() takes a list of strings, expands |
1256 | * in_str support | 1465 | * all variable references within and returns a pointer to |
1257 | */ | 1466 | * a list of expanded strings, possibly with larger number |
1258 | static int static_get(struct in_str *i) | 1467 | * of strings. (Think VAR="a b"; echo $VAR). |
1259 | { | 1468 | * This new list is allocated as a single malloc block. |
1260 | int ch = *i->p++; | 1469 | * NULL-terminated list of char* pointers is at the beginning of it, |
1261 | if (ch == '\0') return EOF; | 1470 | * followed by strings themself. |
1262 | return ch; | 1471 | * Caller can deallocate entire list by single free(list). */ |
1263 | } | ||
1264 | 1472 | ||
1265 | static int static_peek(struct in_str *i) | 1473 | /* Store given string, finalizing the word and starting new one whenever |
1474 | * we encounter IFS char(s). This is used for expanding variable values. | ||
1475 | * End-of-string does NOT finalize word: think about 'echo -$VAR-' */ | ||
1476 | static int expand_on_ifs(o_string *output, int n, const char *str) | ||
1266 | { | 1477 | { |
1267 | return *i->p; | 1478 | while (1) { |
1479 | int word_len = strcspn(str, G.ifs); | ||
1480 | if (word_len) { | ||
1481 | if (output->o_quote || !output->o_glob) | ||
1482 | o_addQstr(output, str, word_len); | ||
1483 | else /* protect backslashes against globbing up :) */ | ||
1484 | o_addstr_duplicate_backslash(output, str, word_len); | ||
1485 | str += word_len; | ||
1486 | } | ||
1487 | if (!*str) /* EOL - do not finalize word */ | ||
1488 | break; | ||
1489 | o_addchr(output, '\0'); | ||
1490 | debug_print_list("expand_on_ifs", output, n); | ||
1491 | n = o_save_ptr(output, n); | ||
1492 | str += strspn(str, G.ifs); /* skip ifs chars */ | ||
1493 | } | ||
1494 | debug_print_list("expand_on_ifs[1]", output, n); | ||
1495 | return n; | ||
1268 | } | 1496 | } |
1269 | 1497 | ||
1270 | #if ENABLE_HUSH_INTERACTIVE | 1498 | #if ENABLE_HUSH_TICK |
1499 | static int process_command_subs(o_string *dest, | ||
1500 | struct in_str *input, const char *subst_end); | ||
1501 | #endif | ||
1271 | 1502 | ||
1272 | #if ENABLE_FEATURE_EDITING | 1503 | /* Expand all variable references in given string, adding words to list[] |
1273 | static void cmdedit_set_initial_prompt(void) | 1504 | * at n, n+1,... positions. Return updated n (so that list[n] is next one |
1505 | * to be filled). This routine is extremely tricky: has to deal with | ||
1506 | * variables/parameters with whitespace, $* and $@, and constructs like | ||
1507 | * 'echo -$*-'. If you play here, you must run testsuite afterwards! */ | ||
1508 | static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | ||
1274 | { | 1509 | { |
1275 | #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT | 1510 | /* or_mask is either 0 (normal case) or 0x80 |
1276 | G.PS1 = NULL; | 1511 | * (expansion of right-hand side of assignment == 1-element expand. |
1277 | #else | 1512 | * It will also do no globbing, and thus we must not backslash-quote!) */ |
1278 | G.PS1 = getenv("PS1"); | 1513 | |
1279 | if (G.PS1 == NULL) | 1514 | char first_ch, ored_ch; |
1280 | G.PS1 = "\\w \\$ "; | 1515 | int i; |
1516 | const char *val; | ||
1517 | char *p; | ||
1518 | |||
1519 | ored_ch = 0; | ||
1520 | |||
1521 | debug_printf_expand("expand_vars_to_list: arg '%s'\n", arg); | ||
1522 | debug_print_list("expand_vars_to_list", output, n); | ||
1523 | n = o_save_ptr(output, n); | ||
1524 | debug_print_list("expand_vars_to_list[0]", output, n); | ||
1525 | |||
1526 | while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { | ||
1527 | #if ENABLE_HUSH_TICK | ||
1528 | o_string subst_result = NULL_O_STRING; | ||
1281 | #endif | 1529 | #endif |
1282 | } | 1530 | o_addstr(output, arg, p - arg); |
1283 | #endif /* EDITING */ | 1531 | debug_print_list("expand_vars_to_list[1]", output, n); |
1532 | arg = ++p; | ||
1533 | p = strchr(p, SPECIAL_VAR_SYMBOL); | ||
1284 | 1534 | ||
1285 | static const char* setup_prompt_string(int promptmode) | 1535 | first_ch = arg[0] | or_mask; /* forced to "quoted" if or_mask = 0x80 */ |
1286 | { | 1536 | /* "$@" is special. Even if quoted, it can still |
1287 | const char *prompt_str; | 1537 | * expand to nothing (not even an empty string) */ |
1288 | debug_printf("setup_prompt_string %d ", promptmode); | 1538 | if ((first_ch & 0x7f) != '@') |
1289 | #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT | 1539 | ored_ch |= first_ch; |
1290 | /* Set up the prompt */ | 1540 | val = NULL; |
1291 | if (promptmode == 0) { /* PS1 */ | 1541 | switch (first_ch & 0x7f) { |
1292 | free((char*)G.PS1); | 1542 | /* Highest bit in first_ch indicates that var is double-quoted */ |
1293 | G.PS1 = xasprintf("%s %c ", G.cwd, (geteuid() != 0) ? '$' : '#'); | 1543 | case '$': /* pid */ |
1294 | prompt_str = G.PS1; | 1544 | val = utoa(G.root_pid); |
1545 | break; | ||
1546 | case '!': /* bg pid */ | ||
1547 | val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)""; | ||
1548 | break; | ||
1549 | case '?': /* exitcode */ | ||
1550 | val = utoa(G.last_return_code); | ||
1551 | break; | ||
1552 | case '#': /* argc */ | ||
1553 | val = utoa(G.global_argc ? G.global_argc-1 : 0); | ||
1554 | break; | ||
1555 | case '*': | ||
1556 | case '@': | ||
1557 | i = 1; | ||
1558 | if (!G.global_argv[i]) | ||
1559 | break; | ||
1560 | ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ | ||
1561 | if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ | ||
1562 | smallint sv = output->o_quote; | ||
1563 | /* unquoted var's contents should be globbed, so don't quote */ | ||
1564 | output->o_quote = 0; | ||
1565 | while (G.global_argv[i]) { | ||
1566 | n = expand_on_ifs(output, n, G.global_argv[i]); | ||
1567 | debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1); | ||
1568 | if (G.global_argv[i++][0] && G.global_argv[i]) { | ||
1569 | /* this argv[] is not empty and not last: | ||
1570 | * put terminating NUL, start new word */ | ||
1571 | o_addchr(output, '\0'); | ||
1572 | debug_print_list("expand_vars_to_list[2]", output, n); | ||
1573 | n = o_save_ptr(output, n); | ||
1574 | debug_print_list("expand_vars_to_list[3]", output, n); | ||
1575 | } | ||
1576 | } | ||
1577 | output->o_quote = sv; | ||
1578 | } else | ||
1579 | /* If or_mask is nonzero, we handle assignment 'a=....$@.....' | ||
1580 | * and in this case should treat it like '$*' - see 'else...' below */ | ||
1581 | if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */ | ||
1582 | while (1) { | ||
1583 | o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i])); | ||
1584 | if (++i >= G.global_argc) | ||
1585 | break; | ||
1586 | o_addchr(output, '\0'); | ||
1587 | debug_print_list("expand_vars_to_list[4]", output, n); | ||
1588 | n = o_save_ptr(output, n); | ||
1589 | } | ||
1590 | } else { /* quoted $*: add as one word */ | ||
1591 | while (1) { | ||
1592 | o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i])); | ||
1593 | if (!G.global_argv[++i]) | ||
1594 | break; | ||
1595 | if (G.ifs[0]) | ||
1596 | o_addchr(output, G.ifs[0]); | ||
1597 | } | ||
1598 | } | ||
1599 | break; | ||
1600 | case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */ | ||
1601 | /* "Empty variable", used to make "" etc to not disappear */ | ||
1602 | arg++; | ||
1603 | ored_ch = 0x80; | ||
1604 | break; | ||
1605 | #if ENABLE_HUSH_TICK | ||
1606 | case '`': { /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ | ||
1607 | struct in_str input; | ||
1608 | *p = '\0'; | ||
1609 | arg++; | ||
1610 | //TODO: can we just stuff it into "output" directly? | ||
1611 | debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); | ||
1612 | setup_string_in_str(&input, arg); | ||
1613 | process_command_subs(&subst_result, &input, NULL); | ||
1614 | debug_printf_subst("SUBST RES '%s'\n", subst_result.data); | ||
1615 | val = subst_result.data; | ||
1616 | goto store_val; | ||
1617 | } | ||
1618 | #endif | ||
1619 | default: /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */ | ||
1620 | *p = '\0'; | ||
1621 | arg[0] = first_ch & 0x7f; | ||
1622 | if (isdigit(arg[0])) { | ||
1623 | i = xatoi_u(arg); | ||
1624 | if (i < G.global_argc) | ||
1625 | val = G.global_argv[i]; | ||
1626 | /* else val remains NULL: $N with too big N */ | ||
1627 | } else | ||
1628 | val = lookup_param(arg); | ||
1629 | arg[0] = first_ch; | ||
1630 | #if ENABLE_HUSH_TICK | ||
1631 | store_val: | ||
1632 | #endif | ||
1633 | *p = SPECIAL_VAR_SYMBOL; | ||
1634 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ | ||
1635 | debug_printf_expand("unquoted '%s', output->o_quote:%d\n", val, output->o_quote); | ||
1636 | if (val) { | ||
1637 | /* unquoted var's contents should be globbed, so don't quote */ | ||
1638 | smallint sv = output->o_quote; | ||
1639 | output->o_quote = 0; | ||
1640 | n = expand_on_ifs(output, n, val); | ||
1641 | val = NULL; | ||
1642 | output->o_quote = sv; | ||
1643 | } | ||
1644 | } else { /* quoted $VAR, val will be appended below */ | ||
1645 | debug_printf_expand("quoted '%s', output->o_quote:%d\n", val, output->o_quote); | ||
1646 | } | ||
1647 | } | ||
1648 | if (val) { | ||
1649 | o_addQstr(output, val, strlen(val)); | ||
1650 | } | ||
1651 | |||
1652 | #if ENABLE_HUSH_TICK | ||
1653 | o_free(&subst_result); | ||
1654 | #endif | ||
1655 | arg = ++p; | ||
1656 | } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */ | ||
1657 | |||
1658 | if (arg[0]) { | ||
1659 | debug_print_list("expand_vars_to_list[a]", output, n); | ||
1660 | /* this part is literal, and it was already pre-quoted | ||
1661 | * if needed (much earlier), do not use o_addQstr here! */ | ||
1662 | o_addstr(output, arg, strlen(arg) + 1); | ||
1663 | debug_print_list("expand_vars_to_list[b]", output, n); | ||
1664 | } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ | ||
1665 | && !(ored_ch & 0x80) /* and all vars were not quoted. */ | ||
1666 | ) { | ||
1667 | n--; | ||
1668 | /* allow to reuse list[n] later without re-growth */ | ||
1669 | output->has_empty_slot = 1; | ||
1295 | } else { | 1670 | } else { |
1296 | prompt_str = G.PS2; | 1671 | o_addchr(output, '\0'); |
1297 | } | 1672 | } |
1298 | #else | 1673 | return n; |
1299 | prompt_str = (promptmode == 0) ? G.PS1 : G.PS2; | ||
1300 | #endif | ||
1301 | debug_printf("result '%s'\n", prompt_str); | ||
1302 | return prompt_str; | ||
1303 | } | 1674 | } |
1304 | 1675 | ||
1305 | static void get_user_input(struct in_str *i) | 1676 | static char **expand_variables(char **argv, int or_mask) |
1306 | { | 1677 | { |
1307 | int r; | 1678 | int n; |
1308 | const char *prompt_str; | 1679 | char **list; |
1680 | char **v; | ||
1681 | o_string output = NULL_O_STRING; | ||
1309 | 1682 | ||
1310 | prompt_str = setup_prompt_string(i->promptmode); | 1683 | if (or_mask & 0x100) { |
1311 | #if ENABLE_FEATURE_EDITING | 1684 | output.o_quote = 1; /* protect against globbing for "$var" */ |
1312 | /* Enable command line editing only while a command line | 1685 | /* (unquoted $var will temporarily switch it off) */ |
1313 | * is actually being read */ | 1686 | output.o_glob = 1; |
1314 | do { | ||
1315 | r = read_line_input(prompt_str, G.user_input_buf, BUFSIZ-1, G.line_input_state); | ||
1316 | } while (r == 0); /* repeat if Ctrl-C */ | ||
1317 | i->eof_flag = (r < 0); | ||
1318 | if (i->eof_flag) { /* EOF/error detected */ | ||
1319 | G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ | ||
1320 | G.user_input_buf[1] = '\0'; | ||
1321 | } | 1687 | } |
1322 | #else | ||
1323 | fputs(prompt_str, stdout); | ||
1324 | fflush(stdout); | ||
1325 | G.user_input_buf[0] = r = fgetc(i->file); | ||
1326 | /*G.user_input_buf[1] = '\0'; - already is and never changed */ | ||
1327 | i->eof_flag = (r == EOF); | ||
1328 | #endif | ||
1329 | i->p = G.user_input_buf; | ||
1330 | } | ||
1331 | 1688 | ||
1332 | #endif /* INTERACTIVE */ | 1689 | n = 0; |
1690 | v = argv; | ||
1691 | while (*v) { | ||
1692 | n = expand_vars_to_list(&output, n, *v, (char)or_mask); | ||
1693 | v++; | ||
1694 | } | ||
1695 | debug_print_list("expand_variables", &output, n); | ||
1333 | 1696 | ||
1334 | /* This is the magic location that prints prompts | 1697 | /* output.data (malloced in one block) gets returned in "list" */ |
1335 | * and gets data back from the user */ | 1698 | list = o_finalize_list(&output, n); |
1336 | static int file_get(struct in_str *i) | 1699 | debug_print_strings("expand_variables[1]", list); |
1337 | { | 1700 | return list; |
1338 | int ch; | 1701 | } |
1339 | 1702 | ||
1340 | /* If there is data waiting, eat it up */ | 1703 | static char **expand_strvec_to_strvec(char **argv) |
1341 | if (i->p && *i->p) { | 1704 | { |
1342 | #if ENABLE_HUSH_INTERACTIVE | 1705 | return expand_variables(argv, 0x100); |
1343 | take_cached: | ||
1344 | #endif | ||
1345 | ch = *i->p++; | ||
1346 | if (i->eof_flag && !*i->p) | ||
1347 | ch = EOF; | ||
1348 | } else { | ||
1349 | /* need to double check i->file because we might be doing something | ||
1350 | * more complicated by now, like sourcing or substituting. */ | ||
1351 | #if ENABLE_HUSH_INTERACTIVE | ||
1352 | if (G.interactive_fd && i->promptme && i->file == stdin) { | ||
1353 | do { | ||
1354 | get_user_input(i); | ||
1355 | } while (!*i->p); /* need non-empty line */ | ||
1356 | i->promptmode = 1; /* PS2 */ | ||
1357 | i->promptme = 0; | ||
1358 | goto take_cached; | ||
1359 | } | ||
1360 | #endif | ||
1361 | ch = fgetc(i->file); | ||
1362 | } | ||
1363 | debug_printf("file_get: got a '%c' %d\n", ch, ch); | ||
1364 | #if ENABLE_HUSH_INTERACTIVE | ||
1365 | if (ch == '\n') | ||
1366 | i->promptme = 1; | ||
1367 | #endif | ||
1368 | return ch; | ||
1369 | } | 1706 | } |
1370 | 1707 | ||
1371 | /* All the callers guarantee this routine will never be | 1708 | /* Used for expansion of right hand of assignments */ |
1372 | * used right after a newline, so prompting is not needed. | 1709 | /* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs |
1373 | */ | 1710 | * "v=/bin/c*" */ |
1374 | static int file_peek(struct in_str *i) | 1711 | static char *expand_string_to_string(const char *str) |
1375 | { | 1712 | { |
1376 | int ch; | 1713 | char *argv[2], **list; |
1377 | if (i->p && *i->p) { | 1714 | |
1378 | if (i->eof_flag && !i->p[1]) | 1715 | argv[0] = (char*)str; |
1379 | return EOF; | 1716 | argv[1] = NULL; |
1380 | return *i->p; | 1717 | list = expand_variables(argv, 0x80); /* 0x80: make one-element expansion */ |
1381 | } | 1718 | if (HUSH_DEBUG) |
1382 | ch = fgetc(i->file); | 1719 | if (!list[0] || list[1]) |
1383 | i->eof_flag = (ch == EOF); | 1720 | bb_error_msg_and_die("BUG in varexp2"); |
1384 | i->peek_buf[0] = ch; | 1721 | /* actually, just move string 2*sizeof(char*) bytes back */ |
1385 | i->peek_buf[1] = '\0'; | 1722 | overlapping_strcpy((char*)list, list[0]); |
1386 | i->p = i->peek_buf; | 1723 | debug_printf_expand("string_to_string='%s'\n", (char*)list); |
1387 | debug_printf("file_peek: got a '%c' %d\n", *i->p, *i->p); | 1724 | return (char*)list; |
1388 | return ch; | ||
1389 | } | 1725 | } |
1390 | 1726 | ||
1391 | static void setup_file_in_str(struct in_str *i, FILE *f) | 1727 | /* Used for "eval" builtin */ |
1728 | static char* expand_strvec_to_string(char **argv) | ||
1392 | { | 1729 | { |
1393 | i->peek = file_peek; | 1730 | char **list; |
1394 | i->get = file_get; | 1731 | |
1395 | #if ENABLE_HUSH_INTERACTIVE | 1732 | list = expand_variables(argv, 0x80); |
1396 | i->promptme = 1; | 1733 | /* Convert all NULs to spaces */ |
1397 | i->promptmode = 0; /* PS1 */ | 1734 | if (list[0]) { |
1398 | #endif | 1735 | int n = 1; |
1399 | i->file = f; | 1736 | while (list[n]) { |
1400 | i->p = NULL; | 1737 | if (HUSH_DEBUG) |
1738 | if (list[n-1] + strlen(list[n-1]) + 1 != list[n]) | ||
1739 | bb_error_msg_and_die("BUG in varexp3"); | ||
1740 | list[n][-1] = ' '; /* TODO: or to G.ifs[0]? */ | ||
1741 | n++; | ||
1742 | } | ||
1743 | } | ||
1744 | overlapping_strcpy((char*)list, list[0]); | ||
1745 | debug_printf_expand("strvec_to_string='%s'\n", (char*)list); | ||
1746 | return (char*)list; | ||
1401 | } | 1747 | } |
1402 | 1748 | ||
1403 | static void setup_string_in_str(struct in_str *i, const char *s) | 1749 | static char **expand_assignments(char **argv, int count) |
1404 | { | 1750 | { |
1405 | i->peek = static_peek; | 1751 | int i; |
1406 | i->get = static_get; | 1752 | char **p = NULL; |
1407 | #if ENABLE_HUSH_INTERACTIVE | 1753 | /* Expand assignments into one string each */ |
1408 | i->promptme = 1; | 1754 | for (i = 0; i < count; i++) { |
1409 | i->promptmode = 0; /* PS1 */ | 1755 | p = add_string_to_strings(p, expand_string_to_string(argv[i])); |
1410 | #endif | 1756 | } |
1411 | i->p = s; | 1757 | return p; |
1412 | i->eof_flag = 0; | ||
1413 | } | 1758 | } |
1414 | 1759 | ||
1415 | 1760 | ||
@@ -1469,22 +1814,104 @@ static void restore_redirects(int squirrel[]) | |||
1469 | } | 1814 | } |
1470 | } | 1815 | } |
1471 | 1816 | ||
1472 | static char **expand_assignments(char **argv, int count) | 1817 | |
1818 | #if !defined(DEBUG_CLEAN) | ||
1819 | #define free_pipe_list(head, indent) free_pipe_list(head) | ||
1820 | #define free_pipe(pi, indent) free_pipe(pi) | ||
1821 | #endif | ||
1822 | static int free_pipe_list(struct pipe *head, int indent); | ||
1823 | |||
1824 | /* return code is the exit status of the pipe */ | ||
1825 | static int free_pipe(struct pipe *pi, int indent) | ||
1473 | { | 1826 | { |
1474 | int i; | 1827 | char **p; |
1475 | char **p = NULL; | 1828 | struct command *command; |
1476 | /* Expand assignments into one string each */ | 1829 | struct redir_struct *r, *rnext; |
1477 | for (i = 0; i < count; i++) { | 1830 | int a, i, ret_code = 0; |
1478 | p = add_string_to_strings(p, expand_string_to_string(argv[i])); | 1831 | |
1832 | if (pi->stopped_cmds > 0) | ||
1833 | return ret_code; | ||
1834 | debug_printf_clean("%s run pipe: (pid %d)\n", indenter(indent), getpid()); | ||
1835 | for (i = 0; i < pi->num_cmds; i++) { | ||
1836 | command = &pi->cmds[i]; | ||
1837 | debug_printf_clean("%s command %d:\n", indenter(indent), i); | ||
1838 | if (command->argv) { | ||
1839 | for (a = 0, p = command->argv; *p; a++, p++) { | ||
1840 | debug_printf_clean("%s argv[%d] = %s\n", indenter(indent), a, *p); | ||
1841 | } | ||
1842 | free_strings(command->argv); | ||
1843 | command->argv = NULL; | ||
1844 | } else if (command->group) { | ||
1845 | debug_printf_clean("%s begin group (grp_type:%d)\n", indenter(indent), command->grp_type); | ||
1846 | ret_code = free_pipe_list(command->group, indent+3); | ||
1847 | debug_printf_clean("%s end group\n", indenter(indent)); | ||
1848 | } else { | ||
1849 | debug_printf_clean("%s (nil)\n", indenter(indent)); | ||
1850 | } | ||
1851 | for (r = command->redirects; r; r = rnext) { | ||
1852 | debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->rd_type].descrip); | ||
1853 | if (r->dup == -1) { | ||
1854 | /* guard against the case >$FOO, where foo is unset or blank */ | ||
1855 | if (r->rd_filename) { | ||
1856 | debug_printf_clean(" %s\n", r->rd_filename); | ||
1857 | free(r->rd_filename); | ||
1858 | r->rd_filename = NULL; | ||
1859 | } | ||
1860 | } else { | ||
1861 | debug_printf_clean("&%d\n", r->dup); | ||
1862 | } | ||
1863 | rnext = r->next; | ||
1864 | free(r); | ||
1865 | } | ||
1866 | command->redirects = NULL; | ||
1479 | } | 1867 | } |
1480 | return p; | 1868 | free(pi->cmds); /* children are an array, they get freed all at once */ |
1869 | pi->cmds = NULL; | ||
1870 | #if ENABLE_HUSH_JOB | ||
1871 | free(pi->cmdtext); | ||
1872 | pi->cmdtext = NULL; | ||
1873 | #endif | ||
1874 | return ret_code; | ||
1481 | } | 1875 | } |
1482 | 1876 | ||
1877 | static int free_pipe_list(struct pipe *head, int indent) | ||
1878 | { | ||
1879 | int rcode = 0; /* if list has no members */ | ||
1880 | struct pipe *pi, *next; | ||
1881 | |||
1882 | for (pi = head; pi; pi = next) { | ||
1883 | #if HAS_KEYWORDS | ||
1884 | debug_printf_clean("%s pipe reserved mode %d\n", indenter(indent), pi->res_word); | ||
1885 | #endif | ||
1886 | rcode = free_pipe(pi, indent); | ||
1887 | debug_printf_clean("%s pipe followup code %d\n", indenter(indent), pi->followup); | ||
1888 | next = pi->next; | ||
1889 | /*pi->next = NULL;*/ | ||
1890 | free(pi); | ||
1891 | } | ||
1892 | return rcode; | ||
1893 | } | ||
1894 | |||
1895 | |||
1896 | #if !BB_MMU | ||
1897 | typedef struct nommu_save_t { | ||
1898 | char **new_env; | ||
1899 | char **old_env; | ||
1900 | char **argv; | ||
1901 | } nommu_save_t; | ||
1902 | #else | ||
1903 | #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ | ||
1904 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) | ||
1905 | #define pseudo_exec(nommu_save, command, argv_expanded) \ | ||
1906 | pseudo_exec(command, argv_expanded) | ||
1907 | #endif | ||
1908 | |||
1483 | /* Called after [v]fork() in run_pipe(), or from builtin_exec(). | 1909 | /* Called after [v]fork() in run_pipe(), or from builtin_exec(). |
1484 | * Never returns. | 1910 | * Never returns. |
1485 | * XXX no exit() here. If you don't exec, use _exit instead. | 1911 | * XXX no exit() here. If you don't exec, use _exit instead. |
1486 | * The at_exit handlers apparently confuse the calling process, | 1912 | * The at_exit handlers apparently confuse the calling process, |
1487 | * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ | 1913 | * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ |
1914 | static void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignment_cnt, char **argv_expanded) NORETURN; | ||
1488 | static void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignment_cnt, char **argv_expanded) | 1915 | static void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignment_cnt, char **argv_expanded) |
1489 | { | 1916 | { |
1490 | int rcode; | 1917 | int rcode; |
@@ -1554,8 +1981,11 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignme | |||
1554 | _exit(EXIT_FAILURE); | 1981 | _exit(EXIT_FAILURE); |
1555 | } | 1982 | } |
1556 | 1983 | ||
1984 | static int run_list(struct pipe *pi); | ||
1985 | |||
1557 | /* Called after [v]fork() in run_pipe() | 1986 | /* Called after [v]fork() in run_pipe() |
1558 | */ | 1987 | */ |
1988 | static void pseudo_exec(nommu_save_t *nommu_save, struct command *command, char **argv_expanded) NORETURN; | ||
1559 | static void pseudo_exec(nommu_save_t *nommu_save, struct command *command, char **argv_expanded) | 1989 | static void pseudo_exec(nommu_save_t *nommu_save, struct command *command, char **argv_expanded) |
1560 | { | 1990 | { |
1561 | if (command->argv) | 1991 | if (command->argv) |
@@ -2461,77 +2891,6 @@ static int run_list(struct pipe *pi) | |||
2461 | return rcode; | 2891 | return rcode; |
2462 | } | 2892 | } |
2463 | 2893 | ||
2464 | /* return code is the exit status of the pipe */ | ||
2465 | static int free_pipe(struct pipe *pi, int indent) | ||
2466 | { | ||
2467 | char **p; | ||
2468 | struct command *command; | ||
2469 | struct redir_struct *r, *rnext; | ||
2470 | int a, i, ret_code = 0; | ||
2471 | |||
2472 | if (pi->stopped_cmds > 0) | ||
2473 | return ret_code; | ||
2474 | debug_printf_clean("%s run pipe: (pid %d)\n", indenter(indent), getpid()); | ||
2475 | for (i = 0; i < pi->num_cmds; i++) { | ||
2476 | command = &pi->cmds[i]; | ||
2477 | debug_printf_clean("%s command %d:\n", indenter(indent), i); | ||
2478 | if (command->argv) { | ||
2479 | for (a = 0, p = command->argv; *p; a++, p++) { | ||
2480 | debug_printf_clean("%s argv[%d] = %s\n", indenter(indent), a, *p); | ||
2481 | } | ||
2482 | free_strings(command->argv); | ||
2483 | command->argv = NULL; | ||
2484 | } else if (command->group) { | ||
2485 | debug_printf_clean("%s begin group (grp_type:%d)\n", indenter(indent), command->grp_type); | ||
2486 | ret_code = free_pipe_list(command->group, indent+3); | ||
2487 | debug_printf_clean("%s end group\n", indenter(indent)); | ||
2488 | } else { | ||
2489 | debug_printf_clean("%s (nil)\n", indenter(indent)); | ||
2490 | } | ||
2491 | for (r = command->redirects; r; r = rnext) { | ||
2492 | debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->rd_type].descrip); | ||
2493 | if (r->dup == -1) { | ||
2494 | /* guard against the case >$FOO, where foo is unset or blank */ | ||
2495 | if (r->rd_filename) { | ||
2496 | debug_printf_clean(" %s\n", r->rd_filename); | ||
2497 | free(r->rd_filename); | ||
2498 | r->rd_filename = NULL; | ||
2499 | } | ||
2500 | } else { | ||
2501 | debug_printf_clean("&%d\n", r->dup); | ||
2502 | } | ||
2503 | rnext = r->next; | ||
2504 | free(r); | ||
2505 | } | ||
2506 | command->redirects = NULL; | ||
2507 | } | ||
2508 | free(pi->cmds); /* children are an array, they get freed all at once */ | ||
2509 | pi->cmds = NULL; | ||
2510 | #if ENABLE_HUSH_JOB | ||
2511 | free(pi->cmdtext); | ||
2512 | pi->cmdtext = NULL; | ||
2513 | #endif | ||
2514 | return ret_code; | ||
2515 | } | ||
2516 | |||
2517 | static int free_pipe_list(struct pipe *head, int indent) | ||
2518 | { | ||
2519 | int rcode = 0; /* if list has no members */ | ||
2520 | struct pipe *pi, *next; | ||
2521 | |||
2522 | for (pi = head; pi; pi = next) { | ||
2523 | #if HAS_KEYWORDS | ||
2524 | debug_printf_clean("%s pipe reserved mode %d\n", indenter(indent), pi->res_word); | ||
2525 | #endif | ||
2526 | rcode = free_pipe(pi, indent); | ||
2527 | debug_printf_clean("%s pipe followup code %d\n", indenter(indent), pi->followup); | ||
2528 | next = pi->next; | ||
2529 | /*pi->next = NULL;*/ | ||
2530 | free(pi); | ||
2531 | } | ||
2532 | return rcode; | ||
2533 | } | ||
2534 | |||
2535 | /* Select which version we will use */ | 2894 | /* Select which version we will use */ |
2536 | static int run_and_free_list(struct pipe *pi) | 2895 | static int run_and_free_list(struct pipe *pi) |
2537 | { | 2896 | { |
@@ -2550,402 +2909,32 @@ static int run_and_free_list(struct pipe *pi) | |||
2550 | } | 2909 | } |
2551 | 2910 | ||
2552 | 2911 | ||
2553 | /* expand_strvec_to_strvec() takes a list of strings, expands | 2912 | /* Peek ahead in the in_str to find out if we have a "&n" construct, |
2554 | * all variable references within and returns a pointer to | 2913 | * as in "2>&1", that represents duplicating a file descriptor. |
2555 | * a list of expanded strings, possibly with larger number | 2914 | * Return either -2 (syntax error), -1 (no &), or the number found. |
2556 | * of strings. (Think VAR="a b"; echo $VAR). | 2915 | */ |
2557 | * This new list is allocated as a single malloc block. | 2916 | static int redirect_dup_num(struct in_str *input) |
2558 | * NULL-terminated list of char* pointers is at the beginning of it, | ||
2559 | * followed by strings themself. | ||
2560 | * Caller can deallocate entire list by single free(list). */ | ||
2561 | |||
2562 | /* Store given string, finalizing the word and starting new one whenever | ||
2563 | * we encounter IFS char(s). This is used for expanding variable values. | ||
2564 | * End-of-string does NOT finalize word: think about 'echo -$VAR-' */ | ||
2565 | static int expand_on_ifs(o_string *output, int n, const char *str) | ||
2566 | { | ||
2567 | while (1) { | ||
2568 | int word_len = strcspn(str, G.ifs); | ||
2569 | if (word_len) { | ||
2570 | if (output->o_quote || !output->o_glob) | ||
2571 | o_addQstr(output, str, word_len); | ||
2572 | else /* protect backslashes against globbing up :) */ | ||
2573 | o_addstr_duplicate_backslash(output, str, word_len); | ||
2574 | str += word_len; | ||
2575 | } | ||
2576 | if (!*str) /* EOL - do not finalize word */ | ||
2577 | break; | ||
2578 | o_addchr(output, '\0'); | ||
2579 | debug_print_list("expand_on_ifs", output, n); | ||
2580 | n = o_save_ptr(output, n); | ||
2581 | str += strspn(str, G.ifs); /* skip ifs chars */ | ||
2582 | } | ||
2583 | debug_print_list("expand_on_ifs[1]", output, n); | ||
2584 | return n; | ||
2585 | } | ||
2586 | |||
2587 | /* Expand all variable references in given string, adding words to list[] | ||
2588 | * at n, n+1,... positions. Return updated n (so that list[n] is next one | ||
2589 | * to be filled). This routine is extremely tricky: has to deal with | ||
2590 | * variables/parameters with whitespace, $* and $@, and constructs like | ||
2591 | * 'echo -$*-'. If you play here, you must run testsuite afterwards! */ | ||
2592 | static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | ||
2593 | { | ||
2594 | /* or_mask is either 0 (normal case) or 0x80 | ||
2595 | * (expansion of right-hand side of assignment == 1-element expand. | ||
2596 | * It will also do no globbing, and thus we must not backslash-quote!) */ | ||
2597 | |||
2598 | char first_ch, ored_ch; | ||
2599 | int i; | ||
2600 | const char *val; | ||
2601 | char *p; | ||
2602 | |||
2603 | ored_ch = 0; | ||
2604 | |||
2605 | debug_printf_expand("expand_vars_to_list: arg '%s'\n", arg); | ||
2606 | debug_print_list("expand_vars_to_list", output, n); | ||
2607 | n = o_save_ptr(output, n); | ||
2608 | debug_print_list("expand_vars_to_list[0]", output, n); | ||
2609 | |||
2610 | while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { | ||
2611 | #if ENABLE_HUSH_TICK | ||
2612 | o_string subst_result = NULL_O_STRING; | ||
2613 | #endif | ||
2614 | o_addstr(output, arg, p - arg); | ||
2615 | debug_print_list("expand_vars_to_list[1]", output, n); | ||
2616 | arg = ++p; | ||
2617 | p = strchr(p, SPECIAL_VAR_SYMBOL); | ||
2618 | |||
2619 | first_ch = arg[0] | or_mask; /* forced to "quoted" if or_mask = 0x80 */ | ||
2620 | /* "$@" is special. Even if quoted, it can still | ||
2621 | * expand to nothing (not even an empty string) */ | ||
2622 | if ((first_ch & 0x7f) != '@') | ||
2623 | ored_ch |= first_ch; | ||
2624 | val = NULL; | ||
2625 | switch (first_ch & 0x7f) { | ||
2626 | /* Highest bit in first_ch indicates that var is double-quoted */ | ||
2627 | case '$': /* pid */ | ||
2628 | val = utoa(G.root_pid); | ||
2629 | break; | ||
2630 | case '!': /* bg pid */ | ||
2631 | val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)""; | ||
2632 | break; | ||
2633 | case '?': /* exitcode */ | ||
2634 | val = utoa(G.last_return_code); | ||
2635 | break; | ||
2636 | case '#': /* argc */ | ||
2637 | val = utoa(G.global_argc ? G.global_argc-1 : 0); | ||
2638 | break; | ||
2639 | case '*': | ||
2640 | case '@': | ||
2641 | i = 1; | ||
2642 | if (!G.global_argv[i]) | ||
2643 | break; | ||
2644 | ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ | ||
2645 | if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ | ||
2646 | smallint sv = output->o_quote; | ||
2647 | /* unquoted var's contents should be globbed, so don't quote */ | ||
2648 | output->o_quote = 0; | ||
2649 | while (G.global_argv[i]) { | ||
2650 | n = expand_on_ifs(output, n, G.global_argv[i]); | ||
2651 | debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1); | ||
2652 | if (G.global_argv[i++][0] && G.global_argv[i]) { | ||
2653 | /* this argv[] is not empty and not last: | ||
2654 | * put terminating NUL, start new word */ | ||
2655 | o_addchr(output, '\0'); | ||
2656 | debug_print_list("expand_vars_to_list[2]", output, n); | ||
2657 | n = o_save_ptr(output, n); | ||
2658 | debug_print_list("expand_vars_to_list[3]", output, n); | ||
2659 | } | ||
2660 | } | ||
2661 | output->o_quote = sv; | ||
2662 | } else | ||
2663 | /* If or_mask is nonzero, we handle assignment 'a=....$@.....' | ||
2664 | * and in this case should treat it like '$*' - see 'else...' below */ | ||
2665 | if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */ | ||
2666 | while (1) { | ||
2667 | o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i])); | ||
2668 | if (++i >= G.global_argc) | ||
2669 | break; | ||
2670 | o_addchr(output, '\0'); | ||
2671 | debug_print_list("expand_vars_to_list[4]", output, n); | ||
2672 | n = o_save_ptr(output, n); | ||
2673 | } | ||
2674 | } else { /* quoted $*: add as one word */ | ||
2675 | while (1) { | ||
2676 | o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i])); | ||
2677 | if (!G.global_argv[++i]) | ||
2678 | break; | ||
2679 | if (G.ifs[0]) | ||
2680 | o_addchr(output, G.ifs[0]); | ||
2681 | } | ||
2682 | } | ||
2683 | break; | ||
2684 | case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */ | ||
2685 | /* "Empty variable", used to make "" etc to not disappear */ | ||
2686 | arg++; | ||
2687 | ored_ch = 0x80; | ||
2688 | break; | ||
2689 | #if ENABLE_HUSH_TICK | ||
2690 | case '`': { /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ | ||
2691 | struct in_str input; | ||
2692 | *p = '\0'; | ||
2693 | arg++; | ||
2694 | //TODO: can we just stuff it into "output" directly? | ||
2695 | debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); | ||
2696 | setup_string_in_str(&input, arg); | ||
2697 | process_command_subs(&subst_result, &input, NULL); | ||
2698 | debug_printf_subst("SUBST RES '%s'\n", subst_result.data); | ||
2699 | val = subst_result.data; | ||
2700 | goto store_val; | ||
2701 | } | ||
2702 | #endif | ||
2703 | default: /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */ | ||
2704 | *p = '\0'; | ||
2705 | arg[0] = first_ch & 0x7f; | ||
2706 | if (isdigit(arg[0])) { | ||
2707 | i = xatoi_u(arg); | ||
2708 | if (i < G.global_argc) | ||
2709 | val = G.global_argv[i]; | ||
2710 | /* else val remains NULL: $N with too big N */ | ||
2711 | } else | ||
2712 | val = lookup_param(arg); | ||
2713 | arg[0] = first_ch; | ||
2714 | #if ENABLE_HUSH_TICK | ||
2715 | store_val: | ||
2716 | #endif | ||
2717 | *p = SPECIAL_VAR_SYMBOL; | ||
2718 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ | ||
2719 | debug_printf_expand("unquoted '%s', output->o_quote:%d\n", val, output->o_quote); | ||
2720 | if (val) { | ||
2721 | /* unquoted var's contents should be globbed, so don't quote */ | ||
2722 | smallint sv = output->o_quote; | ||
2723 | output->o_quote = 0; | ||
2724 | n = expand_on_ifs(output, n, val); | ||
2725 | val = NULL; | ||
2726 | output->o_quote = sv; | ||
2727 | } | ||
2728 | } else { /* quoted $VAR, val will be appended below */ | ||
2729 | debug_printf_expand("quoted '%s', output->o_quote:%d\n", val, output->o_quote); | ||
2730 | } | ||
2731 | } | ||
2732 | if (val) { | ||
2733 | o_addQstr(output, val, strlen(val)); | ||
2734 | } | ||
2735 | |||
2736 | #if ENABLE_HUSH_TICK | ||
2737 | o_free(&subst_result); | ||
2738 | #endif | ||
2739 | arg = ++p; | ||
2740 | } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */ | ||
2741 | |||
2742 | if (arg[0]) { | ||
2743 | debug_print_list("expand_vars_to_list[a]", output, n); | ||
2744 | /* this part is literal, and it was already pre-quoted | ||
2745 | * if needed (much earlier), do not use o_addQstr here! */ | ||
2746 | o_addstr(output, arg, strlen(arg) + 1); | ||
2747 | debug_print_list("expand_vars_to_list[b]", output, n); | ||
2748 | } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ | ||
2749 | && !(ored_ch & 0x80) /* and all vars were not quoted. */ | ||
2750 | ) { | ||
2751 | n--; | ||
2752 | /* allow to reuse list[n] later without re-growth */ | ||
2753 | output->has_empty_slot = 1; | ||
2754 | } else { | ||
2755 | o_addchr(output, '\0'); | ||
2756 | } | ||
2757 | return n; | ||
2758 | } | ||
2759 | |||
2760 | static char **expand_variables(char **argv, int or_mask) | ||
2761 | { | ||
2762 | int n; | ||
2763 | char **list; | ||
2764 | char **v; | ||
2765 | o_string output = NULL_O_STRING; | ||
2766 | |||
2767 | if (or_mask & 0x100) { | ||
2768 | output.o_quote = 1; /* protect against globbing for "$var" */ | ||
2769 | /* (unquoted $var will temporarily switch it off) */ | ||
2770 | output.o_glob = 1; | ||
2771 | } | ||
2772 | |||
2773 | n = 0; | ||
2774 | v = argv; | ||
2775 | while (*v) { | ||
2776 | n = expand_vars_to_list(&output, n, *v, (char)or_mask); | ||
2777 | v++; | ||
2778 | } | ||
2779 | debug_print_list("expand_variables", &output, n); | ||
2780 | |||
2781 | /* output.data (malloced in one block) gets returned in "list" */ | ||
2782 | list = o_finalize_list(&output, n); | ||
2783 | debug_print_strings("expand_variables[1]", list); | ||
2784 | return list; | ||
2785 | } | ||
2786 | |||
2787 | static char **expand_strvec_to_strvec(char **argv) | ||
2788 | { | ||
2789 | return expand_variables(argv, 0x100); | ||
2790 | } | ||
2791 | |||
2792 | /* Used for expansion of right hand of assignments */ | ||
2793 | /* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs | ||
2794 | * "v=/bin/c*" */ | ||
2795 | static char *expand_string_to_string(const char *str) | ||
2796 | { | ||
2797 | char *argv[2], **list; | ||
2798 | |||
2799 | argv[0] = (char*)str; | ||
2800 | argv[1] = NULL; | ||
2801 | list = expand_variables(argv, 0x80); /* 0x80: make one-element expansion */ | ||
2802 | if (HUSH_DEBUG) | ||
2803 | if (!list[0] || list[1]) | ||
2804 | bb_error_msg_and_die("BUG in varexp2"); | ||
2805 | /* actually, just move string 2*sizeof(char*) bytes back */ | ||
2806 | overlapping_strcpy((char*)list, list[0]); | ||
2807 | debug_printf_expand("string_to_string='%s'\n", (char*)list); | ||
2808 | return (char*)list; | ||
2809 | } | ||
2810 | |||
2811 | /* Used for "eval" builtin */ | ||
2812 | static char* expand_strvec_to_string(char **argv) | ||
2813 | { | ||
2814 | char **list; | ||
2815 | |||
2816 | list = expand_variables(argv, 0x80); | ||
2817 | /* Convert all NULs to spaces */ | ||
2818 | if (list[0]) { | ||
2819 | int n = 1; | ||
2820 | while (list[n]) { | ||
2821 | if (HUSH_DEBUG) | ||
2822 | if (list[n-1] + strlen(list[n-1]) + 1 != list[n]) | ||
2823 | bb_error_msg_and_die("BUG in varexp3"); | ||
2824 | list[n][-1] = ' '; /* TODO: or to G.ifs[0]? */ | ||
2825 | n++; | ||
2826 | } | ||
2827 | } | ||
2828 | overlapping_strcpy((char*)list, list[0]); | ||
2829 | debug_printf_expand("strvec_to_string='%s'\n", (char*)list); | ||
2830 | return (char*)list; | ||
2831 | } | ||
2832 | |||
2833 | |||
2834 | /* Used to get/check local shell variables */ | ||
2835 | static struct variable *get_local_var(const char *name) | ||
2836 | { | ||
2837 | struct variable *cur; | ||
2838 | int len; | ||
2839 | |||
2840 | if (!name) | ||
2841 | return NULL; | ||
2842 | len = strlen(name); | ||
2843 | for (cur = G.top_var; cur; cur = cur->next) { | ||
2844 | if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=') | ||
2845 | return cur; | ||
2846 | } | ||
2847 | return NULL; | ||
2848 | } | ||
2849 | |||
2850 | /* str holds "NAME=VAL" and is expected to be malloced. | ||
2851 | * We take ownership of it. */ | ||
2852 | static int set_local_var(char *str, int flg_export) | ||
2853 | { | 2917 | { |
2854 | struct variable *cur; | 2918 | int ch, d = 0, ok = 0; |
2855 | char *value; | 2919 | ch = i_peek(input); |
2856 | int name_len; | 2920 | if (ch != '&') return -1; |
2857 | |||
2858 | value = strchr(str, '='); | ||
2859 | if (!value) { /* not expected to ever happen? */ | ||
2860 | free(str); | ||
2861 | return -1; | ||
2862 | } | ||
2863 | 2921 | ||
2864 | name_len = value - str + 1; /* including '=' */ | 2922 | i_getch(input); /* get the & */ |
2865 | cur = G.top_var; /* cannot be NULL (we have HUSH_VERSION and it's RO) */ | 2923 | ch = i_peek(input); |
2866 | while (1) { | 2924 | if (ch == '-') { |
2867 | if (strncmp(cur->varstr, str, name_len) != 0) { | 2925 | i_getch(input); |
2868 | if (!cur->next) { | 2926 | return -3; /* "-" represents "close me" */ |
2869 | /* Bail out. Note that now cur points | ||
2870 | * to last var in linked list */ | ||
2871 | break; | ||
2872 | } | ||
2873 | cur = cur->next; | ||
2874 | continue; | ||
2875 | } | ||
2876 | /* We found an existing var with this name */ | ||
2877 | *value = '\0'; | ||
2878 | if (cur->flg_read_only) { | ||
2879 | bb_error_msg("%s: readonly variable", str); | ||
2880 | free(str); | ||
2881 | return -1; | ||
2882 | } | ||
2883 | debug_printf_env("%s: unsetenv '%s'\n", __func__, str); | ||
2884 | unsetenv(str); /* just in case */ | ||
2885 | *value = '='; | ||
2886 | if (strcmp(cur->varstr, str) == 0) { | ||
2887 | free_and_exp: | ||
2888 | free(str); | ||
2889 | goto exp; | ||
2890 | } | ||
2891 | if (cur->max_len >= strlen(str)) { | ||
2892 | /* This one is from startup env, reuse space */ | ||
2893 | strcpy(cur->varstr, str); | ||
2894 | goto free_and_exp; | ||
2895 | } | ||
2896 | /* max_len == 0 signifies "malloced" var, which we can | ||
2897 | * (and has to) free */ | ||
2898 | if (!cur->max_len) | ||
2899 | free(cur->varstr); | ||
2900 | cur->max_len = 0; | ||
2901 | goto set_str_and_exp; | ||
2902 | } | 2927 | } |
2903 | 2928 | while (isdigit(ch)) { | |
2904 | /* Not found - create next variable struct */ | 2929 | d = d*10 + (ch-'0'); |
2905 | cur->next = xzalloc(sizeof(*cur)); | 2930 | ok = 1; |
2906 | cur = cur->next; | 2931 | i_getch(input); |
2907 | 2932 | ch = i_peek(input); | |
2908 | set_str_and_exp: | ||
2909 | cur->varstr = str; | ||
2910 | exp: | ||
2911 | if (flg_export) | ||
2912 | cur->flg_export = 1; | ||
2913 | if (cur->flg_export) { | ||
2914 | debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); | ||
2915 | return putenv(cur->varstr); | ||
2916 | } | 2933 | } |
2917 | return 0; | 2934 | if (ok) return d; |
2918 | } | ||
2919 | |||
2920 | static void unset_local_var(const char *name) | ||
2921 | { | ||
2922 | struct variable *cur; | ||
2923 | struct variable *prev = prev; /* for gcc */ | ||
2924 | int name_len; | ||
2925 | 2935 | ||
2926 | if (!name) | 2936 | bb_error_msg("ambiguous redirect"); |
2927 | return; | 2937 | return -2; |
2928 | name_len = strlen(name); | ||
2929 | cur = G.top_var; | ||
2930 | while (cur) { | ||
2931 | if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { | ||
2932 | if (cur->flg_read_only) { | ||
2933 | bb_error_msg("%s: readonly variable", name); | ||
2934 | return; | ||
2935 | } | ||
2936 | /* prev is ok to use here because 1st variable, HUSH_VERSION, | ||
2937 | * is ro, and we cannot reach this code on the 1st pass */ | ||
2938 | prev->next = cur->next; | ||
2939 | debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr); | ||
2940 | bb_unsetenv(cur->varstr); | ||
2941 | if (!cur->max_len) | ||
2942 | free(cur->varstr); | ||
2943 | free(cur); | ||
2944 | return; | ||
2945 | } | ||
2946 | prev = cur; | ||
2947 | cur = cur->next; | ||
2948 | } | ||
2949 | } | 2938 | } |
2950 | 2939 | ||
2951 | /* The src parameter allows us to peek forward to a possible &n syntax | 2940 | /* The src parameter allows us to peek forward to a possible &n syntax |
@@ -2997,6 +2986,7 @@ static int setup_redirect(struct parse_context *ctx, int fd, redir_type style, | |||
2997 | return 0; | 2986 | return 0; |
2998 | } | 2987 | } |
2999 | 2988 | ||
2989 | |||
3000 | static struct pipe *new_pipe(void) | 2990 | static struct pipe *new_pipe(void) |
3001 | { | 2991 | { |
3002 | struct pipe *pi; | 2992 | struct pipe *pi; |
@@ -3006,6 +2996,91 @@ static struct pipe *new_pipe(void) | |||
3006 | return pi; | 2996 | return pi; |
3007 | } | 2997 | } |
3008 | 2998 | ||
2999 | /* Command (member of a pipe) is complete. The only possible error here | ||
3000 | * is out of memory, in which case xmalloc exits. */ | ||
3001 | static int done_command(struct parse_context *ctx) | ||
3002 | { | ||
3003 | /* The command is really already in the pipe structure, so | ||
3004 | * advance the pipe counter and make a new, null command. */ | ||
3005 | struct pipe *pi = ctx->pipe; | ||
3006 | struct command *command = ctx->command; | ||
3007 | |||
3008 | if (command) { | ||
3009 | if (command->group == NULL | ||
3010 | && command->argv == NULL | ||
3011 | && command->redirects == NULL | ||
3012 | ) { | ||
3013 | debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); | ||
3014 | return pi->num_cmds; | ||
3015 | } | ||
3016 | pi->num_cmds++; | ||
3017 | debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds); | ||
3018 | } else { | ||
3019 | debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds); | ||
3020 | } | ||
3021 | |||
3022 | /* Only real trickiness here is that the uncommitted | ||
3023 | * command structure is not counted in pi->num_cmds. */ | ||
3024 | pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1)); | ||
3025 | command = &pi->cmds[pi->num_cmds]; | ||
3026 | memset(command, 0, sizeof(*command)); | ||
3027 | |||
3028 | ctx->command = command; | ||
3029 | /* but ctx->pipe and ctx->list_head remain unchanged */ | ||
3030 | |||
3031 | return pi->num_cmds; /* used only for 0/nonzero check */ | ||
3032 | } | ||
3033 | |||
3034 | static void done_pipe(struct parse_context *ctx, pipe_style type) | ||
3035 | { | ||
3036 | int not_null; | ||
3037 | |||
3038 | debug_printf_parse("done_pipe entered, followup %d\n", type); | ||
3039 | /* Close previous command */ | ||
3040 | not_null = done_command(ctx); | ||
3041 | ctx->pipe->followup = type; | ||
3042 | IF_HAS_KEYWORDS(ctx->pipe->pi_inverted = ctx->ctx_inverted;) | ||
3043 | IF_HAS_KEYWORDS(ctx->ctx_inverted = 0;) | ||
3044 | IF_HAS_KEYWORDS(ctx->pipe->res_word = ctx->ctx_res_w;) | ||
3045 | |||
3046 | /* Without this check, even just <enter> on command line generates | ||
3047 | * tree of three NOPs (!). Which is harmless but annoying. | ||
3048 | * IOW: it is safe to do it unconditionally. | ||
3049 | * RES_NONE case is for "for a in; do ..." (empty IN set) | ||
3050 | * to work, possibly other cases too. */ | ||
3051 | if (not_null IF_HAS_KEYWORDS(|| ctx->ctx_res_w != RES_NONE)) { | ||
3052 | struct pipe *new_p; | ||
3053 | debug_printf_parse("done_pipe: adding new pipe: " | ||
3054 | "not_null:%d ctx->ctx_res_w:%d\n", | ||
3055 | not_null, ctx->ctx_res_w); | ||
3056 | new_p = new_pipe(); | ||
3057 | ctx->pipe->next = new_p; | ||
3058 | ctx->pipe = new_p; | ||
3059 | ctx->command = NULL; /* needed! */ | ||
3060 | /* RES_THEN, RES_DO etc are "sticky" - | ||
3061 | * they remain set for commands inside if/while. | ||
3062 | * This is used to control execution. | ||
3063 | * RES_FOR and RES_IN are NOT sticky (needed to support | ||
3064 | * cases where variable or value happens to match a keyword): | ||
3065 | */ | ||
3066 | #if ENABLE_HUSH_LOOPS | ||
3067 | if (ctx->ctx_res_w == RES_FOR | ||
3068 | || ctx->ctx_res_w == RES_IN) | ||
3069 | ctx->ctx_res_w = RES_NONE; | ||
3070 | #endif | ||
3071 | #if ENABLE_HUSH_CASE | ||
3072 | if (ctx->ctx_res_w == RES_MATCH) | ||
3073 | ctx->ctx_res_w = RES_CASEI; | ||
3074 | #endif | ||
3075 | /* Create the memory for command, roughly: | ||
3076 | * ctx->pipe->cmds = new struct command; | ||
3077 | * ctx->command = &ctx->pipe->cmds[0]; | ||
3078 | */ | ||
3079 | done_command(ctx); | ||
3080 | } | ||
3081 | debug_printf_parse("done_pipe return\n"); | ||
3082 | } | ||
3083 | |||
3009 | static void initialize_context(struct parse_context *ctx) | 3084 | static void initialize_context(struct parse_context *ctx) |
3010 | { | 3085 | { |
3011 | memset(ctx, 0, sizeof(*ctx)); | 3086 | memset(ctx, 0, sizeof(*ctx)); |
@@ -3017,6 +3092,7 @@ static void initialize_context(struct parse_context *ctx) | |||
3017 | done_command(ctx); | 3092 | done_command(ctx); |
3018 | } | 3093 | } |
3019 | 3094 | ||
3095 | |||
3020 | /* If a reserved word is found and processed, parse context is modified | 3096 | /* If a reserved word is found and processed, parse context is modified |
3021 | * and 1 is returned. | 3097 | * and 1 is returned. |
3022 | * Handles if, then, elif, else, fi, for, while, until, do, done. | 3098 | * Handles if, then, elif, else, fi, for, while, until, do, done. |
@@ -3260,119 +3336,6 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3260 | return 0; | 3336 | return 0; |
3261 | } | 3337 | } |
3262 | 3338 | ||
3263 | /* Command (member of a pipe) is complete. The only possible error here | ||
3264 | * is out of memory, in which case xmalloc exits. */ | ||
3265 | static int done_command(struct parse_context *ctx) | ||
3266 | { | ||
3267 | /* The command is really already in the pipe structure, so | ||
3268 | * advance the pipe counter and make a new, null command. */ | ||
3269 | struct pipe *pi = ctx->pipe; | ||
3270 | struct command *command = ctx->command; | ||
3271 | |||
3272 | if (command) { | ||
3273 | if (command->group == NULL | ||
3274 | && command->argv == NULL | ||
3275 | && command->redirects == NULL | ||
3276 | ) { | ||
3277 | debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); | ||
3278 | return pi->num_cmds; | ||
3279 | } | ||
3280 | pi->num_cmds++; | ||
3281 | debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds); | ||
3282 | } else { | ||
3283 | debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds); | ||
3284 | } | ||
3285 | |||
3286 | /* Only real trickiness here is that the uncommitted | ||
3287 | * command structure is not counted in pi->num_cmds. */ | ||
3288 | pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1)); | ||
3289 | command = &pi->cmds[pi->num_cmds]; | ||
3290 | memset(command, 0, sizeof(*command)); | ||
3291 | |||
3292 | ctx->command = command; | ||
3293 | /* but ctx->pipe and ctx->list_head remain unchanged */ | ||
3294 | |||
3295 | return pi->num_cmds; /* used only for 0/nonzero check */ | ||
3296 | } | ||
3297 | |||
3298 | static void done_pipe(struct parse_context *ctx, pipe_style type) | ||
3299 | { | ||
3300 | int not_null; | ||
3301 | |||
3302 | debug_printf_parse("done_pipe entered, followup %d\n", type); | ||
3303 | /* Close previous command */ | ||
3304 | not_null = done_command(ctx); | ||
3305 | ctx->pipe->followup = type; | ||
3306 | IF_HAS_KEYWORDS(ctx->pipe->pi_inverted = ctx->ctx_inverted;) | ||
3307 | IF_HAS_KEYWORDS(ctx->ctx_inverted = 0;) | ||
3308 | IF_HAS_KEYWORDS(ctx->pipe->res_word = ctx->ctx_res_w;) | ||
3309 | |||
3310 | /* Without this check, even just <enter> on command line generates | ||
3311 | * tree of three NOPs (!). Which is harmless but annoying. | ||
3312 | * IOW: it is safe to do it unconditionally. | ||
3313 | * RES_NONE case is for "for a in; do ..." (empty IN set) | ||
3314 | * to work, possibly other cases too. */ | ||
3315 | if (not_null IF_HAS_KEYWORDS(|| ctx->ctx_res_w != RES_NONE)) { | ||
3316 | struct pipe *new_p; | ||
3317 | debug_printf_parse("done_pipe: adding new pipe: " | ||
3318 | "not_null:%d ctx->ctx_res_w:%d\n", | ||
3319 | not_null, ctx->ctx_res_w); | ||
3320 | new_p = new_pipe(); | ||
3321 | ctx->pipe->next = new_p; | ||
3322 | ctx->pipe = new_p; | ||
3323 | ctx->command = NULL; /* needed! */ | ||
3324 | /* RES_THEN, RES_DO etc are "sticky" - | ||
3325 | * they remain set for commands inside if/while. | ||
3326 | * This is used to control execution. | ||
3327 | * RES_FOR and RES_IN are NOT sticky (needed to support | ||
3328 | * cases where variable or value happens to match a keyword): | ||
3329 | */ | ||
3330 | #if ENABLE_HUSH_LOOPS | ||
3331 | if (ctx->ctx_res_w == RES_FOR | ||
3332 | || ctx->ctx_res_w == RES_IN) | ||
3333 | ctx->ctx_res_w = RES_NONE; | ||
3334 | #endif | ||
3335 | #if ENABLE_HUSH_CASE | ||
3336 | if (ctx->ctx_res_w == RES_MATCH) | ||
3337 | ctx->ctx_res_w = RES_CASEI; | ||
3338 | #endif | ||
3339 | /* Create the memory for command, roughly: | ||
3340 | * ctx->pipe->cmds = new struct command; | ||
3341 | * ctx->command = &ctx->pipe->cmds[0]; | ||
3342 | */ | ||
3343 | done_command(ctx); | ||
3344 | } | ||
3345 | debug_printf_parse("done_pipe return\n"); | ||
3346 | } | ||
3347 | |||
3348 | /* Peek ahead in the in_str to find out if we have a "&n" construct, | ||
3349 | * as in "2>&1", that represents duplicating a file descriptor. | ||
3350 | * Return either -2 (syntax error), -1 (no &), or the number found. | ||
3351 | */ | ||
3352 | static int redirect_dup_num(struct in_str *input) | ||
3353 | { | ||
3354 | int ch, d = 0, ok = 0; | ||
3355 | ch = i_peek(input); | ||
3356 | if (ch != '&') return -1; | ||
3357 | |||
3358 | i_getch(input); /* get the & */ | ||
3359 | ch = i_peek(input); | ||
3360 | if (ch == '-') { | ||
3361 | i_getch(input); | ||
3362 | return -3; /* "-" represents "close me" */ | ||
3363 | } | ||
3364 | while (isdigit(ch)) { | ||
3365 | d = d*10 + (ch-'0'); | ||
3366 | ok = 1; | ||
3367 | i_getch(input); | ||
3368 | ch = i_peek(input); | ||
3369 | } | ||
3370 | if (ok) return d; | ||
3371 | |||
3372 | bb_error_msg("ambiguous redirect"); | ||
3373 | return -2; | ||
3374 | } | ||
3375 | |||
3376 | /* If a redirect is immediately preceded by a number, that number is | 3339 | /* If a redirect is immediately preceded by a number, that number is |
3377 | * supposed to tell which file descriptor to redirect. This routine | 3340 | * supposed to tell which file descriptor to redirect. This routine |
3378 | * looks for such preceding numbers. In an ideal world this routine | 3341 | * looks for such preceding numbers. In an ideal world this routine |
@@ -3444,6 +3407,9 @@ static FILE *generate_stream_from_list(struct pipe *head) | |||
3444 | /* 'head' is freed by the caller */ | 3407 | /* 'head' is freed by the caller */ |
3445 | } | 3408 | } |
3446 | 3409 | ||
3410 | static int parse_stream(o_string *dest, struct parse_context *ctx, | ||
3411 | struct in_str *input0, const char *end_trigger); | ||
3412 | |||
3447 | /* Return code is exit status of the process that is run. */ | 3413 | /* Return code is exit status of the process that is run. */ |
3448 | static int process_command_subs(o_string *dest, | 3414 | static int process_command_subs(o_string *dest, |
3449 | struct in_str *input, | 3415 | struct in_str *input, |
@@ -3544,16 +3510,6 @@ static int parse_group(o_string *dest, struct parse_context *ctx, | |||
3544 | /* command remains "open", available for possible redirects */ | 3510 | /* command remains "open", available for possible redirects */ |
3545 | } | 3511 | } |
3546 | 3512 | ||
3547 | /* Basically useful version until someone wants to get fancier, | ||
3548 | * see the bash man page under "Parameter Expansion" */ | ||
3549 | static const char *lookup_param(const char *src) | ||
3550 | { | ||
3551 | struct variable *var = get_local_var(src); | ||
3552 | if (var) | ||
3553 | return strchr(var->varstr, '=') + 1; | ||
3554 | return NULL; | ||
3555 | } | ||
3556 | |||
3557 | #if ENABLE_HUSH_TICK | 3513 | #if ENABLE_HUSH_TICK |
3558 | /* Subroutines for copying $(...) and `...` things */ | 3514 | /* Subroutines for copying $(...) and `...` things */ |
3559 | static void add_till_backquote(o_string *dest, struct in_str *input); | 3515 | static void add_till_backquote(o_string *dest, struct in_str *input); |
@@ -3761,7 +3717,7 @@ static int handle_dollar(o_string *dest, struct in_str *input) | |||
3761 | * -1 on EOF (but if end_trigger == NULL then return 0), | 3717 | * -1 on EOF (but if end_trigger == NULL then return 0), |
3762 | * 1 for syntax error */ | 3718 | * 1 for syntax error */ |
3763 | static int parse_stream(o_string *dest, struct parse_context *ctx, | 3719 | static int parse_stream(o_string *dest, struct parse_context *ctx, |
3764 | struct in_str *input, const char *end_trigger) | 3720 | struct in_str *input, const char *end_trigger) |
3765 | { | 3721 | { |
3766 | int ch, m; | 3722 | int ch, m; |
3767 | int redir_fd; | 3723 | int redir_fd; |
@@ -4680,45 +4636,49 @@ static int builtin_read(char **argv) | |||
4680 | */ | 4636 | */ |
4681 | static int builtin_set(char **argv) | 4637 | static int builtin_set(char **argv) |
4682 | { | 4638 | { |
4683 | struct variable *e; | 4639 | int n; |
4684 | char **pp; | 4640 | char **pp, **g_argv; |
4685 | char *arg = *++argv; | 4641 | char *arg = *++argv; |
4686 | 4642 | ||
4687 | if (arg == NULL) { | 4643 | if (arg == NULL) { |
4644 | struct variable *e; | ||
4688 | for (e = G.top_var; e; e = e->next) | 4645 | for (e = G.top_var; e; e = e->next) |
4689 | puts(e->varstr); | 4646 | puts(e->varstr); |
4690 | } else { | 4647 | return EXIT_SUCCESS; |
4691 | /* NB: G.global_argv[0] ($0) is never freed/changed */ | 4648 | } |
4692 | 4649 | ||
4693 | if (G.global_args_malloced) { | 4650 | do { |
4694 | pp = G.global_argv; | 4651 | if (arg[0] == '+') |
4695 | while (*++pp) | 4652 | continue; |
4696 | free(*pp); | 4653 | if (arg[0] != '-') |
4697 | G.global_argv[1] = NULL; | 4654 | break; |
4698 | } else { | 4655 | if (arg[1] == '-' && arg[2] == '\0') { |
4699 | G.global_args_malloced = 1; | 4656 | argv++; |
4700 | pp = xzalloc(sizeof(pp[0]) * 2); | 4657 | break; |
4701 | pp[0] = G.global_argv[0]; /* retain $0 */ | ||
4702 | G.global_argv = pp; | ||
4703 | } | 4658 | } |
4704 | do { | 4659 | } while ((arg = *++argv) != NULL); |
4705 | if (arg[0] == '+') | 4660 | /* Now argv[0] is 1st argument */ |
4706 | continue; | ||
4707 | if (arg[0] != '-') | ||
4708 | break; | ||
4709 | if (arg[1] == '-' && arg[2] == '\0') { | ||
4710 | argv++; | ||
4711 | break; | ||
4712 | } | ||
4713 | } while ((arg = *++argv) != NULL); | ||
4714 | /* Now argv[0] is 1st argument */ | ||
4715 | 4661 | ||
4716 | /* This realloc's G.global_argv */ | 4662 | /* NB: G.global_argv[0] ($0) is never freed/changed */ |
4717 | G.global_argv = pp = add_strings_to_strings(G.global_argv, argv, /*dup:*/ 1); | 4663 | g_argv = G.global_argv; |
4718 | G.global_argc = 1; | 4664 | if (G.global_args_malloced) { |
4665 | pp = g_argv; | ||
4719 | while (*++pp) | 4666 | while (*++pp) |
4720 | G.global_argc++; | 4667 | free(*pp); |
4668 | g_argv[1] = NULL; | ||
4669 | } else { | ||
4670 | G.global_args_malloced = 1; | ||
4671 | pp = xzalloc(sizeof(pp[0]) * 2); | ||
4672 | pp[0] = g_argv[0]; /* retain $0 */ | ||
4673 | g_argv = pp; | ||
4721 | } | 4674 | } |
4675 | /* This realloc's G.global_argv */ | ||
4676 | G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1); | ||
4677 | |||
4678 | n = 1; | ||
4679 | while (*++pp) | ||
4680 | n++; | ||
4681 | G.global_argc = n; | ||
4722 | 4682 | ||
4723 | return EXIT_SUCCESS; | 4683 | return EXIT_SUCCESS; |
4724 | } | 4684 | } |