diff options
| author | Eric Andersen <andersen@codepoet.org> | 2001-05-19 03:00:46 +0000 |
|---|---|---|
| committer | Eric Andersen <andersen@codepoet.org> | 2001-05-19 03:00:46 +0000 |
| commit | 9ffb7dd9a4688161140a4c19221247566886ef55 (patch) | |
| tree | ae31dd5e3f2c2edf2b4784835de11195d11769b9 /shell | |
| parent | 6197c51834c36a36cfc0b9a5cc146e6d79a9f5c9 (diff) | |
| download | busybox-w32-9ffb7dd9a4688161140a4c19221247566886ef55.tar.gz busybox-w32-9ffb7dd9a4688161140a4c19221247566886ef55.tar.bz2 busybox-w32-9ffb7dd9a4688161140a4c19221247566886ef55.zip | |
This is a patch from Vladimir:
> I rewrite *local_variable* function in hush.c with:
> 1) remove many memory leaks
> 2) add support read_only protect (require write builtin function for set this,
> I write this special for variable HUSH_VERION=0.01)
> 3) commad read set only local variable now
> 4) remove many error messages if "set unset export" not defined variable
> (bash syntax not put and set error code). Hmm, if I set result to -1, you hush
> called waitpid and returned with error "no waitpid" ( i not found place this
> error).
> 5) destroy error in new version check xgetcwd()==NULL and set "(unknow)" -
> this have error: crashe in next call `pwd`, but xgetcwd(not null) called
> free(arg).
> 6) next add integraion with libbb
Valdimir's patch missed two cases of local variable handling
FOO=bar
export FOO=baz
unset FOO
and
export FOO=bar
FOO=baz
which were working before, so I fixed those two cases.
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 382 |
1 files changed, 184 insertions, 198 deletions
diff --git a/shell/hush.c b/shell/hush.c index 59568189f..a1e65b24e 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -227,6 +227,14 @@ struct close_me { | |||
| 227 | struct close_me *next; | 227 | struct close_me *next; |
| 228 | }; | 228 | }; |
| 229 | 229 | ||
| 230 | struct variables { | ||
| 231 | char *name; | ||
| 232 | char *value; | ||
| 233 | int flg_export; | ||
| 234 | int flg_read_only; | ||
| 235 | struct variables *next; | ||
| 236 | }; | ||
| 237 | |||
| 230 | /* globals, connect us to the outside world | 238 | /* globals, connect us to the outside world |
| 231 | * the first three support $?, $#, and $1 */ | 239 | * the first three support $?, $#, and $1 */ |
| 232 | char **global_argv; | 240 | char **global_argv; |
| @@ -248,13 +256,14 @@ static const char *cwd; | |||
| 248 | static struct jobset *job_list; | 256 | static struct jobset *job_list; |
| 249 | static unsigned int last_bg_pid; | 257 | static unsigned int last_bg_pid; |
| 250 | static char *PS1; | 258 | static char *PS1; |
| 251 | static char *PS2; | 259 | static char PS2[] = "> "; |
| 252 | static char **__shell_local_env; | 260 | |
| 261 | struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; | ||
| 262 | |||
| 263 | struct variables *top_vars = &shell_ver; | ||
| 253 | 264 | ||
| 254 | #define B_CHUNK (100) | 265 | #define B_CHUNK (100) |
| 255 | #define B_NOSPAC 1 | 266 | #define B_NOSPAC 1 |
| 256 | #define MAX_LINE 256 /* for cwd */ | ||
| 257 | #define MAX_READ 256 /* for builtin_read */ | ||
| 258 | 267 | ||
| 259 | typedef struct { | 268 | typedef struct { |
| 260 | char *data; | 269 | char *data; |
| @@ -303,12 +312,12 @@ static void debug_printf(const char *format, ...) | |||
| 303 | va_end(args); | 312 | va_end(args); |
| 304 | } | 313 | } |
| 305 | #else | 314 | #else |
| 306 | static void debug_printf(const char *format, ...) { } | 315 | static inline void debug_printf(const char *format, ...) { } |
| 307 | #endif | 316 | #endif |
| 308 | #define final_printf debug_printf | 317 | #define final_printf debug_printf |
| 309 | 318 | ||
| 310 | void __syntax(char *file, int line) { | 319 | static void __syntax(char *file, int line) { |
| 311 | fprintf(stderr,"syntax error %s:%d\n",file,line); | 320 | error_msg("syntax error %s:%d", file, line); |
| 312 | } | 321 | } |
| 313 | #define syntax() __syntax(__FILE__, __LINE__) | 322 | #define syntax() __syntax(__FILE__, __LINE__) |
| 314 | 323 | ||
| @@ -362,7 +371,6 @@ static int globhack(const char *src, int flags, glob_t *pglob); | |||
| 362 | static int glob_needed(const char *s); | 371 | static int glob_needed(const char *s); |
| 363 | static int xglob(o_string *dest, int flags, glob_t *pglob); | 372 | static int xglob(o_string *dest, int flags, glob_t *pglob); |
| 364 | /* variable assignment: */ | 373 | /* variable assignment: */ |
| 365 | static int set_local_var(const char *s); | ||
| 366 | static int is_assignment(const char *s); | 374 | static int is_assignment(const char *s); |
| 367 | /* data structure manipulation: */ | 375 | /* data structure manipulation: */ |
| 368 | static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input); | 376 | static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input); |
| @@ -390,8 +398,8 @@ static void remove_bg_job(struct pipe *pi); | |||
| 390 | static void free_pipe(struct pipe *pi); | 398 | static void free_pipe(struct pipe *pi); |
| 391 | /* local variable support */ | 399 | /* local variable support */ |
| 392 | static char *get_local_var(const char *var); | 400 | 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); | 401 | static void unset_local_var(const char *name); |
| 402 | static int set_local_var(const char *s, int flg_export); | ||
| 395 | 403 | ||
| 396 | 404 | ||
| 397 | /* Table of built-in functions. They can be forked or not, depending on | 405 | /* Table of built-in functions. They can be forked or not, depending on |
| @@ -427,6 +435,17 @@ static struct built_in_command bltins[] = { | |||
| 427 | {NULL, NULL, NULL} | 435 | {NULL, NULL, NULL} |
| 428 | }; | 436 | }; |
| 429 | 437 | ||
| 438 | static const char *set_cwd(void) | ||
| 439 | { | ||
| 440 | if(cwd==unknown) | ||
| 441 | cwd = NULL; /* xgetcwd(arg) called free(arg) */ | ||
| 442 | cwd = xgetcwd((char *)cwd); | ||
| 443 | if (!cwd) | ||
| 444 | cwd = unknown; | ||
| 445 | return cwd; | ||
| 446 | } | ||
| 447 | |||
| 448 | |||
| 430 | /* built-in 'cd <path>' handler */ | 449 | /* built-in 'cd <path>' handler */ |
| 431 | static int builtin_cd(struct child_prog *child) | 450 | static int builtin_cd(struct child_prog *child) |
| 432 | { | 451 | { |
| @@ -439,9 +458,7 @@ static int builtin_cd(struct child_prog *child) | |||
| 439 | printf("cd: %s: %s\n", newdir, strerror(errno)); | 458 | printf("cd: %s: %s\n", newdir, strerror(errno)); |
| 440 | return EXIT_FAILURE; | 459 | return EXIT_FAILURE; |
| 441 | } | 460 | } |
| 442 | cwd = xgetcwd((char *)cwd); | 461 | set_cwd(); |
| 443 | if (!cwd) | ||
| 444 | cwd = unknown; | ||
| 445 | return EXIT_SUCCESS; | 462 | return EXIT_SUCCESS; |
| 446 | } | 463 | } |
| 447 | 464 | ||
| @@ -477,43 +494,48 @@ static int builtin_exit(struct child_prog *child) | |||
| 477 | /* built-in 'export VAR=value' handler */ | 494 | /* built-in 'export VAR=value' handler */ |
| 478 | static int builtin_export(struct child_prog *child) | 495 | static int builtin_export(struct child_prog *child) |
| 479 | { | 496 | { |
| 480 | int res; | 497 | int res = 0; |
| 481 | char *value, *name = child->argv[1]; | 498 | char *name = child->argv[1]; |
| 482 | 499 | ||
| 483 | if (name == NULL) { | 500 | if (name == NULL) { |
| 484 | return (builtin_env(child)); | 501 | return (builtin_env(child)); |
| 485 | } | 502 | } |
| 486 | 503 | ||
| 487 | value = strchr(name, '='); | 504 | name = strdup(name); |
| 505 | |||
| 506 | if(name) { | ||
| 507 | char *value = strchr(name, '='); | ||
| 508 | |||
| 488 | if (!value) { | 509 | if (!value) { |
| 489 | /* They are exporting something without an =VALUE. | 510 | char *tmp; |
| 490 | * Assume this is a local shell variable they are exporting */ | 511 | /* They are exporting something without an =VALUE */ |
| 491 | name = get_local_var(name); | 512 | |
| 492 | if (! name ) { | 513 | value = get_local_var(name); |
| 493 | error_msg("export failed"); | 514 | if (value) { |
| 494 | return (EXIT_FAILURE); | 515 | size_t ln = strlen(name); |
| 516 | |||
| 517 | tmp = realloc(name, ln+strlen(value)+2); | ||
| 518 | if(tmp==NULL) | ||
| 519 | res = -1; | ||
| 520 | else { | ||
| 521 | sprintf(tmp+ln, "=%s", value); | ||
| 522 | name = tmp; | ||
| 523 | } | ||
| 524 | } else { | ||
| 525 | /* bash not put error and set error code | ||
| 526 | if exporting not defined variable */ | ||
| 527 | res = 1; | ||
| 495 | } | 528 | } |
| 496 | /* FIXME -- I leak memory!!!!! */ | 529 | } |
| 497 | value = malloc(strlen(child->argv[1]) + strlen(name) + 2); | 530 | } |
| 498 | sprintf(value, "%s=%s", child->argv[1], name); | 531 | if (res<0) |
| 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); | ||
| 514 | if (res) | ||
| 515 | perror_msg("export"); | 532 | perror_msg("export"); |
| 516 | return (res); | 533 | else if(res==0) |
| 534 | res = set_local_var(name, 1); | ||
| 535 | else | ||
| 536 | res = 0; | ||
| 537 | free(name); | ||
| 538 | return res; | ||
| 517 | } | 539 | } |
| 518 | 540 | ||
| 519 | /* built-in 'fg' and 'bg' handler */ | 541 | /* built-in 'fg' and 'bg' handler */ |
| @@ -605,66 +627,52 @@ static int builtin_jobs(struct child_prog *child) | |||
| 605 | /* built-in 'pwd' handler */ | 627 | /* built-in 'pwd' handler */ |
| 606 | static int builtin_pwd(struct child_prog *dummy) | 628 | static int builtin_pwd(struct child_prog *dummy) |
| 607 | { | 629 | { |
| 608 | cwd = xgetcwd((char *)cwd); | 630 | puts(set_cwd()); |
| 609 | if (!cwd) | ||
| 610 | cwd = unknown; | ||
| 611 | puts(cwd); | ||
| 612 | return EXIT_SUCCESS; | 631 | return EXIT_SUCCESS; |
| 613 | } | 632 | } |
| 614 | 633 | ||
| 615 | /* built-in 'read VAR' handler */ | 634 | /* built-in 'read VAR' handler */ |
| 616 | static int builtin_read(struct child_prog *child) | 635 | static int builtin_read(struct child_prog *child) |
| 617 | { | 636 | { |
| 618 | int res = 0, len, newlen; | 637 | int res; |
| 619 | char *s; | ||
| 620 | char string[MAX_READ]; | ||
| 621 | 638 | ||
| 622 | if (child->argv[1]) { | 639 | if (child->argv[1]) { |
| 623 | /* argument (VAR) given: put "VAR=" into buffer */ | 640 | char string[BUFSIZ]; |
| 624 | strcpy(string, child->argv[1]); | 641 | char *var = 0; |
| 625 | len = strlen(string); | 642 | |
| 626 | string[len++] = '='; | 643 | string[0] = 0; /* for correct work if stdin have "only EOF" */ |
| 627 | string[len] = '\0'; | 644 | /* read string */ |
| 628 | /* XXX would it be better to go through in_str? */ | 645 | fgets(string, sizeof(string), stdin); |
| 629 | fgets(&string[len], sizeof(string) - len, stdin); /* read string */ | 646 | chomp(string); |
| 630 | newlen = strlen(string); | 647 | var = malloc(strlen(child->argv[1])+strlen(string)+2); |
| 631 | if(newlen > len) | 648 | if(var) { |
| 632 | string[--newlen] = '\0'; /* chomp trailing newline */ | 649 | sprintf(var, "%s=%s", child->argv[1], string); |
| 633 | /* | 650 | res = set_local_var(var, 0); |
| 634 | ** string should now contain "VAR=<value>" | 651 | } else |
| 635 | ** copy it (putenv() won't do that, so we must make sure | ||
| 636 | ** the string resides in a static buffer!) | ||
| 637 | */ | ||
| 638 | res = -1; | 652 | res = -1; |
| 639 | if((s = strdup(string))) | ||
| 640 | res = putenv(s); | ||
| 641 | if (res) | 653 | if (res) |
| 642 | fprintf(stderr, "read: %s\n", strerror(errno)); | 654 | fprintf(stderr, "read: %m\n"); |
| 655 | free(var); /* not move up - saved errno */ | ||
| 656 | return res; | ||
| 657 | } else { | ||
| 658 | do res=getchar(); while(res!='\n' && res!=EOF); | ||
| 659 | return 0; | ||
| 643 | } | 660 | } |
| 644 | else | ||
| 645 | fgets(string, sizeof(string), stdin); | ||
| 646 | |||
| 647 | return (res); | ||
| 648 | } | 661 | } |
| 649 | 662 | ||
| 650 | /* built-in 'set VAR=value' handler */ | 663 | /* built-in 'set VAR=value' handler */ |
| 651 | static int builtin_set(struct child_prog *child) | 664 | static int builtin_set(struct child_prog *child) |
| 652 | { | 665 | { |
| 653 | int res; | ||
| 654 | char *temp = child->argv[1]; | 666 | char *temp = child->argv[1]; |
| 667 | struct variables *e; | ||
| 668 | |||
| 669 | if (temp == NULL) | ||
| 670 | for(e = top_vars; e; e=e->next) | ||
| 671 | printf("%s=%s\n", e->name, e->value); | ||
| 672 | else | ||
| 673 | set_local_var(temp, 0); | ||
| 655 | 674 | ||
| 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; | 675 | 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 | } | 676 | } |
| 669 | 677 | ||
| 670 | 678 | ||
| @@ -697,7 +705,7 @@ static int builtin_source(struct child_prog *child) | |||
| 697 | /* XXX search through $PATH is missing */ | 705 | /* XXX search through $PATH is missing */ |
| 698 | input = fopen(child->argv[1], "r"); | 706 | input = fopen(child->argv[1], "r"); |
| 699 | if (!input) { | 707 | if (!input) { |
| 700 | fprintf(stderr, "Couldn't open file '%s'\n", child->argv[1]); | 708 | error_msg("Couldn't open file '%s'", child->argv[1]); |
| 701 | return EXIT_FAILURE; | 709 | return EXIT_FAILURE; |
| 702 | } | 710 | } |
| 703 | 711 | ||
| @@ -732,11 +740,7 @@ static int builtin_umask(struct child_prog *child) | |||
| 732 | /* built-in 'unset VAR' handler */ | 740 | /* built-in 'unset VAR' handler */ |
| 733 | static int builtin_unset(struct child_prog *child) | 741 | static int builtin_unset(struct child_prog *child) |
| 734 | { | 742 | { |
| 735 | if (child->argv[1] == NULL) { | 743 | /* bash returned already true */ |
| 736 | fprintf(stderr, "unset: parameter required.\n"); | ||
| 737 | return EXIT_FAILURE; | ||
| 738 | } | ||
| 739 | unsetenv(child->argv[1]); | ||
| 740 | unset_local_var(child->argv[1]); | 744 | unset_local_var(child->argv[1]); |
| 741 | return EXIT_SUCCESS; | 745 | return EXIT_SUCCESS; |
| 742 | } | 746 | } |
| @@ -998,8 +1002,7 @@ static int setup_redirects(struct child_prog *prog, int squirrel[]) | |||
| 998 | if (openfd < 0) { | 1002 | if (openfd < 0) { |
| 999 | /* this could get lost if stderr has been redirected, but | 1003 | /* this could get lost if stderr has been redirected, but |
| 1000 | bash and ash both lose it as well (though zsh doesn't!) */ | 1004 | bash and ash both lose it as well (though zsh doesn't!) */ |
| 1001 | fprintf(stderr,"error opening %s: %s\n", redir->word.gl_pathv[0], | 1005 | perror_msg("error opening %s", redir->word.gl_pathv[0]); |
| 1002 | strerror(errno)); | ||
| 1003 | return 1; | 1006 | return 1; |
| 1004 | } | 1007 | } |
| 1005 | } else { | 1008 | } else { |
| @@ -1160,10 +1163,9 @@ static void insert_bg_job(struct pipe *pi) | |||
| 1160 | /* physically copy the struct job */ | 1163 | /* physically copy the struct job */ |
| 1161 | memcpy(thejob, pi, sizeof(struct pipe)); | 1164 | memcpy(thejob, pi, sizeof(struct pipe)); |
| 1162 | thejob->next = NULL; | 1165 | thejob->next = NULL; |
| 1163 | //thejob->num_progs = 0; | ||
| 1164 | thejob->running_progs = thejob->num_progs; | 1166 | thejob->running_progs = thejob->num_progs; |
| 1165 | thejob->stopped_progs = 0; | 1167 | thejob->stopped_progs = 0; |
| 1166 | thejob->text = xmalloc(MAX_LINE); | 1168 | thejob->text = xmalloc(BUFSIZ); /* cmdedit buffer size */ |
| 1167 | 1169 | ||
| 1168 | //if (pi->progs[0] && pi->progs[0].argv && pi->progs[0].argv[0]) | 1170 | //if (pi->progs[0] && pi->progs[0].argv && pi->progs[0].argv[0]) |
| 1169 | { | 1171 | { |
| @@ -1327,7 +1329,7 @@ static int run_pipe_real(struct pipe *pi) | |||
| 1327 | if (i!=0 && child->argv[i]==NULL) { | 1329 | if (i!=0 && child->argv[i]==NULL) { |
| 1328 | /* assignments, but no command: set the local environment */ | 1330 | /* assignments, but no command: set the local environment */ |
| 1329 | for (i=0; child->argv[i]!=NULL; i++) { | 1331 | for (i=0; child->argv[i]!=NULL; i++) { |
| 1330 | set_local_var(child->argv[i]); | 1332 | set_local_var(child->argv[i], 0); |
| 1331 | } | 1333 | } |
| 1332 | return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ | 1334 | return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ |
| 1333 | } | 1335 | } |
| @@ -1646,12 +1648,10 @@ static int xglob(o_string *dest, int flags, glob_t *pglob) | |||
| 1646 | gr = globhack(dest->data, flags, pglob); | 1648 | gr = globhack(dest->data, flags, pglob); |
| 1647 | debug_printf("globhack returned %d\n",gr); | 1649 | debug_printf("globhack returned %d\n",gr); |
| 1648 | } | 1650 | } |
| 1649 | if (gr == GLOB_NOSPACE) { | 1651 | if (gr == GLOB_NOSPACE) |
| 1650 | fprintf(stderr,"out of memory during glob\n"); | 1652 | error_msg_and_die("out of memory during glob"); |
| 1651 | exit(1); | ||
| 1652 | } | ||
| 1653 | if (gr != 0) { /* GLOB_ABORTED ? */ | 1653 | if (gr != 0) { /* GLOB_ABORTED ? */ |
| 1654 | fprintf(stderr,"glob(3) error %d\n",gr); | 1654 | error_msg("glob(3) error %d",gr); |
| 1655 | } | 1655 | } |
| 1656 | /* globprint(glob_target); */ | 1656 | /* globprint(glob_target); */ |
| 1657 | return gr; | 1657 | return gr; |
| @@ -1660,129 +1660,113 @@ static int xglob(o_string *dest, int flags, glob_t *pglob) | |||
| 1660 | /* This is used to get/check local shell variables */ | 1660 | /* This is used to get/check local shell variables */ |
| 1661 | static char *get_local_var(const char *s) | 1661 | static char *get_local_var(const char *s) |
| 1662 | { | 1662 | { |
| 1663 | char **p; | 1663 | struct variables *cur; |
| 1664 | int len; | ||
| 1665 | 1664 | ||
| 1666 | if (!s) | 1665 | if (!s) |
| 1667 | return NULL; | 1666 | return NULL; |
| 1668 | if (!__shell_local_env) | 1667 | for (cur = top_vars; cur; cur=cur->next) |
| 1669 | return NULL; | 1668 | if(strcmp(cur->name, s)==0) |
| 1670 | len = strlen(s); | 1669 | return cur->value; |
| 1671 | |||
| 1672 | for (p = __shell_local_env; *p; p++) { | ||
| 1673 | if (memcmp(s, *p, len) == 0 && (*p)[len] == '=') { | ||
| 1674 | return *p + len + 1; | ||
| 1675 | } | ||
| 1676 | } | ||
| 1677 | return NULL; | 1670 | return NULL; |
| 1678 | } | 1671 | } |
| 1679 | 1672 | ||
| 1680 | /* This is used to set local shell variables */ | 1673 | /* This is used to set local shell variables |
| 1681 | static int set_local_var(const char *s) | 1674 | flg_export==0 if only local (not exporting) variable |
| 1675 | flg_export==1 if "new" exporting environ | ||
| 1676 | flg_export>1 if current startup environ (not call putenv()) */ | ||
| 1677 | static int set_local_var(const char *s, int flg_export) | ||
| 1682 | { | 1678 | { |
| 1683 | char **ep; | 1679 | char *name, *value; |
| 1684 | char *tmp,*name, *value; | ||
| 1685 | size_t size; | ||
| 1686 | size_t namelen; | ||
| 1687 | size_t vallen; | ||
| 1688 | int result=0; | 1680 | int result=0; |
| 1681 | struct variables *cur; | ||
| 1682 | char *newval = 0; | ||
| 1689 | 1683 | ||
| 1690 | name=tmp=strdup(s); | 1684 | name=strdup(s); |
| 1691 | 1685 | ||
| 1692 | /* Assume when we enter this function that we are already in | 1686 | /* Assume when we enter this function that we are already in |
| 1693 | * NAME=VALUE format. So the first order of business is to | 1687 | * NAME=VALUE format. So the first order of business is to |
| 1694 | * split 's' on the '=' into 'name' and 'value' */ | 1688 | * split 's' on the '=' into 'name' and 'value' */ |
| 1695 | value = strchr(name, '='); | 1689 | value = strchr(name, '='); |
| 1696 | if (!value) { | 1690 | if (value==0 || (newval = strdup(value+1))==0) { |
| 1697 | result = -1; | 1691 | result = -1; |
| 1698 | goto done_already; | 1692 | } else { |
| 1699 | } | 1693 | *value++ = 0; |
| 1700 | *value='\0'; | ||
| 1701 | ++value; | ||
| 1702 | |||
| 1703 | namelen = strlen (name); | ||
| 1704 | vallen = strlen (value); | ||
| 1705 | 1694 | ||
| 1706 | /* Now see how many local environment entries we have, and check | 1695 | for(cur = top_vars; cur; cur = cur->next) |
| 1707 | * if we match an existing environment entry (so we can overwrite it) */ | 1696 | if(strcmp(cur->name, name)==0) |
| 1708 | size = 0; | ||
| 1709 | for (ep = __shell_local_env; ep && *ep != NULL; ++ep) { | ||
| 1710 | if (!memcmp (*ep, name, namelen) && (*ep)[namelen] == '=') | ||
| 1711 | break; | 1697 | break; |
| 1712 | else | ||
| 1713 | ++size; | ||
| 1714 | } | ||
| 1715 | 1698 | ||
| 1716 | if (ep == NULL || *ep == NULL) { | 1699 | if(cur) { |
| 1717 | static char **last_environ = NULL; | 1700 | if(strcmp(cur->value, value)==0) { |
| 1718 | char **new_environ = (char **) malloc((size + 2) * sizeof(char *)); | 1701 | result = cur->flg_export == flg_export; |
| 1719 | if (new_environ == NULL) { | 1702 | } else { |
| 1720 | result = -1; | 1703 | if(cur->flg_read_only) { |
| 1721 | goto done_already; | ||
| 1722 | } | ||
| 1723 | memcpy((__ptr_t) new_environ, (__ptr_t) __shell_local_env, | ||
| 1724 | size * sizeof(char *)); | ||
| 1725 | |||
| 1726 | new_environ[size] = malloc (namelen + 1 + vallen + 1); | ||
| 1727 | if (new_environ[size] == NULL) { | ||
| 1728 | free (new_environ); | ||
| 1729 | errno=ENOMEM; | ||
| 1730 | result = -1; | 1704 | result = -1; |
| 1731 | goto done_already; | 1705 | error_msg("%s: readonly variable", name); |
| 1706 | } else { | ||
| 1707 | free(cur->value); | ||
| 1708 | cur->value = newval; | ||
| 1709 | newval = 0; /* protect free */ | ||
| 1732 | } | 1710 | } |
| 1733 | memcpy (new_environ[size], name, namelen); | 1711 | } |
| 1734 | new_environ[size][namelen] = '='; | 1712 | } else { |
| 1735 | memcpy (&new_environ[size][namelen + 1], value, vallen + 1); | 1713 | cur = malloc(sizeof(struct variables)); |
| 1736 | 1714 | if(cur==0) { | |
| 1737 | new_environ[size + 1] = NULL; | ||
| 1738 | |||
| 1739 | if (last_environ != NULL) | ||
| 1740 | free ((__ptr_t) last_environ); | ||
| 1741 | last_environ = new_environ; | ||
| 1742 | __shell_local_env = new_environ; | ||
| 1743 | } | ||
| 1744 | else { | ||
| 1745 | size_t len = strlen (*ep); | ||
| 1746 | if (len < namelen + 1 + vallen) { | ||
| 1747 | char *new = malloc (namelen + 1 + vallen + 1); | ||
| 1748 | if (new == NULL) { | ||
| 1749 | result = -1; | 1715 | result = -1; |
| 1750 | goto done_already; | 1716 | } else { |
| 1717 | cur->name = strdup(name); | ||
| 1718 | if(cur->name == 0) { | ||
| 1719 | free(cur); | ||
| 1720 | result = -1; | ||
| 1721 | } else { | ||
| 1722 | struct variables *bottom = top_vars; | ||
| 1723 | |||
| 1724 | cur->value = newval; | ||
| 1725 | newval = 0; /* protect free */ | ||
| 1726 | cur->next = 0; | ||
| 1727 | cur->flg_export = flg_export; | ||
| 1728 | cur->flg_read_only = 0; | ||
| 1729 | while(bottom->next) bottom=bottom->next; | ||
| 1730 | bottom->next = cur; | ||
| 1751 | } | 1731 | } |
| 1752 | *ep = new; | ||
| 1753 | memcpy (*ep, name, namelen); | ||
| 1754 | (*ep)[namelen] = '='; | ||
| 1755 | } | 1732 | } |
| 1756 | memcpy (&(*ep)[namelen + 1], value, vallen + 1); | ||
| 1757 | } | 1733 | } |
| 1758 | |||
| 1759 | /* One last little detail... If this variable is already | ||
| 1760 | * in the environment we must set it there as well... */ | ||
| 1761 | tmp = getenv(name); | ||
| 1762 | if (tmp) { | ||
| 1763 | /* FIXME -- I leak memory!!!!! */ | ||
| 1764 | putenv(strdup(s)); | ||
| 1765 | } | 1734 | } |
| 1766 | 1735 | ||
| 1767 | done_already: | 1736 | if((result==0 && flg_export==1) || (result>0 && cur->flg_export>0)) { |
| 1737 | *(value-1) = '='; | ||
| 1738 | result = putenv(name); | ||
| 1739 | } else { | ||
| 1768 | free(name); | 1740 | free(name); |
| 1741 | if(result>0) /* equivalent to previous set */ | ||
| 1742 | result = 0; | ||
| 1743 | } | ||
| 1744 | free(newval); | ||
| 1769 | return result; | 1745 | return result; |
| 1770 | } | 1746 | } |
| 1771 | 1747 | ||
| 1772 | static void unset_local_var(const char *name) | 1748 | static void unset_local_var(const char *name) |
| 1773 | { | 1749 | { |
| 1774 | char **ep, **dp; | 1750 | struct variables *cur; |
| 1775 | size_t namelen; | ||
| 1776 | 1751 | ||
| 1777 | if (!name) | 1752 | if (name) { |
| 1753 | for (cur = top_vars; cur; cur=cur->next) | ||
| 1754 | if(strcmp(cur->name, name)==0) | ||
| 1755 | break; | ||
| 1756 | if(cur!=0) { | ||
| 1757 | struct variables *next = top_vars; | ||
| 1758 | if(cur==next) | ||
| 1778 | return; | 1759 | return; |
| 1779 | namelen = strlen(name); | 1760 | else { |
| 1780 | for (dp = ep = __shell_local_env; ep && *ep != NULL; ++ep) { | 1761 | if(cur->flg_export) |
| 1781 | if (memcmp (*ep, name, namelen)==0 && (*ep)[namelen] == '=') { | 1762 | unsetenv(cur->name); |
| 1782 | *dp = *ep; | 1763 | free(cur->name); |
| 1783 | ++dp; | 1764 | free(cur->value); |
| 1784 | *ep = NULL; | 1765 | while (next->next != cur) |
| 1785 | break; | 1766 | next = next->next; |
| 1767 | next->next = cur->next; | ||
| 1768 | } | ||
| 1769 | free(cur); | ||
| 1786 | } | 1770 | } |
| 1787 | } | 1771 | } |
| 1788 | } | 1772 | } |
| @@ -1963,7 +1947,7 @@ static int done_word(o_string *dest, struct p_context *ctx) | |||
| 1963 | if (ctx->pending_redirect) { | 1947 | if (ctx->pending_redirect) { |
| 1964 | ctx->pending_redirect=NULL; | 1948 | ctx->pending_redirect=NULL; |
| 1965 | if (glob_target->gl_pathc != 1) { | 1949 | if (glob_target->gl_pathc != 1) { |
| 1966 | fprintf(stderr, "ambiguous redirect\n"); | 1950 | error_msg("ambiguous redirect"); |
| 1967 | return 1; | 1951 | return 1; |
| 1968 | } | 1952 | } |
| 1969 | } else { | 1953 | } else { |
| @@ -2049,7 +2033,7 @@ static int redirect_dup_num(struct in_str *input) | |||
| 2049 | } | 2033 | } |
| 2050 | if (ok) return d; | 2034 | if (ok) return d; |
| 2051 | 2035 | ||
| 2052 | fprintf(stderr, "ambiguous redirect\n"); | 2036 | error_msg("ambiguous redirect"); |
| 2053 | return -2; | 2037 | return -2; |
| 2054 | } | 2038 | } |
| 2055 | 2039 | ||
| @@ -2261,7 +2245,7 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i | |||
| 2261 | case '-': | 2245 | case '-': |
| 2262 | case '_': | 2246 | case '_': |
| 2263 | /* still unhandled, but should be eventually */ | 2247 | /* still unhandled, but should be eventually */ |
| 2264 | fprintf(stderr,"unhandled syntax: $%c\n",ch); | 2248 | error_msg("unhandled syntax: $%c",ch); |
| 2265 | return 1; | 2249 | return 1; |
| 2266 | break; | 2250 | break; |
| 2267 | default: | 2251 | default: |
| @@ -2505,16 +2489,14 @@ int shell_main(int argc, char **argv) | |||
| 2505 | int opt; | 2489 | int opt; |
| 2506 | FILE *input; | 2490 | FILE *input; |
| 2507 | struct jobset joblist_end = { NULL, NULL }; | 2491 | struct jobset joblist_end = { NULL, NULL }; |
| 2492 | char **e = environ; | ||
| 2508 | 2493 | ||
| 2509 | /* (re?) initialize globals */ | 2494 | /* initialize globals */ |
| 2510 | ifs=NULL; | 2495 | if (e) { |
| 2511 | fake_mode=0; | 2496 | for (; *e; e++) |
| 2512 | interactive=0; | 2497 | set_local_var(*e, 2); /* without call putenv() */ |
| 2513 | close_me_head = NULL; | 2498 | } |
| 2514 | job_list = &joblist_end; | 2499 | job_list = &joblist_end; |
| 2515 | last_bg_pid=0; | ||
| 2516 | PS2 = "> "; | ||
| 2517 | __shell_local_env = 0; | ||
| 2518 | 2500 | ||
| 2519 | last_return_code=EXIT_SUCCESS; | 2501 | last_return_code=EXIT_SUCCESS; |
| 2520 | 2502 | ||
| @@ -2581,9 +2563,13 @@ int shell_main(int argc, char **argv) | |||
| 2581 | fake_mode++; | 2563 | fake_mode++; |
| 2582 | break; | 2564 | break; |
| 2583 | default: | 2565 | default: |
| 2566 | #ifndef BB_VER | ||
| 2584 | fprintf(stderr, "Usage: sh [FILE]...\n" | 2567 | fprintf(stderr, "Usage: sh [FILE]...\n" |
| 2585 | " or: sh -c command [args]...\n\n"); | 2568 | " or: sh -c command [args]...\n\n"); |
| 2586 | exit(EXIT_FAILURE); | 2569 | exit(EXIT_FAILURE); |
| 2570 | #else | ||
| 2571 | show_usage(); | ||
| 2572 | #endif | ||
| 2587 | } | 2573 | } |
| 2588 | } | 2574 | } |
| 2589 | /* A shell is interactive if the `-i' flag was given, or if all of | 2575 | /* A shell is interactive if the `-i' flag was given, or if all of |
