summaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-05-25 02:16:25 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-05-25 02:16:25 +0000
commitd76c049cc4b1da8d5b42f0013ecdcb4d592c9282 (patch)
tree89ada1134f9e9dd148160b291ba6a142c00127e2 /shell/hush.c
parent163a8557310911bcf8b852a282c0aa855b778058 (diff)
downloadbusybox-w32-d76c049cc4b1da8d5b42f0013ecdcb4d592c9282.tar.gz
busybox-w32-d76c049cc4b1da8d5b42f0013ecdcb4d592c9282.tar.bz2
busybox-w32-d76c049cc4b1da8d5b42f0013ecdcb4d592c9282.zip
hush: rework variable storage and environment handling.
More that -100 bytes of code + memory leak plugged. Added a testcase for it.
Diffstat (limited to 'shell/hush.c')
-rw-r--r--shell/hush.c330
1 files changed, 163 insertions, 167 deletions
diff --git a/shell/hush.c b/shell/hush.c
index d96615d54..0b8e7d68d 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -37,7 +37,6 @@
37 * across continuation lines. 37 * across continuation lines.
38 * 38 *
39 * Bash grammar not implemented: (how many of these were in original sh?) 39 * Bash grammar not implemented: (how many of these were in original sh?)
40 * $@ (those sure look like weird quoting rules)
41 * $_ 40 * $_
42 * ! negation operator for pipes 41 * ! negation operator for pipes
43 * &> and >& redirection of stdout+stderr 42 * &> and >& redirection of stdout+stderr
@@ -279,11 +278,18 @@ struct close_me {
279 int fd; 278 int fd;
280}; 279};
281 280
282struct variables { 281/* On program start, environ points to initial environment.
283 struct variables *next; 282 * putenv adds new pointers into it, unsetenv removes them.
284 const char *name; 283 * Neither of these (de)allocates the strings.
285 const char *value; 284 * setenv allocates new strings in malloc space and does putenv,
286 smallint flg_export; 285 * and thus setenv is unusable (leaky) for shell's purposes */
286#define setenv(...) setenv_is_leaky_dont_use()
287struct variable {
288 struct variable *next;
289 char *name; /* points to "name=" portion */
290 char *value; /* points directly after "=" */
291 int max_len; /* if > 0, name is part of initial env; else name is malloced */
292 smallint flg_export; /* putenv should be done on this var */
287 smallint flg_read_only; 293 smallint flg_read_only;
288}; 294};
289 295
@@ -322,14 +328,21 @@ enum {
322 CHAR_SPECIAL = 3, /* example: $ */ 328 CHAR_SPECIAL = 3, /* example: $ */
323}; 329};
324 330
331#define HUSH_VER_STR "0.02"
325 332
326/* "Globals" within this file */ 333static const char version_str[] = "HUSH_VERSION="HUSH_VER_STR;
327 334
328#define HUSH_VER_STR "0.02" 335static const struct variable const_shell_ver = {
329static const struct variables const_shell_ver = { 336 .next = NULL,
330 NULL, "HUSH_VERSION", HUSH_VER_STR, 1, 1 337 .name = (char*)version_str,
338 .value = (char*)version_str + sizeof("HUSH_VERSION=")-1,
339 .max_len = 1, /* 0 can provoke free(name) */
340 .flg_export = 1,
341 .flg_read_only = 1,
331}; 342};
332 343
344/* "Globals" within this file */
345
333/* Sorted roughly by size (smaller offsets == smaller code) */ 346/* Sorted roughly by size (smaller offsets == smaller code) */
334struct globals { 347struct globals {
335#if ENABLE_HUSH_INTERACTIVE 348#if ENABLE_HUSH_INTERACTIVE
@@ -360,8 +373,8 @@ struct globals {
360 struct close_me *close_me_head; 373 struct close_me *close_me_head;
361 const char *cwd; 374 const char *cwd;
362 unsigned last_bg_pid; 375 unsigned last_bg_pid;
363 struct variables *top_vars; /* = &shell_ver (both are set in main()) */ 376 struct variable *top_var; /* = &shell_ver (both are set in main()) */
364 struct variables shell_ver; /* = const_shell_ver */ 377 struct variable shell_ver; /* = const_shell_ver */
365#if ENABLE_FEATURE_SH_STANDALONE 378#if ENABLE_FEATURE_SH_STANDALONE
366 struct nofork_save_area nofork_save; 379 struct nofork_save_area nofork_save;
367#endif 380#endif
@@ -407,7 +420,7 @@ enum { run_list_level = 0 };
407#define close_me_head (G.close_me_head ) 420#define close_me_head (G.close_me_head )
408#define cwd (G.cwd ) 421#define cwd (G.cwd )
409#define last_bg_pid (G.last_bg_pid ) 422#define last_bg_pid (G.last_bg_pid )
410#define top_vars (G.top_vars ) 423#define top_var (G.top_var )
411#define shell_ver (G.shell_ver ) 424#define shell_ver (G.shell_ver )
412#if ENABLE_FEATURE_SH_STANDALONE 425#if ENABLE_FEATURE_SH_STANDALONE
413#define nofork_save (G.nofork_save ) 426#define nofork_save (G.nofork_save )
@@ -541,8 +554,8 @@ static char **expand_strvec_to_strvec(char **argv);
541static char *expand_strvec_to_string(char **argv); 554static char *expand_strvec_to_string(char **argv);
542/* used for expansion of right hand of assignments */ 555/* used for expansion of right hand of assignments */
543static char *expand_string_to_string(const char *str); 556static char *expand_string_to_string(const char *str);
544static const char *get_local_var(const char *var); 557static struct variable *get_local_var(const char *var);
545static int set_local_var(const char *s, int flg_export); 558static int set_local_var(char *s, int flg_export);
546static void unset_local_var(const char *name); 559static void unset_local_var(const char *name);
547 560
548/* Table of built-in functions. They can be forked or not, depending on 561/* Table of built-in functions. They can be forked or not, depending on
@@ -795,7 +808,7 @@ static int builtin_exit(char **argv)
795/* built-in 'export VAR=value' handler */ 808/* built-in 'export VAR=value' handler */
796static int builtin_export(char **argv) 809static int builtin_export(char **argv)
797{ 810{
798 int res = 0; 811 const char *value;
799 char *name = argv[1]; 812 char *name = argv[1];
800 813
801 if (name == NULL) { 814 if (name == NULL) {
@@ -810,36 +823,23 @@ static int builtin_export(char **argv)
810 return EXIT_SUCCESS; 823 return EXIT_SUCCESS;
811 } 824 }
812 825
813 name = xstrdup(name); 826 value = strchr(name, '=');
814 { 827 if (!value) {
815 const char *value = strchr(name, '='); 828 /* They are exporting something without a =VALUE */
816 829 struct variable *var;
817 if (!value) {
818 char *tmp;
819 /* They are exporting something without an =VALUE */
820
821 value = get_local_var(name);
822 if (value) {
823 size_t ln = strlen(name);
824 830
825 tmp = xrealloc(name, ln+strlen(value)+2); 831 var = get_local_var(name);
826 sprintf(tmp+ln, "=%s", value); 832 if (var) {
827 name = tmp; 833 var->flg_export = 1;
828 } else { 834 putenv(var->name);
829 /* bash does not return an error when trying to export
830 * an undefined variable. Do likewise. */
831 res = 1;
832 }
833 } 835 }
836 /* bash does not return an error when trying to export
837 * an undefined variable. Do likewise. */
838 return EXIT_SUCCESS;
834 } 839 }
835 if (res < 0) 840
836 bb_perror_msg("export"); 841 set_local_var(xstrdup(name), 1);
837 else if (res == 0) 842 return EXIT_SUCCESS;
838 res = set_local_var(name, 1);
839 else
840 res = 0;
841 free(name);
842 return res;
843} 843}
844 844
845#if ENABLE_HUSH_JOB 845#if ENABLE_HUSH_JOB
@@ -965,20 +965,20 @@ static int builtin_read(char **argv)
965 /* read string. name_len+1 chars are already used by 'name=' */ 965 /* read string. name_len+1 chars are already used by 'name=' */
966 fgets(p, sizeof(string) - 1 - name_len, stdin); 966 fgets(p, sizeof(string) - 1 - name_len, stdin);
967 chomp(p); 967 chomp(p);
968 return set_local_var(string, 0); 968 return set_local_var(xstrdup(string), 0);
969} 969}
970 970
971/* built-in 'set [VAR=value]' handler */ 971/* built-in 'set [VAR=value]' handler */
972static int builtin_set(char **argv) 972static int builtin_set(char **argv)
973{ 973{
974 char *temp = argv[1]; 974 char *temp = argv[1];
975 struct variables *e; 975 struct variable *e;
976 976
977 if (temp == NULL) 977 if (temp == NULL)
978 for (e = top_vars; e; e = e->next) 978 for (e = top_var; e; e = e->next)
979 printf("%s=%s\n", e->name, e->value); 979 puts(e->name);
980 else 980 else
981 set_local_var(temp, 0); 981 set_local_var(xstrdup(temp), 0);
982 982
983 return EXIT_SUCCESS; 983 return EXIT_SUCCESS;
984} 984}
@@ -1742,26 +1742,9 @@ static int run_pipe_real(struct pipe *pi)
1742 if (i != 0 && argv[i] == NULL) { 1742 if (i != 0 && argv[i] == NULL) {
1743 /* assignments, but no command: set the local environment */ 1743 /* assignments, but no command: set the local environment */
1744 for (i = 0; argv[i] != NULL; i++) { 1744 for (i = 0; argv[i] != NULL; i++) {
1745 /* Ok, this case is tricky. We have to decide if this is a 1745 debug_printf("local environment set: %s\n", argv[i]);
1746 * local variable, or an already exported variable. If it is
1747 * already exported, we have to export the new value. If it is
1748 * not exported, we need only set this as a local variable.
1749 * This junk is all to decide whether or not to export this
1750 * variable. */
1751 int export_me = 0;
1752 char *name, *value;
1753 name = xstrdup(argv[i]);
1754 debug_printf("local environment set: %s\n", name);
1755 value = strchr(name, '=');
1756 if (value)
1757 *value = '\0';
1758 if (get_local_var(name)) {
1759 export_me = 1;
1760 }
1761 free(name);
1762 p = expand_string_to_string(argv[i]); 1746 p = expand_string_to_string(argv[i]);
1763 set_local_var(p, export_me); 1747 set_local_var(p, 0);
1764 free(p);
1765 } 1748 }
1766 return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ 1749 return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */
1767 } 1750 }
@@ -2693,109 +2676,115 @@ static char* expand_strvec_to_string(char **argv)
2693} 2676}
2694 2677
2695/* This is used to get/check local shell variables */ 2678/* This is used to get/check local shell variables */
2696static const char *get_local_var(const char *s) 2679static struct variable *get_local_var(const char *s)
2697{ 2680{
2698 struct variables *cur; 2681 struct variable *cur;
2682 int len;
2699 2683
2700 if (!s) 2684 if (!s)
2701 return NULL; 2685 return NULL;
2702 for (cur = top_vars; cur; cur = cur->next) { 2686 len = strlen(s);
2703 if (strcmp(cur->name, s) == 0) 2687 for (cur = top_var; cur; cur = cur->next) {
2704 return cur->value; 2688 if (strncmp(cur->name, s, len) == 0 && cur->name[len] == '=')
2689 return cur;
2705 } 2690 }
2706 return NULL; 2691 return NULL;
2707} 2692}
2708 2693
2709/* This is used to set local shell variables 2694/* name holds "NAME=VAL" and is expected to be malloced.
2710 flg_export == 0 if only local (not exporting) variable 2695 * We take ownership of it. */
2711 flg_export == 1 if "new" exporting environ 2696static int set_local_var(char *name, int flg_export)
2712 flg_export > 1 if current startup environ (not call putenv()) */
2713static int set_local_var(const char *s, int flg_export)
2714{ 2697{
2715 char *name, *value; 2698 struct variable *cur;
2716 int result = 0; 2699 char *value;
2717 struct variables *cur; 2700 int name_len;
2718
2719 name = xstrdup(s);
2720 2701
2721 /* Assume when we enter this function that we are already in
2722 * NAME=VALUE format. So the first order of business is to
2723 * split 's' on the '=' into 'name' and 'value' */
2724 value = strchr(name, '='); 2702 value = strchr(name, '=');
2725 /*if (value == 0 && ++value == 0) ??? -vda */ 2703 if (!value) { /* not expected to ever happen? */
2726 if (value == NULL || value[1] == '\0') {
2727 free(name); 2704 free(name);
2728 return -1; 2705 return -1;
2729 } 2706 }
2730 *value++ = '\0';
2731 2707
2732 for (cur = top_vars; cur; cur = cur->next) { 2708 name_len = value - name;
2733 if (strcmp(cur->name, name) == 0) { 2709 cur = top_var; /* cannot be NULL (we have HUSH_VERSION and it's RO) */
2734 if (strcmp(cur->value, value) == 0) { 2710 while (1) {
2735 if (flg_export && !cur->flg_export) 2711 if (strncmp(cur->name, name, name_len) != 0 || cur->name[name_len] != '=') {
2736 cur->flg_export = flg_export; 2712 if (!cur->next) {
2737 else 2713 /* cur points to last var in linked list */
2738 result++; 2714 break;
2739 } else if (cur->flg_read_only) {
2740 bb_error_msg("%s: readonly variable", name);
2741 result = -1;
2742 } else {
2743 if (flg_export > 0 || cur->flg_export > 1)
2744 cur->flg_export = 1;
2745 free((char*)cur->value);
2746 cur->value = xstrdup(value);
2747 } 2715 }
2748 goto skip; 2716 cur = cur->next;
2717 continue;
2749 } 2718 }
2750 } 2719 /* We already have a var with this name */
2751 2720 if (cur->flg_read_only) {
2752 cur = xzalloc(sizeof(*cur)); 2721 bb_error_msg("%s: readonly variable", name);
2753 /*cur->next = 0;*/ 2722 free(name);
2754 cur->name = xstrdup(name); 2723 return -1;
2755 cur->value = xstrdup(value); 2724 }
2756 cur->flg_export = flg_export; 2725 *value = '\0';
2757 /*cur->flg_read_only = 0;*/ 2726 unsetenv(name); /* just in case */
2758 { 2727 *value++ = '=';
2759 struct variables *bottom = top_vars; 2728 if (strcmp(cur->value, value) == 0) {
2760 while (bottom->next) 2729 free_and_exp:
2761 bottom = bottom->next; 2730 free(name);
2762 bottom->next = cur; 2731 goto exp;
2763 } 2732 }
2764 skip: 2733 if (cur->max_len >= strlen(name)) {
2765 if (result == 0 && cur->flg_export == 1) { 2734 /* This one is from startup env, reuse space */
2766 *(value-1) = '='; 2735 strcpy(cur->name, name);
2767 result = putenv(name); 2736 goto free_and_exp;
2768 } else { 2737 }
2769 free(name); 2738 /* max_len == 0 signifies "malloced" var, which we can
2770 if (result > 0) /* equivalent to previous set */ 2739 * (and has to) free */
2771 result = 0; 2740 if (!cur->max_len)
2772 } 2741 free(cur->name);
2773 return result; 2742 cur->max_len = 0;
2743 goto set_name_and_exp;
2744 }
2745
2746 /* Not found - create next variable struct */
2747 cur->next = xzalloc(sizeof(*cur));
2748 cur = cur->next;
2749
2750 set_name_and_exp:
2751 cur->name = name;
2752 exp:
2753 cur->value = cur->name + name_len + 1;
2754 if (flg_export)
2755 cur->flg_export = 1;
2756 if (cur->flg_export)
2757 return putenv(cur->name);
2758 return 0;
2774} 2759}
2775 2760
2776static void unset_local_var(const char *name) 2761static void unset_local_var(const char *name)
2777{ 2762{
2778 struct variables *cur, *next; 2763 struct variable *cur;
2764 struct variable *prev = prev; /* for gcc */
2765 int name_len;
2779 2766
2780 if (!name) 2767 if (!name)
2781 return; 2768 return;
2782 for (cur = top_vars; cur; cur = cur->next) { 2769 name_len = strlen(name);
2783 if (strcmp(cur->name, name) == 0) { 2770 cur = top_var;
2771 while (cur) {
2772 if (strncmp(cur->name, name, name_len) == 0 && cur->name[name_len] == '=') {
2784 if (cur->flg_read_only) { 2773 if (cur->flg_read_only) {
2785 bb_error_msg("%s: readonly variable", name); 2774 bb_error_msg("%s: readonly variable", name);
2786 return; 2775 return;
2787 } 2776 }
2788 if (cur->flg_export) 2777 /* prev is ok to use here because 1st variable, HUSH_VERSION,
2789 unsetenv(cur->name); 2778 * is ro, and we cannot reach this code on the 1st pass */
2790 free((char*)cur->name); 2779 prev->next = cur->next;
2791 free((char*)cur->value); 2780 unsetenv(cur->name);
2792 next = top_vars; 2781 if (!cur->max_len)
2793 while (next->next != cur) 2782 free(cur->name);
2794 next = next->next;
2795 next->next = cur->next;
2796 free(cur); 2783 free(cur);
2797 return; 2784 return;
2798 } 2785 }
2786 prev = cur;
2787 cur = cur->next;
2799 } 2788 }
2800} 2789}
2801 2790
@@ -3265,13 +3254,10 @@ static int parse_group(o_string *dest, struct p_context *ctx,
3265 * see the bash man page under "Parameter Expansion" */ 3254 * see the bash man page under "Parameter Expansion" */
3266static const char *lookup_param(const char *src) 3255static const char *lookup_param(const char *src)
3267{ 3256{
3268 const char *p = NULL; 3257 struct variable *var = get_local_var(src);
3269 if (src) { 3258 if (var)
3270 p = getenv(src); 3259 return var->value;
3271 if (!p) 3260 return NULL;
3272 p = get_local_var(src);
3273 }
3274 return p;
3275} 3261}
3276 3262
3277/* return code: 0 for OK, 1 for syntax error */ 3263/* return code: 0 for OK, 1 for syntax error */
@@ -3681,10 +3667,29 @@ int hush_main(int argc, char **argv)
3681 int opt; 3667 int opt;
3682 FILE *input; 3668 FILE *input;
3683 char **e; 3669 char **e;
3670 struct variable *cur_var;
3684 3671
3685 PTR_TO_GLOBALS = xzalloc(sizeof(G)); 3672 PTR_TO_GLOBALS = xzalloc(sizeof(G));
3686 top_vars = &shell_ver; 3673
3687 shell_ver = const_shell_ver; /* copying struct here */ 3674 shell_ver = const_shell_ver; /* copying struct here */
3675 top_var = &shell_ver;
3676 /* initialize our shell local variables with the values
3677 * currently living in the environment */
3678 e = environ;
3679 cur_var = top_var;
3680 if (e) while (*e) {
3681 char *value = strchr(*e, '=');
3682 if (value) { /* paranoia */
3683 cur_var->next = xzalloc(sizeof(*cur_var));
3684 cur_var = cur_var->next;
3685 cur_var->name = *e;
3686 cur_var->value = value + 1;
3687 cur_var->max_len = strlen(*e);
3688 cur_var->flg_export = 1;
3689 }
3690 e++;
3691 }
3692 putenv(shell_ver.name);
3688 3693
3689#if ENABLE_FEATURE_EDITING 3694#if ENABLE_FEATURE_EDITING
3690 line_input_state = new_line_input_t(FOR_SHELL); 3695 line_input_state = new_line_input_t(FOR_SHELL);
@@ -3701,14 +3706,8 @@ int hush_main(int argc, char **argv)
3701 PS2 = "> "; 3706 PS2 = "> ";
3702#endif 3707#endif
3703 3708
3704 /* initialize our shell local variables with the values 3709 if (EXIT_SUCCESS) /* otherwise is already done */
3705 * currently living in the environment */ 3710 last_return_code = EXIT_SUCCESS;
3706 e = environ;
3707 if (e)
3708 while (*e)
3709 set_local_var(*e++, 2); /* without call putenv() */
3710
3711 last_return_code = EXIT_SUCCESS;
3712 3711
3713 if (argv[0] && argv[0][0] == '-') { 3712 if (argv[0] && argv[0][0] == '-') {
3714 debug_printf("sourcing /etc/profile\n"); 3713 debug_printf("sourcing /etc/profile\n");
@@ -3818,23 +3817,20 @@ int hush_main(int argc, char **argv)
3818 input = xfopen(argv[optind], "r"); 3817 input = xfopen(argv[optind], "r");
3819 opt = parse_and_run_file(input); 3818 opt = parse_and_run_file(input);
3820 3819
3820 final_return:
3821
3821#if ENABLE_FEATURE_CLEAN_UP 3822#if ENABLE_FEATURE_CLEAN_UP
3822 fclose(input); 3823 fclose(input);
3823 if (cwd != bb_msg_unknown) 3824 if (cwd != bb_msg_unknown)
3824 free((char*)cwd); 3825 free((char*)cwd);
3825 { 3826 cur_var = top_var->next;
3826 struct variables *cur, *tmp; 3827 while (cur_var) {
3827 for (cur = top_vars; cur; cur = tmp) { 3828 struct variable *tmp = cur_var;
3828 tmp = cur->next; 3829 if (!cur_var->max_len)
3829 if (!cur->flg_read_only) { 3830 free(cur_var->name);
3830 free((char*)cur->name); 3831 cur_var = cur_var->next;
3831 free((char*)cur->value); 3832 free(tmp);
3832 free(cur);
3833 }
3834 }
3835 } 3833 }
3836#endif 3834#endif
3837
3838 final_return:
3839 hush_exit(opt ? opt : last_return_code); 3835 hush_exit(opt ? opt : last_return_code);
3840} 3836}