diff options
| author | Eric Andersen <andersen@codepoet.org> | 2001-05-15 23:21:41 +0000 |
|---|---|---|
| committer | Eric Andersen <andersen@codepoet.org> | 2001-05-15 23:21:41 +0000 |
| commit | f72f562b2f118d32d4ca49b6248805936a0cfa66 (patch) | |
| tree | 5554e694d9363f88a0b11fa78cfd72fe25d3b537 /shell | |
| parent | 9abfe85e969e535dca385df3802ed76a44d977a2 (diff) | |
| download | busybox-w32-f72f562b2f118d32d4ca49b6248805936a0cfa66.tar.gz busybox-w32-f72f562b2f118d32d4ca49b6248805936a0cfa66.tar.bz2 busybox-w32-f72f562b2f118d32d4ca49b6248805936a0cfa66.zip | |
Fix the behavior of local shell variables to match that of bash and ash.
-Erik
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 134 |
1 files changed, 109 insertions, 25 deletions
diff --git a/shell/hush.c b/shell/hush.c index cfe0b826b..5a4966b07 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -55,7 +55,6 @@ | |||
| 55 | * to-do: | 55 | * to-do: |
| 56 | * port selected bugfixes from post-0.49 busybox lash - done? | 56 | * port selected bugfixes from post-0.49 busybox lash - done? |
| 57 | * finish implementing reserved words: for, while, until, do, done | 57 | * finish implementing reserved words: for, while, until, do, done |
| 58 | * finish implementing local variable assignment | ||
| 59 | * change { and } from special chars to reserved words | 58 | * change { and } from special chars to reserved words |
| 60 | * builtins: break, continue, eval, return, set, trap, ulimit | 59 | * builtins: break, continue, eval, return, set, trap, ulimit |
| 61 | * test magic exec | 60 | * test magic exec |
| @@ -325,6 +324,7 @@ static int builtin_help(struct child_prog *child); | |||
| 325 | static int builtin_jobs(struct child_prog *child); | 324 | static int builtin_jobs(struct child_prog *child); |
| 326 | static int builtin_pwd(struct child_prog *child); | 325 | static int builtin_pwd(struct child_prog *child); |
| 327 | static int builtin_read(struct child_prog *child); | 326 | static int builtin_read(struct child_prog *child); |
| 327 | static int builtin_set(struct child_prog *child); | ||
| 328 | static int builtin_shift(struct child_prog *child); | 328 | static int builtin_shift(struct child_prog *child); |
| 329 | static int builtin_source(struct child_prog *child); | 329 | static int builtin_source(struct child_prog *child); |
| 330 | static int builtin_umask(struct child_prog *child); | 330 | static int builtin_umask(struct child_prog *child); |
| @@ -388,6 +388,11 @@ static void checkjobs(); | |||
| 388 | static void insert_bg_job(struct pipe *pi); | 388 | static void insert_bg_job(struct pipe *pi); |
| 389 | static void remove_bg_job(struct pipe *pi); | 389 | static void remove_bg_job(struct pipe *pi); |
| 390 | static void free_pipe(struct pipe *pi); | 390 | static void free_pipe(struct pipe *pi); |
| 391 | /* local variable support */ | ||
| 392 | static char *get_local_var(const char *var); | ||
| 393 | static int set_local_var(const char *s); | ||
| 394 | static void unset_local_var(const char *name); | ||
| 395 | |||
| 391 | 396 | ||
| 392 | /* Table of built-in functions. They can be forked or not, depending on | 397 | /* Table of built-in functions. They can be forked or not, depending on |
| 393 | * context: within pipes, they fork. As simple commands, they do not. | 398 | * context: within pipes, they fork. As simple commands, they do not. |
| @@ -402,7 +407,8 @@ static struct built_in_command bltins[] = { | |||
| 402 | {"continue", "Continue for, while or until loop", builtin_not_written}, | 407 | {"continue", "Continue for, while or until loop", builtin_not_written}, |
| 403 | {"env", "Print all environment variables", builtin_env}, | 408 | {"env", "Print all environment variables", builtin_env}, |
| 404 | {"eval", "Construct and run shell command", builtin_not_written}, | 409 | {"eval", "Construct and run shell command", builtin_not_written}, |
| 405 | {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec}, | 410 | {"exec", "Exec command, replacing this shell with the exec'd process", |
| 411 | builtin_exec}, | ||
| 406 | {"exit", "Exit from shell()", builtin_exit}, | 412 | {"exit", "Exit from shell()", builtin_exit}, |
| 407 | {"export", "Set environment variable", builtin_export}, | 413 | {"export", "Set environment variable", builtin_export}, |
| 408 | {"fg", "Bring job into the foreground", builtin_fg_bg}, | 414 | {"fg", "Bring job into the foreground", builtin_fg_bg}, |
| @@ -410,7 +416,7 @@ static struct built_in_command bltins[] = { | |||
| 410 | {"pwd", "Print current directory", builtin_pwd}, | 416 | {"pwd", "Print current directory", builtin_pwd}, |
| 411 | {"read", "Input environment variable", builtin_read}, | 417 | {"read", "Input environment variable", builtin_read}, |
| 412 | {"return", "Return from a function", builtin_not_written}, | 418 | {"return", "Return from a function", builtin_not_written}, |
| 413 | {"set", "Set/unset shell options", builtin_not_written}, | 419 | {"set", "Set/unset shell local variables", builtin_set}, |
| 414 | {"shift", "Shift positional parameters", builtin_shift}, | 420 | {"shift", "Shift positional parameters", builtin_shift}, |
| 415 | {"trap", "Trap signals", builtin_not_written}, | 421 | {"trap", "Trap signals", builtin_not_written}, |
| 416 | {"ulimit","Controls resource limits", builtin_not_written}, | 422 | {"ulimit","Controls resource limits", builtin_not_written}, |
| @@ -472,16 +478,41 @@ static int builtin_exit(struct child_prog *child) | |||
| 472 | static int builtin_export(struct child_prog *child) | 478 | static int builtin_export(struct child_prog *child) |
| 473 | { | 479 | { |
| 474 | int res; | 480 | int res; |
| 481 | char *value, *name = child->argv[1]; | ||
| 475 | 482 | ||
| 476 | if (child->argv[1] == NULL) { | 483 | if (name == NULL) { |
| 477 | return (builtin_env(child)); | 484 | return (builtin_env(child)); |
| 478 | } | 485 | } |
| 479 | /* FIXME -- I leak memory. This will be | 486 | |
| 480 | * fixed up properly when we add local | 487 | value = strchr(name, '='); |
| 481 | * variable support -- I hope */ | 488 | if (!value) { |
| 482 | res = putenv(strdup(child->argv[1])); | 489 | /* They are exporting something without an =VALUE. |
| 490 | * Assume this is a local shell variable they are exporting */ | ||
| 491 | name = get_local_var(name); | ||
| 492 | if (! name ) { | ||
| 493 | error_msg("export failed"); | ||
| 494 | return (EXIT_FAILURE); | ||
| 495 | } | ||
| 496 | /* FIXME -- I leak memory!!!!! */ | ||
| 497 | value = malloc(strlen(child->argv[1]) + strlen(name) + 2); | ||
| 498 | sprintf(value, "%s=%s", child->argv[1], name); | ||
| 499 | } else { | ||
| 500 | /* Bourne shells always put exported variables into the | ||
| 501 | * local shell variable list. Do that first... */ | ||
| 502 | set_local_var(name); | ||
| 503 | /* FIXME -- I leak memory!!!!! */ | ||
| 504 | value = strdup(name); | ||
| 505 | } | ||
| 506 | |||
| 507 | /* FIXME -- I leak memory!!!!! | ||
| 508 | * It seems most putenv implementations place the very char* pointer | ||
| 509 | * we pass in directly into the environ array, so the memory holding | ||
| 510 | * this string has to be persistant. We can't even use the memory for | ||
| 511 | * the local shell variable list, since where that memory is keeps | ||
| 512 | * changing due to reallocs... */ | ||
| 513 | res = putenv(value); | ||
| 483 | if (res) | 514 | if (res) |
| 484 | fprintf(stderr, "export: %s\n", strerror(errno)); | 515 | perror_msg("export"); |
| 485 | return (res); | 516 | return (res); |
| 486 | } | 517 | } |
| 487 | 518 | ||
| @@ -616,6 +647,27 @@ static int builtin_read(struct child_prog *child) | |||
| 616 | return (res); | 647 | return (res); |
| 617 | } | 648 | } |
| 618 | 649 | ||
| 650 | /* built-in 'set VAR=value' handler */ | ||
| 651 | static int builtin_set(struct child_prog *child) | ||
| 652 | { | ||
| 653 | int res; | ||
| 654 | char *temp = child->argv[1]; | ||
| 655 | |||
| 656 | if (child->argv[1] == NULL) { | ||
| 657 | char **e = __shell_local_env; | ||
| 658 | if (e == NULL) return EXIT_FAILURE; | ||
| 659 | for (; *e; e++) { | ||
| 660 | puts(*e); | ||
| 661 | } | ||
| 662 | return EXIT_SUCCESS; | ||
| 663 | } | ||
| 664 | res = set_local_var(temp); | ||
| 665 | if (res) | ||
| 666 | fprintf(stderr, "set: %s\n", strerror(errno)); | ||
| 667 | return (res); | ||
| 668 | } | ||
| 669 | |||
| 670 | |||
| 619 | /* Built-in 'shift' handler */ | 671 | /* Built-in 'shift' handler */ |
| 620 | static int builtin_shift(struct child_prog *child) | 672 | static int builtin_shift(struct child_prog *child) |
| 621 | { | 673 | { |
| @@ -685,6 +737,7 @@ static int builtin_unset(struct child_prog *child) | |||
| 685 | return EXIT_FAILURE; | 737 | return EXIT_FAILURE; |
| 686 | } | 738 | } |
| 687 | unsetenv(child->argv[1]); | 739 | unsetenv(child->argv[1]); |
| 740 | unset_local_var(child->argv[1]); | ||
| 688 | return EXIT_SUCCESS; | 741 | return EXIT_SUCCESS; |
| 689 | } | 742 | } |
| 690 | 743 | ||
| @@ -1610,7 +1663,27 @@ static int xglob(o_string *dest, int flags, glob_t *pglob) | |||
| 1610 | return gr; | 1663 | return gr; |
| 1611 | } | 1664 | } |
| 1612 | 1665 | ||
| 1613 | /* This is used to set local variables (i.e. stuff not going into the environment) */ | 1666 | /* This is used to get/check local shell variables */ |
| 1667 | static char *get_local_var(const char *s) | ||
| 1668 | { | ||
| 1669 | char **p; | ||
| 1670 | int len; | ||
| 1671 | |||
| 1672 | if (!s) | ||
| 1673 | return NULL; | ||
| 1674 | if (!__shell_local_env) | ||
| 1675 | return NULL; | ||
| 1676 | len = strlen(s); | ||
| 1677 | |||
| 1678 | for (p = __shell_local_env; *p; p++) { | ||
| 1679 | if (memcmp(s, *p, len) == 0 && (*p)[len] == '=') { | ||
| 1680 | return *p + len + 1; | ||
| 1681 | } | ||
| 1682 | } | ||
| 1683 | return NULL; | ||
| 1684 | } | ||
| 1685 | |||
| 1686 | /* This is used to set local shell variables */ | ||
| 1614 | static int set_local_var(const char *s) | 1687 | static int set_local_var(const char *s) |
| 1615 | { | 1688 | { |
| 1616 | char **ep; | 1689 | char **ep; |
| @@ -1653,7 +1726,8 @@ static int set_local_var(const char *s) | |||
| 1653 | result = -1; | 1726 | result = -1; |
| 1654 | goto done_already; | 1727 | goto done_already; |
| 1655 | } | 1728 | } |
| 1656 | memcpy((__ptr_t) new_environ, (__ptr_t) __shell_local_env, size * sizeof(char *)); | 1729 | memcpy((__ptr_t) new_environ, (__ptr_t) __shell_local_env, |
| 1730 | size * sizeof(char *)); | ||
| 1657 | 1731 | ||
| 1658 | new_environ[size] = malloc (namelen + 1 + vallen + 1); | 1732 | new_environ[size] = malloc (namelen + 1 + vallen + 1); |
| 1659 | if (new_environ[size] == NULL) { | 1733 | if (new_environ[size] == NULL) { |
| @@ -1688,26 +1762,35 @@ static int set_local_var(const char *s) | |||
| 1688 | memcpy (&(*ep)[namelen + 1], value, vallen + 1); | 1762 | memcpy (&(*ep)[namelen + 1], value, vallen + 1); |
| 1689 | } | 1763 | } |
| 1690 | 1764 | ||
| 1765 | /* One last little detail... If this variable is already | ||
| 1766 | * in the environment we must set it there as well... */ | ||
| 1767 | tmp = getenv(name); | ||
| 1768 | if (tmp) { | ||
| 1769 | /* FIXME -- I leak memory!!!!! */ | ||
| 1770 | putenv(strdup(s)); | ||
| 1771 | } | ||
| 1772 | |||
| 1691 | done_already: | 1773 | done_already: |
| 1692 | free(name); | 1774 | free(name); |
| 1693 | return result; | 1775 | return result; |
| 1694 | } | 1776 | } |
| 1695 | 1777 | ||
| 1696 | char *get_local_var(const char *var) | 1778 | static void unset_local_var(const char *name) |
| 1697 | { | 1779 | { |
| 1698 | char **p; | 1780 | char **ep, **dp; |
| 1699 | int len; | 1781 | size_t namelen; |
| 1700 | |||
| 1701 | len = strlen(var); | ||
| 1702 | |||
| 1703 | if (!__shell_local_env) | ||
| 1704 | return 0; | ||
| 1705 | 1782 | ||
| 1706 | for (p = __shell_local_env; *p; p++) { | 1783 | if (!name) |
| 1707 | if (memcmp(var, *p, len) == 0 && (*p)[len] == '=') | 1784 | return; |
| 1708 | return *p + len + 1; | 1785 | namelen = strlen(name); |
| 1786 | for (dp = ep = __shell_local_env; ep && *ep != NULL; ++ep) { | ||
| 1787 | if (memcmp (*ep, name, namelen)==0 && (*ep)[namelen] == '=') { | ||
| 1788 | *dp = *ep; | ||
| 1789 | ++dp; | ||
| 1790 | *ep = NULL; | ||
| 1791 | break; | ||
| 1792 | } | ||
| 1709 | } | 1793 | } |
| 1710 | return 0; | ||
| 1711 | } | 1794 | } |
| 1712 | 1795 | ||
| 1713 | static int is_assignment(const char *s) | 1796 | static int is_assignment(const char *s) |
| @@ -2112,8 +2195,9 @@ static void lookup_param(o_string *dest, struct p_context *ctx, o_string *src) | |||
| 2112 | { | 2195 | { |
| 2113 | const char *p=NULL; | 2196 | const char *p=NULL; |
| 2114 | if (src->data) { | 2197 | if (src->data) { |
| 2115 | p = get_local_var(src->data); | 2198 | p = getenv(src->data); |
| 2116 | if (!p) p = getenv(src->data); | 2199 | if (!p) |
| 2200 | p = get_local_var(src->data); | ||
| 2117 | } | 2201 | } |
| 2118 | if (p) parse_string(dest, ctx, p); /* recursion */ | 2202 | if (p) parse_string(dest, ctx, p); /* recursion */ |
| 2119 | b_free(src); | 2203 | b_free(src); |
