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 | |
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.
-rw-r--r-- | hush.c | 382 | ||||
-rw-r--r-- | shell/hush.c | 382 |
2 files changed, 368 insertions, 396 deletions
@@ -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 |
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 |