diff options
author | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2007-02-23 21:08:58 +0000 |
---|---|---|
committer | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2007-02-23 21:08:58 +0000 |
commit | 96dd882fef3ec81174c00f2ec05267cbe67a144c (patch) | |
tree | cd4e3fa13064adaee22081ba312266b31bc4fa95 /shell | |
parent | a02e9ff74128cf5dc6bcba7142687e6c94447670 (diff) | |
download | busybox-w32-96dd882fef3ec81174c00f2ec05267cbe67a144c.tar.gz busybox-w32-96dd882fef3ec81174c00f2ec05267cbe67a144c.tar.bz2 busybox-w32-96dd882fef3ec81174c00f2ec05267cbe67a144c.zip |
ash: starting second round of cleanups. #1
git-svn-id: svn://busybox.net/trunk/busybox@17963 69ca8d6d-28ef-0310-b511-8ec308f3f277
Diffstat (limited to 'shell')
-rw-r--r-- | shell/ash.c | 2676 |
1 files changed, 1327 insertions, 1349 deletions
diff --git a/shell/ash.c b/shell/ash.c index 0e039322b..664a6fa16 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -408,6 +408,39 @@ out2str(const char *p) | |||
408 | 408 | ||
409 | /* ============ Parsing structures */ | 409 | /* ============ Parsing structures */ |
410 | 410 | ||
411 | /* control characters in argument strings */ | ||
412 | #define CTLESC '\201' /* escape next character */ | ||
413 | #define CTLVAR '\202' /* variable defn */ | ||
414 | #define CTLENDVAR '\203' | ||
415 | #define CTLBACKQ '\204' | ||
416 | #define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ | ||
417 | /* CTLBACKQ | CTLQUOTE == '\205' */ | ||
418 | #define CTLARI '\206' /* arithmetic expression */ | ||
419 | #define CTLENDARI '\207' | ||
420 | #define CTLQUOTEMARK '\210' | ||
421 | |||
422 | /* variable substitution byte (follows CTLVAR) */ | ||
423 | #define VSTYPE 0x0f /* type of variable substitution */ | ||
424 | #define VSNUL 0x10 /* colon--treat the empty string as unset */ | ||
425 | #define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ | ||
426 | |||
427 | /* values of VSTYPE field */ | ||
428 | #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ | ||
429 | #define VSMINUS 0x2 /* ${var-text} */ | ||
430 | #define VSPLUS 0x3 /* ${var+text} */ | ||
431 | #define VSQUESTION 0x4 /* ${var?message} */ | ||
432 | #define VSASSIGN 0x5 /* ${var=text} */ | ||
433 | #define VSTRIMRIGHT 0x6 /* ${var%pattern} */ | ||
434 | #define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ | ||
435 | #define VSTRIMLEFT 0x8 /* ${var#pattern} */ | ||
436 | #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ | ||
437 | #define VSLENGTH 0xa /* ${#var} */ | ||
438 | |||
439 | /* values of checkkwd variable */ | ||
440 | #define CHKALIAS 0x1 | ||
441 | #define CHKKWD 0x2 | ||
442 | #define CHKNL 0x4 | ||
443 | |||
411 | #define NCMD 0 | 444 | #define NCMD 0 |
412 | #define NPIPE 1 | 445 | #define NPIPE 1 |
413 | #define NREDIR 2 | 446 | #define NREDIR 2 |
@@ -551,6 +584,16 @@ struct funcnode { | |||
551 | union node n; | 584 | union node n; |
552 | }; | 585 | }; |
553 | 586 | ||
587 | /* | ||
588 | * Free a parse tree. | ||
589 | */ | ||
590 | static void | ||
591 | freefunc(struct funcnode *f) | ||
592 | { | ||
593 | if (f && --f->count < 0) | ||
594 | free(f); | ||
595 | } | ||
596 | |||
554 | 597 | ||
555 | /* ============ Debugging output */ | 598 | /* ============ Debugging output */ |
556 | 599 | ||
@@ -886,11 +929,11 @@ showtree(union node *n) | |||
886 | #endif /* DEBUG */ | 929 | #endif /* DEBUG */ |
887 | 930 | ||
888 | 931 | ||
889 | /* ============ Parser data | 932 | /* ============ Parser data */ |
890 | * | 933 | |
934 | /* | ||
891 | * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up. | 935 | * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up. |
892 | */ | 936 | */ |
893 | |||
894 | struct strlist { | 937 | struct strlist { |
895 | struct strlist *next; | 938 | struct strlist *next; |
896 | char *text; | 939 | char *text; |
@@ -1431,7 +1474,7 @@ single_quote(const char *s) | |||
1431 | } | 1474 | } |
1432 | 1475 | ||
1433 | 1476 | ||
1434 | /* ============ ... */ | 1477 | /* ============ nextopt */ |
1435 | 1478 | ||
1436 | static char **argptr; /* argument list for builtin commands */ | 1479 | static char **argptr; /* argument list for builtin commands */ |
1437 | static char *optionarg; /* set by nextopt (like getopt) */ | 1480 | static char *optionarg; /* set by nextopt (like getopt) */ |
@@ -2348,47 +2391,7 @@ pwdcmd(int argc, char **argv) | |||
2348 | } | 2391 | } |
2349 | 2392 | ||
2350 | 2393 | ||
2351 | /* ============ Unsorted yet */ | 2394 | /* ============ ... */ |
2352 | |||
2353 | |||
2354 | /* parser.h */ | ||
2355 | |||
2356 | /* control characters in argument strings */ | ||
2357 | #define CTL_FIRST '\201' /* first 'special' character */ | ||
2358 | #define CTLESC '\201' /* escape next character */ | ||
2359 | #define CTLVAR '\202' /* variable defn */ | ||
2360 | #define CTLENDVAR '\203' | ||
2361 | #define CTLBACKQ '\204' | ||
2362 | #define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ | ||
2363 | /* CTLBACKQ | CTLQUOTE == '\205' */ | ||
2364 | #define CTLARI '\206' /* arithmetic expression */ | ||
2365 | #define CTLENDARI '\207' | ||
2366 | #define CTLQUOTEMARK '\210' | ||
2367 | #define CTL_LAST '\210' /* last 'special' character */ | ||
2368 | |||
2369 | /* variable substitution byte (follows CTLVAR) */ | ||
2370 | #define VSTYPE 0x0f /* type of variable substitution */ | ||
2371 | #define VSNUL 0x10 /* colon--treat the empty string as unset */ | ||
2372 | #define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ | ||
2373 | |||
2374 | /* values of VSTYPE field */ | ||
2375 | #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ | ||
2376 | #define VSMINUS 0x2 /* ${var-text} */ | ||
2377 | #define VSPLUS 0x3 /* ${var+text} */ | ||
2378 | #define VSQUESTION 0x4 /* ${var?message} */ | ||
2379 | #define VSASSIGN 0x5 /* ${var=text} */ | ||
2380 | #define VSTRIMRIGHT 0x6 /* ${var%pattern} */ | ||
2381 | #define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ | ||
2382 | #define VSTRIMLEFT 0x8 /* ${var#pattern} */ | ||
2383 | #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ | ||
2384 | #define VSLENGTH 0xa /* ${#var} */ | ||
2385 | |||
2386 | /* values of checkkwd variable */ | ||
2387 | #define CHKALIAS 0x1 | ||
2388 | #define CHKKWD 0x2 | ||
2389 | #define CHKNL 0x4 | ||
2390 | |||
2391 | #define IBUFSIZ (BUFSIZ + 1) | ||
2392 | 2395 | ||
2393 | /* | 2396 | /* |
2394 | * NEOF is returned by parsecmd when it encounters an end of file. It | 2397 | * NEOF is returned by parsecmd when it encounters an end of file. It |
@@ -2404,6 +2407,7 @@ static int parselleft; /* copy of parsefile->lleft */ | |||
2404 | /* next character in input buffer */ | 2407 | /* next character in input buffer */ |
2405 | static char *parsenextc; /* copy of parsefile->nextc */ | 2408 | static char *parsenextc; /* copy of parsefile->nextc */ |
2406 | 2409 | ||
2410 | #define IBUFSIZ (BUFSIZ + 1) | ||
2407 | #define basebuf bb_common_bufsiz1 /* buffer for top level input file */ | 2411 | #define basebuf bb_common_bufsiz1 /* buffer for top level input file */ |
2408 | 2412 | ||
2409 | static int tokpushback; /* last token pushed back */ | 2413 | static int tokpushback; /* last token pushed back */ |
@@ -2431,98 +2435,6 @@ static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' }; | |||
2431 | 2435 | ||
2432 | #define xlikely(x) __builtin_expect((x),1) | 2436 | #define xlikely(x) __builtin_expect((x),1) |
2433 | 2437 | ||
2434 | #define TEOF 0 | ||
2435 | #define TNL 1 | ||
2436 | #define TREDIR 2 | ||
2437 | #define TWORD 3 | ||
2438 | #define TSEMI 4 | ||
2439 | #define TBACKGND 5 | ||
2440 | #define TAND 6 | ||
2441 | #define TOR 7 | ||
2442 | #define TPIPE 8 | ||
2443 | #define TLP 9 | ||
2444 | #define TRP 10 | ||
2445 | #define TENDCASE 11 | ||
2446 | #define TENDBQUOTE 12 | ||
2447 | #define TNOT 13 | ||
2448 | #define TCASE 14 | ||
2449 | #define TDO 15 | ||
2450 | #define TDONE 16 | ||
2451 | #define TELIF 17 | ||
2452 | #define TELSE 18 | ||
2453 | #define TESAC 19 | ||
2454 | #define TFI 20 | ||
2455 | #define TFOR 21 | ||
2456 | #define TIF 22 | ||
2457 | #define TIN 23 | ||
2458 | #define TTHEN 24 | ||
2459 | #define TUNTIL 25 | ||
2460 | #define TWHILE 26 | ||
2461 | #define TBEGIN 27 | ||
2462 | #define TEND 28 | ||
2463 | |||
2464 | /* first char is indicating which tokens mark the end of a list */ | ||
2465 | static const char *const tokname_array[] = { | ||
2466 | "\1end of file", | ||
2467 | "\0newline", | ||
2468 | "\0redirection", | ||
2469 | "\0word", | ||
2470 | "\0;", | ||
2471 | "\0&", | ||
2472 | "\0&&", | ||
2473 | "\0||", | ||
2474 | "\0|", | ||
2475 | "\0(", | ||
2476 | "\1)", | ||
2477 | "\1;;", | ||
2478 | "\1`", | ||
2479 | #define KWDOFFSET 13 | ||
2480 | /* the following are keywords */ | ||
2481 | "\0!", | ||
2482 | "\0case", | ||
2483 | "\1do", | ||
2484 | "\1done", | ||
2485 | "\1elif", | ||
2486 | "\1else", | ||
2487 | "\1esac", | ||
2488 | "\1fi", | ||
2489 | "\0for", | ||
2490 | "\0if", | ||
2491 | "\0in", | ||
2492 | "\1then", | ||
2493 | "\0until", | ||
2494 | "\0while", | ||
2495 | "\0{", | ||
2496 | "\1}", | ||
2497 | }; | ||
2498 | |||
2499 | static const char * | ||
2500 | tokname(int tok) | ||
2501 | { | ||
2502 | static char buf[16]; | ||
2503 | |||
2504 | if (tok >= TSEMI) | ||
2505 | buf[0] = '"'; | ||
2506 | sprintf(buf + (tok >= TSEMI), "%s%c", | ||
2507 | tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0)); | ||
2508 | return buf; | ||
2509 | } | ||
2510 | |||
2511 | /* Wrapper around strcmp for qsort/bsearch/... */ | ||
2512 | static int | ||
2513 | pstrcmp(const void *a, const void *b) | ||
2514 | { | ||
2515 | return strcmp((const char *) a, (*(const char *const *) b) + 1); | ||
2516 | } | ||
2517 | |||
2518 | static const char *const * | ||
2519 | findkwd(const char *s) | ||
2520 | { | ||
2521 | return bsearch(s, tokname_array + KWDOFFSET, | ||
2522 | (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET, | ||
2523 | sizeof(const char *), pstrcmp); | ||
2524 | } | ||
2525 | |||
2526 | /* Syntax classes */ | 2438 | /* Syntax classes */ |
2527 | #define CWORD 0 /* character is nothing special */ | 2439 | #define CWORD 0 /* character is nothing special */ |
2528 | #define CNL 1 /* newline character */ | 2440 | #define CNL 1 /* newline character */ |
@@ -2966,99 +2878,8 @@ static const char syntax_index_table[258] = { | |||
2966 | #endif /* USE_SIT_FUNCTION */ | 2878 | #endif /* USE_SIT_FUNCTION */ |
2967 | 2879 | ||
2968 | 2880 | ||
2969 | /* alias.c */ | ||
2970 | |||
2971 | #define ATABSIZE 39 | ||
2972 | |||
2973 | static int funcblocksize; /* size of structures in function */ | ||
2974 | static int funcstringsize; /* size of strings in node */ | ||
2975 | static void *funcblock; /* block to allocate function from */ | ||
2976 | static char *funcstring; /* block to allocate strings from */ | ||
2977 | |||
2978 | static const short nodesize[26] = { | ||
2979 | SHELL_ALIGN(sizeof(struct ncmd)), | ||
2980 | SHELL_ALIGN(sizeof(struct npipe)), | ||
2981 | SHELL_ALIGN(sizeof(struct nredir)), | ||
2982 | SHELL_ALIGN(sizeof(struct nredir)), | ||
2983 | SHELL_ALIGN(sizeof(struct nredir)), | ||
2984 | SHELL_ALIGN(sizeof(struct nbinary)), | ||
2985 | SHELL_ALIGN(sizeof(struct nbinary)), | ||
2986 | SHELL_ALIGN(sizeof(struct nbinary)), | ||
2987 | SHELL_ALIGN(sizeof(struct nif)), | ||
2988 | SHELL_ALIGN(sizeof(struct nbinary)), | ||
2989 | SHELL_ALIGN(sizeof(struct nbinary)), | ||
2990 | SHELL_ALIGN(sizeof(struct nfor)), | ||
2991 | SHELL_ALIGN(sizeof(struct ncase)), | ||
2992 | SHELL_ALIGN(sizeof(struct nclist)), | ||
2993 | SHELL_ALIGN(sizeof(struct narg)), | ||
2994 | SHELL_ALIGN(sizeof(struct narg)), | ||
2995 | SHELL_ALIGN(sizeof(struct nfile)), | ||
2996 | SHELL_ALIGN(sizeof(struct nfile)), | ||
2997 | SHELL_ALIGN(sizeof(struct nfile)), | ||
2998 | SHELL_ALIGN(sizeof(struct nfile)), | ||
2999 | SHELL_ALIGN(sizeof(struct nfile)), | ||
3000 | SHELL_ALIGN(sizeof(struct ndup)), | ||
3001 | SHELL_ALIGN(sizeof(struct ndup)), | ||
3002 | SHELL_ALIGN(sizeof(struct nhere)), | ||
3003 | SHELL_ALIGN(sizeof(struct nhere)), | ||
3004 | SHELL_ALIGN(sizeof(struct nnot)), | ||
3005 | }; | ||
3006 | |||
3007 | static void calcsize(union node *); | ||
3008 | static void sizenodelist(struct nodelist *); | ||
3009 | static union node *copynode(union node *); | ||
3010 | static struct nodelist *copynodelist(struct nodelist *); | ||
3011 | static char *nodeckstrdup(char *); | ||
3012 | |||
3013 | static int evalskip; /* set if we are skipping commands */ | ||
3014 | static int skipcount; /* number of levels to skip */ | ||
3015 | static int funcnest; /* depth of function calls */ | ||
3016 | |||
3017 | /* reasons for skipping commands (see comment on breakcmd routine) */ | ||
3018 | #define SKIPBREAK (1 << 0) | ||
3019 | #define SKIPCONT (1 << 1) | ||
3020 | #define SKIPFUNC (1 << 2) | ||
3021 | #define SKIPFILE (1 << 3) | ||
3022 | #define SKIPEVAL (1 << 4) | ||
3023 | |||
3024 | |||
3025 | /* exec.h */ | 2881 | /* exec.h */ |
3026 | 2882 | ||
3027 | /* values of cmdtype */ | ||
3028 | #define CMDUNKNOWN -1 /* no entry in table for command */ | ||
3029 | #define CMDNORMAL 0 /* command is an executable program */ | ||
3030 | #define CMDFUNCTION 1 /* command is a shell function */ | ||
3031 | #define CMDBUILTIN 2 /* command is a shell builtin */ | ||
3032 | |||
3033 | struct builtincmd { | ||
3034 | const char *name; | ||
3035 | int (*builtin)(int, char **); | ||
3036 | /* unsigned flags; */ | ||
3037 | }; | ||
3038 | |||
3039 | struct cmdentry { | ||
3040 | int cmdtype; | ||
3041 | union param { | ||
3042 | int index; | ||
3043 | const struct builtincmd *cmd; | ||
3044 | struct funcnode *func; | ||
3045 | } u; | ||
3046 | }; | ||
3047 | |||
3048 | /* action to find_command() */ | ||
3049 | #define DO_ERR 0x01 /* prints errors */ | ||
3050 | #define DO_ABS 0x02 /* checks absolute paths */ | ||
3051 | #define DO_NOFUNC 0x04 /* don't return shell functions, for command */ | ||
3052 | #define DO_ALTPATH 0x08 /* using alternate path */ | ||
3053 | #define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ | ||
3054 | |||
3055 | static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN; | ||
3056 | static char *padvance(const char **, const char *); | ||
3057 | static void find_command(char *, struct cmdentry *, int, const char *); | ||
3058 | static struct builtincmd *find_builtin(const char *); | ||
3059 | static void defun(char *, union node *); | ||
3060 | static void unsetfunc(const char *); | ||
3061 | |||
3062 | #if ENABLE_ASH_MATH_SUPPORT_64 | 2883 | #if ENABLE_ASH_MATH_SUPPORT_64 |
3063 | typedef int64_t arith_t; | 2884 | typedef int64_t arith_t; |
3064 | #define arith_t_type long long | 2885 | #define arith_t_type long long |
@@ -3222,6 +3043,8 @@ static int is_safe_applet(char *name) | |||
3222 | #define ALIASINUSE 1 | 3043 | #define ALIASINUSE 1 |
3223 | #define ALIASDEAD 2 | 3044 | #define ALIASDEAD 2 |
3224 | 3045 | ||
3046 | #define ATABSIZE 39 | ||
3047 | |||
3225 | struct alias { | 3048 | struct alias { |
3226 | struct alias *next; | 3049 | struct alias *next; |
3227 | char *name; | 3050 | char *name; |
@@ -4899,13 +4722,937 @@ casematch(union node *pattern, char *val) | |||
4899 | } | 4722 | } |
4900 | 4723 | ||
4901 | 4724 | ||
4725 | /* ============ find_command */ | ||
4726 | |||
4727 | struct builtincmd { | ||
4728 | const char *name; | ||
4729 | int (*builtin)(int, char **); | ||
4730 | /* unsigned flags; */ | ||
4731 | }; | ||
4732 | #define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1) | ||
4733 | #define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2) | ||
4734 | #define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4) | ||
4735 | |||
4736 | struct cmdentry { | ||
4737 | int cmdtype; | ||
4738 | union param { | ||
4739 | int index; | ||
4740 | const struct builtincmd *cmd; | ||
4741 | struct funcnode *func; | ||
4742 | } u; | ||
4743 | }; | ||
4744 | /* values of cmdtype */ | ||
4745 | #define CMDUNKNOWN -1 /* no entry in table for command */ | ||
4746 | #define CMDNORMAL 0 /* command is an executable program */ | ||
4747 | #define CMDFUNCTION 1 /* command is a shell function */ | ||
4748 | #define CMDBUILTIN 2 /* command is a shell builtin */ | ||
4749 | |||
4750 | /* action to find_command() */ | ||
4751 | #define DO_ERR 0x01 /* prints errors */ | ||
4752 | #define DO_ABS 0x02 /* checks absolute paths */ | ||
4753 | #define DO_NOFUNC 0x04 /* don't return shell functions, for command */ | ||
4754 | #define DO_ALTPATH 0x08 /* using alternate path */ | ||
4755 | #define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ | ||
4756 | |||
4757 | static void find_command(char *, struct cmdentry *, int, const char *); | ||
4758 | |||
4759 | |||
4760 | /* ============ Hashing commands */ | ||
4761 | |||
4762 | /* | ||
4763 | * When commands are first encountered, they are entered in a hash table. | ||
4764 | * This ensures that a full path search will not have to be done for them | ||
4765 | * on each invocation. | ||
4766 | * | ||
4767 | * We should investigate converting to a linear search, even though that | ||
4768 | * would make the command name "hash" a misnomer. | ||
4769 | */ | ||
4770 | |||
4771 | #define CMDTABLESIZE 31 /* should be prime */ | ||
4772 | #define ARB 1 /* actual size determined at run time */ | ||
4773 | |||
4774 | struct tblentry { | ||
4775 | struct tblentry *next; /* next entry in hash chain */ | ||
4776 | union param param; /* definition of builtin function */ | ||
4777 | short cmdtype; /* index identifying command */ | ||
4778 | char rehash; /* if set, cd done since entry created */ | ||
4779 | char cmdname[ARB]; /* name of command */ | ||
4780 | }; | ||
4781 | |||
4782 | static struct tblentry *cmdtable[CMDTABLESIZE]; | ||
4783 | static int builtinloc = -1; /* index in path of %builtin, or -1 */ | ||
4784 | |||
4785 | static void | ||
4786 | tryexec(char *cmd, char **argv, char **envp) | ||
4787 | { | ||
4788 | int repeated = 0; | ||
4789 | struct BB_applet *a; | ||
4790 | int argc = 0; | ||
4791 | char **c; | ||
4792 | |||
4793 | if (strchr(cmd, '/') == NULL | ||
4794 | && (a = find_applet_by_name(cmd)) != NULL | ||
4795 | && is_safe_applet(cmd) | ||
4796 | ) { | ||
4797 | c = argv; | ||
4798 | while (*c != NULL) { | ||
4799 | c++; argc++; | ||
4800 | } | ||
4801 | applet_name = cmd; | ||
4802 | exit(a->main(argc, argv)); | ||
4803 | } | ||
4804 | #if ENABLE_FEATURE_SH_STANDALONE_SHELL | ||
4805 | if (find_applet_by_name(cmd) != NULL) { | ||
4806 | /* re-exec ourselves with the new arguments */ | ||
4807 | execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp); | ||
4808 | /* If they called chroot or otherwise made the binary no longer | ||
4809 | * executable, fall through */ | ||
4810 | } | ||
4811 | #endif | ||
4812 | |||
4813 | repeat: | ||
4814 | #ifdef SYSV | ||
4815 | do { | ||
4816 | execve(cmd, argv, envp); | ||
4817 | } while (errno == EINTR); | ||
4818 | #else | ||
4819 | execve(cmd, argv, envp); | ||
4820 | #endif | ||
4821 | if (repeated++) { | ||
4822 | free(argv); | ||
4823 | } else if (errno == ENOEXEC) { | ||
4824 | char **ap; | ||
4825 | char **new; | ||
4826 | |||
4827 | for (ap = argv; *ap; ap++) | ||
4828 | ; | ||
4829 | ap = new = ckmalloc((ap - argv + 2) * sizeof(char *)); | ||
4830 | ap[1] = cmd; | ||
4831 | *ap = cmd = (char *)DEFAULT_SHELL; | ||
4832 | ap += 2; | ||
4833 | argv++; | ||
4834 | while ((*ap++ = *argv++)) | ||
4835 | ; | ||
4836 | argv = new; | ||
4837 | goto repeat; | ||
4838 | } | ||
4839 | } | ||
4840 | |||
4841 | /* | ||
4842 | * Exec a program. Never returns. If you change this routine, you may | ||
4843 | * have to change the find_command routine as well. | ||
4844 | */ | ||
4845 | #define environment() listvars(VEXPORT, VUNSET, 0) | ||
4846 | static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN; | ||
4847 | static void | ||
4848 | shellexec(char **argv, const char *path, int idx) | ||
4849 | { | ||
4850 | char *cmdname; | ||
4851 | int e; | ||
4852 | char **envp; | ||
4853 | int exerrno; | ||
4854 | |||
4855 | clearredir(1); | ||
4856 | envp = environment(); | ||
4857 | if (strchr(argv[0], '/') || is_safe_applet(argv[0]) | ||
4858 | #if ENABLE_FEATURE_SH_STANDALONE_SHELL | ||
4859 | || find_applet_by_name(argv[0]) | ||
4860 | #endif | ||
4861 | ) { | ||
4862 | tryexec(argv[0], argv, envp); | ||
4863 | e = errno; | ||
4864 | } else { | ||
4865 | e = ENOENT; | ||
4866 | while ((cmdname = padvance(&path, argv[0])) != NULL) { | ||
4867 | if (--idx < 0 && pathopt == NULL) { | ||
4868 | tryexec(cmdname, argv, envp); | ||
4869 | if (errno != ENOENT && errno != ENOTDIR) | ||
4870 | e = errno; | ||
4871 | } | ||
4872 | stunalloc(cmdname); | ||
4873 | } | ||
4874 | } | ||
4875 | |||
4876 | /* Map to POSIX errors */ | ||
4877 | switch (e) { | ||
4878 | case EACCES: | ||
4879 | exerrno = 126; | ||
4880 | break; | ||
4881 | case ENOENT: | ||
4882 | exerrno = 127; | ||
4883 | break; | ||
4884 | default: | ||
4885 | exerrno = 2; | ||
4886 | break; | ||
4887 | } | ||
4888 | exitstatus = exerrno; | ||
4889 | TRACE(("shellexec failed for %s, errno %d, suppressint %d\n", | ||
4890 | argv[0], e, suppressint )); | ||
4891 | ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found")); | ||
4892 | /* NOTREACHED */ | ||
4893 | } | ||
4894 | |||
4895 | static void | ||
4896 | printentry(struct tblentry *cmdp) | ||
4897 | { | ||
4898 | int idx; | ||
4899 | const char *path; | ||
4900 | char *name; | ||
4901 | |||
4902 | idx = cmdp->param.index; | ||
4903 | path = pathval(); | ||
4904 | do { | ||
4905 | name = padvance(&path, cmdp->cmdname); | ||
4906 | stunalloc(name); | ||
4907 | } while (--idx >= 0); | ||
4908 | out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); | ||
4909 | } | ||
4910 | |||
4911 | /* | ||
4912 | * Clear out command entries. The argument specifies the first entry in | ||
4913 | * PATH which has changed. | ||
4914 | */ | ||
4915 | static void | ||
4916 | clearcmdentry(int firstchange) | ||
4917 | { | ||
4918 | struct tblentry **tblp; | ||
4919 | struct tblentry **pp; | ||
4920 | struct tblentry *cmdp; | ||
4921 | |||
4922 | INT_OFF; | ||
4923 | for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) { | ||
4924 | pp = tblp; | ||
4925 | while ((cmdp = *pp) != NULL) { | ||
4926 | if ((cmdp->cmdtype == CMDNORMAL && | ||
4927 | cmdp->param.index >= firstchange) | ||
4928 | || (cmdp->cmdtype == CMDBUILTIN && | ||
4929 | builtinloc >= firstchange) | ||
4930 | ) { | ||
4931 | *pp = cmdp->next; | ||
4932 | free(cmdp); | ||
4933 | } else { | ||
4934 | pp = &cmdp->next; | ||
4935 | } | ||
4936 | } | ||
4937 | } | ||
4938 | INT_ON; | ||
4939 | } | ||
4940 | |||
4941 | /* | ||
4942 | * Locate a command in the command hash table. If "add" is nonzero, | ||
4943 | * add the command to the table if it is not already present. The | ||
4944 | * variable "lastcmdentry" is set to point to the address of the link | ||
4945 | * pointing to the entry, so that delete_cmd_entry can delete the | ||
4946 | * entry. | ||
4947 | * | ||
4948 | * Interrupts must be off if called with add != 0. | ||
4949 | */ | ||
4950 | static struct tblentry **lastcmdentry; | ||
4951 | |||
4952 | static struct tblentry * | ||
4953 | cmdlookup(const char *name, int add) | ||
4954 | { | ||
4955 | unsigned int hashval; | ||
4956 | const char *p; | ||
4957 | struct tblentry *cmdp; | ||
4958 | struct tblentry **pp; | ||
4959 | |||
4960 | p = name; | ||
4961 | hashval = (unsigned char)*p << 4; | ||
4962 | while (*p) | ||
4963 | hashval += (unsigned char)*p++; | ||
4964 | hashval &= 0x7FFF; | ||
4965 | pp = &cmdtable[hashval % CMDTABLESIZE]; | ||
4966 | for (cmdp = *pp; cmdp; cmdp = cmdp->next) { | ||
4967 | if (strcmp(cmdp->cmdname, name) == 0) | ||
4968 | break; | ||
4969 | pp = &cmdp->next; | ||
4970 | } | ||
4971 | if (add && cmdp == NULL) { | ||
4972 | cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB | ||
4973 | + strlen(name) + 1); | ||
4974 | cmdp->next = NULL; | ||
4975 | cmdp->cmdtype = CMDUNKNOWN; | ||
4976 | strcpy(cmdp->cmdname, name); | ||
4977 | } | ||
4978 | lastcmdentry = pp; | ||
4979 | return cmdp; | ||
4980 | } | ||
4981 | |||
4982 | /* | ||
4983 | * Delete the command entry returned on the last lookup. | ||
4984 | */ | ||
4985 | static void | ||
4986 | delete_cmd_entry(void) | ||
4987 | { | ||
4988 | struct tblentry *cmdp; | ||
4989 | |||
4990 | INT_OFF; | ||
4991 | cmdp = *lastcmdentry; | ||
4992 | *lastcmdentry = cmdp->next; | ||
4993 | if (cmdp->cmdtype == CMDFUNCTION) | ||
4994 | freefunc(cmdp->param.func); | ||
4995 | free(cmdp); | ||
4996 | INT_ON; | ||
4997 | } | ||
4998 | |||
4999 | /* | ||
5000 | * Add a new command entry, replacing any existing command entry for | ||
5001 | * the same name - except special builtins. | ||
5002 | */ | ||
5003 | static void | ||
5004 | addcmdentry(char *name, struct cmdentry *entry) | ||
5005 | { | ||
5006 | struct tblentry *cmdp; | ||
5007 | |||
5008 | cmdp = cmdlookup(name, 1); | ||
5009 | if (cmdp->cmdtype == CMDFUNCTION) { | ||
5010 | freefunc(cmdp->param.func); | ||
5011 | } | ||
5012 | cmdp->cmdtype = entry->cmdtype; | ||
5013 | cmdp->param = entry->u; | ||
5014 | cmdp->rehash = 0; | ||
5015 | } | ||
5016 | |||
5017 | static int | ||
5018 | hashcmd(int argc, char **argv) | ||
5019 | { | ||
5020 | struct tblentry **pp; | ||
5021 | struct tblentry *cmdp; | ||
5022 | int c; | ||
5023 | struct cmdentry entry; | ||
5024 | char *name; | ||
5025 | |||
5026 | while ((c = nextopt("r")) != '\0') { | ||
5027 | clearcmdentry(0); | ||
5028 | return 0; | ||
5029 | } | ||
5030 | if (*argptr == NULL) { | ||
5031 | for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) { | ||
5032 | for (cmdp = *pp; cmdp; cmdp = cmdp->next) { | ||
5033 | if (cmdp->cmdtype == CMDNORMAL) | ||
5034 | printentry(cmdp); | ||
5035 | } | ||
5036 | } | ||
5037 | return 0; | ||
5038 | } | ||
5039 | c = 0; | ||
5040 | while ((name = *argptr) != NULL) { | ||
5041 | cmdp = cmdlookup(name, 0); | ||
5042 | if (cmdp != NULL | ||
5043 | && (cmdp->cmdtype == CMDNORMAL | ||
5044 | || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) | ||
5045 | delete_cmd_entry(); | ||
5046 | find_command(name, &entry, DO_ERR, pathval()); | ||
5047 | if (entry.cmdtype == CMDUNKNOWN) | ||
5048 | c = 1; | ||
5049 | argptr++; | ||
5050 | } | ||
5051 | return c; | ||
5052 | } | ||
5053 | |||
5054 | /* | ||
5055 | * Called when a cd is done. Marks all commands so the next time they | ||
5056 | * are executed they will be rehashed. | ||
5057 | */ | ||
5058 | static void | ||
5059 | hashcd(void) | ||
5060 | { | ||
5061 | struct tblentry **pp; | ||
5062 | struct tblentry *cmdp; | ||
5063 | |||
5064 | for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) { | ||
5065 | for (cmdp = *pp; cmdp; cmdp = cmdp->next) { | ||
5066 | if (cmdp->cmdtype == CMDNORMAL || ( | ||
5067 | cmdp->cmdtype == CMDBUILTIN && | ||
5068 | !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) && | ||
5069 | builtinloc > 0 | ||
5070 | )) | ||
5071 | cmdp->rehash = 1; | ||
5072 | } | ||
5073 | } | ||
5074 | } | ||
5075 | |||
5076 | /* | ||
5077 | * Fix command hash table when PATH changed. | ||
5078 | * Called before PATH is changed. The argument is the new value of PATH; | ||
5079 | * pathval() still returns the old value at this point. | ||
5080 | * Called with interrupts off. | ||
5081 | */ | ||
5082 | static void | ||
5083 | changepath(const char *newval) | ||
5084 | { | ||
5085 | const char *old, *new; | ||
5086 | int idx; | ||
5087 | int firstchange; | ||
5088 | int idx_bltin; | ||
5089 | |||
5090 | old = pathval(); | ||
5091 | new = newval; | ||
5092 | firstchange = 9999; /* assume no change */ | ||
5093 | idx = 0; | ||
5094 | idx_bltin = -1; | ||
5095 | for (;;) { | ||
5096 | if (*old != *new) { | ||
5097 | firstchange = idx; | ||
5098 | if ((*old == '\0' && *new == ':') | ||
5099 | || (*old == ':' && *new == '\0')) | ||
5100 | firstchange++; | ||
5101 | old = new; /* ignore subsequent differences */ | ||
5102 | } | ||
5103 | if (*new == '\0') | ||
5104 | break; | ||
5105 | if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin")) | ||
5106 | idx_bltin = idx; | ||
5107 | if (*new == ':') { | ||
5108 | idx++; | ||
5109 | } | ||
5110 | new++, old++; | ||
5111 | } | ||
5112 | if (builtinloc < 0 && idx_bltin >= 0) | ||
5113 | builtinloc = idx_bltin; /* zap builtins */ | ||
5114 | if (builtinloc >= 0 && idx_bltin < 0) | ||
5115 | firstchange = 0; | ||
5116 | clearcmdentry(firstchange); | ||
5117 | builtinloc = idx_bltin; | ||
5118 | } | ||
5119 | |||
5120 | #define TEOF 0 | ||
5121 | #define TNL 1 | ||
5122 | #define TREDIR 2 | ||
5123 | #define TWORD 3 | ||
5124 | #define TSEMI 4 | ||
5125 | #define TBACKGND 5 | ||
5126 | #define TAND 6 | ||
5127 | #define TOR 7 | ||
5128 | #define TPIPE 8 | ||
5129 | #define TLP 9 | ||
5130 | #define TRP 10 | ||
5131 | #define TENDCASE 11 | ||
5132 | #define TENDBQUOTE 12 | ||
5133 | #define TNOT 13 | ||
5134 | #define TCASE 14 | ||
5135 | #define TDO 15 | ||
5136 | #define TDONE 16 | ||
5137 | #define TELIF 17 | ||
5138 | #define TELSE 18 | ||
5139 | #define TESAC 19 | ||
5140 | #define TFI 20 | ||
5141 | #define TFOR 21 | ||
5142 | #define TIF 22 | ||
5143 | #define TIN 23 | ||
5144 | #define TTHEN 24 | ||
5145 | #define TUNTIL 25 | ||
5146 | #define TWHILE 26 | ||
5147 | #define TBEGIN 27 | ||
5148 | #define TEND 28 | ||
5149 | |||
5150 | /* first char is indicating which tokens mark the end of a list */ | ||
5151 | static const char *const tokname_array[] = { | ||
5152 | "\1end of file", | ||
5153 | "\0newline", | ||
5154 | "\0redirection", | ||
5155 | "\0word", | ||
5156 | "\0;", | ||
5157 | "\0&", | ||
5158 | "\0&&", | ||
5159 | "\0||", | ||
5160 | "\0|", | ||
5161 | "\0(", | ||
5162 | "\1)", | ||
5163 | "\1;;", | ||
5164 | "\1`", | ||
5165 | #define KWDOFFSET 13 | ||
5166 | /* the following are keywords */ | ||
5167 | "\0!", | ||
5168 | "\0case", | ||
5169 | "\1do", | ||
5170 | "\1done", | ||
5171 | "\1elif", | ||
5172 | "\1else", | ||
5173 | "\1esac", | ||
5174 | "\1fi", | ||
5175 | "\0for", | ||
5176 | "\0if", | ||
5177 | "\0in", | ||
5178 | "\1then", | ||
5179 | "\0until", | ||
5180 | "\0while", | ||
5181 | "\0{", | ||
5182 | "\1}", | ||
5183 | }; | ||
5184 | |||
5185 | static const char * | ||
5186 | tokname(int tok) | ||
5187 | { | ||
5188 | static char buf[16]; | ||
5189 | |||
5190 | if (tok >= TSEMI) | ||
5191 | buf[0] = '"'; | ||
5192 | sprintf(buf + (tok >= TSEMI), "%s%c", | ||
5193 | tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0)); | ||
5194 | return buf; | ||
5195 | } | ||
5196 | |||
5197 | /* Wrapper around strcmp for qsort/bsearch/... */ | ||
5198 | static int | ||
5199 | pstrcmp(const void *a, const void *b) | ||
5200 | { | ||
5201 | return strcmp((const char *) a, (*(const char *const *) b) + 1); | ||
5202 | } | ||
5203 | |||
5204 | static const char *const * | ||
5205 | findkwd(const char *s) | ||
5206 | { | ||
5207 | return bsearch(s, tokname_array + KWDOFFSET, | ||
5208 | (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET, | ||
5209 | sizeof(const char *), pstrcmp); | ||
5210 | } | ||
5211 | |||
5212 | /* | ||
5213 | * Locate and print what a word is... | ||
5214 | */ | ||
5215 | #if ENABLE_ASH_CMDCMD | ||
5216 | static int | ||
5217 | describe_command(char *command, int describe_command_verbose) | ||
5218 | #else | ||
5219 | #define describe_command_verbose 1 | ||
5220 | static int | ||
5221 | describe_command(char *command) | ||
5222 | #endif | ||
5223 | { | ||
5224 | struct cmdentry entry; | ||
5225 | struct tblentry *cmdp; | ||
5226 | #if ENABLE_ASH_ALIAS | ||
5227 | const struct alias *ap; | ||
5228 | #endif | ||
5229 | const char *path = pathval(); | ||
5230 | |||
5231 | if (describe_command_verbose) { | ||
5232 | out1str(command); | ||
5233 | } | ||
5234 | |||
5235 | /* First look at the keywords */ | ||
5236 | if (findkwd(command)) { | ||
5237 | out1str(describe_command_verbose ? " is a shell keyword" : command); | ||
5238 | goto out; | ||
5239 | } | ||
5240 | |||
5241 | #if ENABLE_ASH_ALIAS | ||
5242 | /* Then look at the aliases */ | ||
5243 | ap = lookupalias(command, 0); | ||
5244 | if (ap != NULL) { | ||
5245 | if (describe_command_verbose) { | ||
5246 | out1fmt(" is an alias for %s", ap->val); | ||
5247 | } else { | ||
5248 | out1str("alias "); | ||
5249 | printalias(ap); | ||
5250 | return 0; | ||
5251 | } | ||
5252 | goto out; | ||
5253 | } | ||
5254 | #endif | ||
5255 | /* Then check if it is a tracked alias */ | ||
5256 | cmdp = cmdlookup(command, 0); | ||
5257 | if (cmdp != NULL) { | ||
5258 | entry.cmdtype = cmdp->cmdtype; | ||
5259 | entry.u = cmdp->param; | ||
5260 | } else { | ||
5261 | /* Finally use brute force */ | ||
5262 | find_command(command, &entry, DO_ABS, path); | ||
5263 | } | ||
5264 | |||
5265 | switch (entry.cmdtype) { | ||
5266 | case CMDNORMAL: { | ||
5267 | int j = entry.u.index; | ||
5268 | char *p; | ||
5269 | if (j == -1) { | ||
5270 | p = command; | ||
5271 | } else { | ||
5272 | do { | ||
5273 | p = padvance(&path, command); | ||
5274 | stunalloc(p); | ||
5275 | } while (--j >= 0); | ||
5276 | } | ||
5277 | if (describe_command_verbose) { | ||
5278 | out1fmt(" is%s %s", | ||
5279 | (cmdp ? " a tracked alias for" : nullstr), p | ||
5280 | ); | ||
5281 | } else { | ||
5282 | out1str(p); | ||
5283 | } | ||
5284 | break; | ||
5285 | } | ||
5286 | |||
5287 | case CMDFUNCTION: | ||
5288 | if (describe_command_verbose) { | ||
5289 | out1str(" is a shell function"); | ||
5290 | } else { | ||
5291 | out1str(command); | ||
5292 | } | ||
5293 | break; | ||
5294 | |||
5295 | case CMDBUILTIN: | ||
5296 | if (describe_command_verbose) { | ||
5297 | out1fmt(" is a %sshell builtin", | ||
5298 | IS_BUILTIN_SPECIAL(entry.u.cmd) ? | ||
5299 | "special " : nullstr | ||
5300 | ); | ||
5301 | } else { | ||
5302 | out1str(command); | ||
5303 | } | ||
5304 | break; | ||
5305 | |||
5306 | default: | ||
5307 | if (describe_command_verbose) { | ||
5308 | out1str(": not found\n"); | ||
5309 | } | ||
5310 | return 127; | ||
5311 | } | ||
5312 | out: | ||
5313 | outstr("\n", stdout); | ||
5314 | return 0; | ||
5315 | } | ||
5316 | |||
5317 | static int | ||
5318 | typecmd(int argc, char **argv) | ||
5319 | { | ||
5320 | int i; | ||
5321 | int err = 0; | ||
5322 | |||
5323 | for (i = 1; i < argc; i++) { | ||
5324 | #if ENABLE_ASH_CMDCMD | ||
5325 | err |= describe_command(argv[i], 1); | ||
5326 | #else | ||
5327 | err |= describe_command(argv[i]); | ||
5328 | #endif | ||
5329 | } | ||
5330 | return err; | ||
5331 | } | ||
5332 | |||
5333 | #if ENABLE_ASH_CMDCMD | ||
5334 | static int | ||
5335 | commandcmd(int argc, char **argv) | ||
5336 | { | ||
5337 | int c; | ||
5338 | enum { | ||
5339 | VERIFY_BRIEF = 1, | ||
5340 | VERIFY_VERBOSE = 2, | ||
5341 | } verify = 0; | ||
5342 | |||
5343 | while ((c = nextopt("pvV")) != '\0') | ||
5344 | if (c == 'V') | ||
5345 | verify |= VERIFY_VERBOSE; | ||
5346 | else if (c == 'v') | ||
5347 | verify |= VERIFY_BRIEF; | ||
5348 | #if DEBUG | ||
5349 | else if (c != 'p') | ||
5350 | abort(); | ||
5351 | #endif | ||
5352 | if (verify) | ||
5353 | return describe_command(*argptr, verify - VERIFY_BRIEF); | ||
5354 | |||
5355 | return 0; | ||
5356 | } | ||
5357 | #endif | ||
5358 | |||
5359 | |||
4902 | /* ============ eval.c */ | 5360 | /* ============ eval.c */ |
4903 | 5361 | ||
5362 | static int funcblocksize; /* size of structures in function */ | ||
5363 | static int funcstringsize; /* size of strings in node */ | ||
5364 | static void *funcblock; /* block to allocate function from */ | ||
5365 | static char *funcstring; /* block to allocate strings from */ | ||
5366 | |||
4904 | /* flags in argument to evaltree */ | 5367 | /* flags in argument to evaltree */ |
4905 | #define EV_EXIT 01 /* exit after evaluating tree */ | 5368 | #define EV_EXIT 01 /* exit after evaluating tree */ |
4906 | #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ | 5369 | #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ |
4907 | #define EV_BACKCMD 04 /* command executing within back quotes */ | 5370 | #define EV_BACKCMD 04 /* command executing within back quotes */ |
4908 | 5371 | ||
5372 | static const short nodesize[26] = { | ||
5373 | SHELL_ALIGN(sizeof(struct ncmd)), | ||
5374 | SHELL_ALIGN(sizeof(struct npipe)), | ||
5375 | SHELL_ALIGN(sizeof(struct nredir)), | ||
5376 | SHELL_ALIGN(sizeof(struct nredir)), | ||
5377 | SHELL_ALIGN(sizeof(struct nredir)), | ||
5378 | SHELL_ALIGN(sizeof(struct nbinary)), | ||
5379 | SHELL_ALIGN(sizeof(struct nbinary)), | ||
5380 | SHELL_ALIGN(sizeof(struct nbinary)), | ||
5381 | SHELL_ALIGN(sizeof(struct nif)), | ||
5382 | SHELL_ALIGN(sizeof(struct nbinary)), | ||
5383 | SHELL_ALIGN(sizeof(struct nbinary)), | ||
5384 | SHELL_ALIGN(sizeof(struct nfor)), | ||
5385 | SHELL_ALIGN(sizeof(struct ncase)), | ||
5386 | SHELL_ALIGN(sizeof(struct nclist)), | ||
5387 | SHELL_ALIGN(sizeof(struct narg)), | ||
5388 | SHELL_ALIGN(sizeof(struct narg)), | ||
5389 | SHELL_ALIGN(sizeof(struct nfile)), | ||
5390 | SHELL_ALIGN(sizeof(struct nfile)), | ||
5391 | SHELL_ALIGN(sizeof(struct nfile)), | ||
5392 | SHELL_ALIGN(sizeof(struct nfile)), | ||
5393 | SHELL_ALIGN(sizeof(struct nfile)), | ||
5394 | SHELL_ALIGN(sizeof(struct ndup)), | ||
5395 | SHELL_ALIGN(sizeof(struct ndup)), | ||
5396 | SHELL_ALIGN(sizeof(struct nhere)), | ||
5397 | SHELL_ALIGN(sizeof(struct nhere)), | ||
5398 | SHELL_ALIGN(sizeof(struct nnot)), | ||
5399 | }; | ||
5400 | |||
5401 | static void calcsize(union node *n); | ||
5402 | |||
5403 | static void | ||
5404 | sizenodelist(struct nodelist *lp) | ||
5405 | { | ||
5406 | while (lp) { | ||
5407 | funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); | ||
5408 | calcsize(lp->n); | ||
5409 | lp = lp->next; | ||
5410 | } | ||
5411 | } | ||
5412 | |||
5413 | static void | ||
5414 | calcsize(union node *n) | ||
5415 | { | ||
5416 | if (n == NULL) | ||
5417 | return; | ||
5418 | funcblocksize += nodesize[n->type]; | ||
5419 | switch (n->type) { | ||
5420 | case NCMD: | ||
5421 | calcsize(n->ncmd.redirect); | ||
5422 | calcsize(n->ncmd.args); | ||
5423 | calcsize(n->ncmd.assign); | ||
5424 | break; | ||
5425 | case NPIPE: | ||
5426 | sizenodelist(n->npipe.cmdlist); | ||
5427 | break; | ||
5428 | case NREDIR: | ||
5429 | case NBACKGND: | ||
5430 | case NSUBSHELL: | ||
5431 | calcsize(n->nredir.redirect); | ||
5432 | calcsize(n->nredir.n); | ||
5433 | break; | ||
5434 | case NAND: | ||
5435 | case NOR: | ||
5436 | case NSEMI: | ||
5437 | case NWHILE: | ||
5438 | case NUNTIL: | ||
5439 | calcsize(n->nbinary.ch2); | ||
5440 | calcsize(n->nbinary.ch1); | ||
5441 | break; | ||
5442 | case NIF: | ||
5443 | calcsize(n->nif.elsepart); | ||
5444 | calcsize(n->nif.ifpart); | ||
5445 | calcsize(n->nif.test); | ||
5446 | break; | ||
5447 | case NFOR: | ||
5448 | funcstringsize += strlen(n->nfor.var) + 1; | ||
5449 | calcsize(n->nfor.body); | ||
5450 | calcsize(n->nfor.args); | ||
5451 | break; | ||
5452 | case NCASE: | ||
5453 | calcsize(n->ncase.cases); | ||
5454 | calcsize(n->ncase.expr); | ||
5455 | break; | ||
5456 | case NCLIST: | ||
5457 | calcsize(n->nclist.body); | ||
5458 | calcsize(n->nclist.pattern); | ||
5459 | calcsize(n->nclist.next); | ||
5460 | break; | ||
5461 | case NDEFUN: | ||
5462 | case NARG: | ||
5463 | sizenodelist(n->narg.backquote); | ||
5464 | funcstringsize += strlen(n->narg.text) + 1; | ||
5465 | calcsize(n->narg.next); | ||
5466 | break; | ||
5467 | case NTO: | ||
5468 | case NCLOBBER: | ||
5469 | case NFROM: | ||
5470 | case NFROMTO: | ||
5471 | case NAPPEND: | ||
5472 | calcsize(n->nfile.fname); | ||
5473 | calcsize(n->nfile.next); | ||
5474 | break; | ||
5475 | case NTOFD: | ||
5476 | case NFROMFD: | ||
5477 | calcsize(n->ndup.vname); | ||
5478 | calcsize(n->ndup.next); | ||
5479 | break; | ||
5480 | case NHERE: | ||
5481 | case NXHERE: | ||
5482 | calcsize(n->nhere.doc); | ||
5483 | calcsize(n->nhere.next); | ||
5484 | break; | ||
5485 | case NNOT: | ||
5486 | calcsize(n->nnot.com); | ||
5487 | break; | ||
5488 | }; | ||
5489 | } | ||
5490 | |||
5491 | static char * | ||
5492 | nodeckstrdup(char *s) | ||
5493 | { | ||
5494 | char *rtn = funcstring; | ||
5495 | |||
5496 | strcpy(funcstring, s); | ||
5497 | funcstring += strlen(s) + 1; | ||
5498 | return rtn; | ||
5499 | } | ||
5500 | |||
5501 | static union node *copynode(union node *); | ||
5502 | |||
5503 | static struct nodelist * | ||
5504 | copynodelist(struct nodelist *lp) | ||
5505 | { | ||
5506 | struct nodelist *start; | ||
5507 | struct nodelist **lpp; | ||
5508 | |||
5509 | lpp = &start; | ||
5510 | while (lp) { | ||
5511 | *lpp = funcblock; | ||
5512 | funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); | ||
5513 | (*lpp)->n = copynode(lp->n); | ||
5514 | lp = lp->next; | ||
5515 | lpp = &(*lpp)->next; | ||
5516 | } | ||
5517 | *lpp = NULL; | ||
5518 | return start; | ||
5519 | } | ||
5520 | |||
5521 | static union node * | ||
5522 | copynode(union node *n) | ||
5523 | { | ||
5524 | union node *new; | ||
5525 | |||
5526 | if (n == NULL) | ||
5527 | return NULL; | ||
5528 | new = funcblock; | ||
5529 | funcblock = (char *) funcblock + nodesize[n->type]; | ||
5530 | |||
5531 | switch (n->type) { | ||
5532 | case NCMD: | ||
5533 | new->ncmd.redirect = copynode(n->ncmd.redirect); | ||
5534 | new->ncmd.args = copynode(n->ncmd.args); | ||
5535 | new->ncmd.assign = copynode(n->ncmd.assign); | ||
5536 | break; | ||
5537 | case NPIPE: | ||
5538 | new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); | ||
5539 | new->npipe.backgnd = n->npipe.backgnd; | ||
5540 | break; | ||
5541 | case NREDIR: | ||
5542 | case NBACKGND: | ||
5543 | case NSUBSHELL: | ||
5544 | new->nredir.redirect = copynode(n->nredir.redirect); | ||
5545 | new->nredir.n = copynode(n->nredir.n); | ||
5546 | break; | ||
5547 | case NAND: | ||
5548 | case NOR: | ||
5549 | case NSEMI: | ||
5550 | case NWHILE: | ||
5551 | case NUNTIL: | ||
5552 | new->nbinary.ch2 = copynode(n->nbinary.ch2); | ||
5553 | new->nbinary.ch1 = copynode(n->nbinary.ch1); | ||
5554 | break; | ||
5555 | case NIF: | ||
5556 | new->nif.elsepart = copynode(n->nif.elsepart); | ||
5557 | new->nif.ifpart = copynode(n->nif.ifpart); | ||
5558 | new->nif.test = copynode(n->nif.test); | ||
5559 | break; | ||
5560 | case NFOR: | ||
5561 | new->nfor.var = nodeckstrdup(n->nfor.var); | ||
5562 | new->nfor.body = copynode(n->nfor.body); | ||
5563 | new->nfor.args = copynode(n->nfor.args); | ||
5564 | break; | ||
5565 | case NCASE: | ||
5566 | new->ncase.cases = copynode(n->ncase.cases); | ||
5567 | new->ncase.expr = copynode(n->ncase.expr); | ||
5568 | break; | ||
5569 | case NCLIST: | ||
5570 | new->nclist.body = copynode(n->nclist.body); | ||
5571 | new->nclist.pattern = copynode(n->nclist.pattern); | ||
5572 | new->nclist.next = copynode(n->nclist.next); | ||
5573 | break; | ||
5574 | case NDEFUN: | ||
5575 | case NARG: | ||
5576 | new->narg.backquote = copynodelist(n->narg.backquote); | ||
5577 | new->narg.text = nodeckstrdup(n->narg.text); | ||
5578 | new->narg.next = copynode(n->narg.next); | ||
5579 | break; | ||
5580 | case NTO: | ||
5581 | case NCLOBBER: | ||
5582 | case NFROM: | ||
5583 | case NFROMTO: | ||
5584 | case NAPPEND: | ||
5585 | new->nfile.fname = copynode(n->nfile.fname); | ||
5586 | new->nfile.fd = n->nfile.fd; | ||
5587 | new->nfile.next = copynode(n->nfile.next); | ||
5588 | break; | ||
5589 | case NTOFD: | ||
5590 | case NFROMFD: | ||
5591 | new->ndup.vname = copynode(n->ndup.vname); | ||
5592 | new->ndup.dupfd = n->ndup.dupfd; | ||
5593 | new->ndup.fd = n->ndup.fd; | ||
5594 | new->ndup.next = copynode(n->ndup.next); | ||
5595 | break; | ||
5596 | case NHERE: | ||
5597 | case NXHERE: | ||
5598 | new->nhere.doc = copynode(n->nhere.doc); | ||
5599 | new->nhere.fd = n->nhere.fd; | ||
5600 | new->nhere.next = copynode(n->nhere.next); | ||
5601 | break; | ||
5602 | case NNOT: | ||
5603 | new->nnot.com = copynode(n->nnot.com); | ||
5604 | break; | ||
5605 | }; | ||
5606 | new->type = n->type; | ||
5607 | return new; | ||
5608 | } | ||
5609 | |||
5610 | /* | ||
5611 | * Make a copy of a parse tree. | ||
5612 | */ | ||
5613 | static struct funcnode * | ||
5614 | copyfunc(union node *n) | ||
5615 | { | ||
5616 | struct funcnode *f; | ||
5617 | size_t blocksize; | ||
5618 | |||
5619 | funcblocksize = offsetof(struct funcnode, n); | ||
5620 | funcstringsize = 0; | ||
5621 | calcsize(n); | ||
5622 | blocksize = funcblocksize; | ||
5623 | f = ckmalloc(blocksize + funcstringsize); | ||
5624 | funcblock = (char *) f + offsetof(struct funcnode, n); | ||
5625 | funcstring = (char *) f + blocksize; | ||
5626 | copynode(n); | ||
5627 | f->count = 0; | ||
5628 | return f; | ||
5629 | } | ||
5630 | |||
5631 | /* | ||
5632 | * Define a shell function. | ||
5633 | */ | ||
5634 | static void | ||
5635 | defun(char *name, union node *func) | ||
5636 | { | ||
5637 | struct cmdentry entry; | ||
5638 | |||
5639 | INT_OFF; | ||
5640 | entry.cmdtype = CMDFUNCTION; | ||
5641 | entry.u.func = copyfunc(func); | ||
5642 | addcmdentry(name, &entry); | ||
5643 | INT_ON; | ||
5644 | } | ||
5645 | |||
5646 | static int evalskip; /* set if we are skipping commands */ | ||
5647 | /* reasons for skipping commands (see comment on breakcmd routine) */ | ||
5648 | #define SKIPBREAK (1 << 0) | ||
5649 | #define SKIPCONT (1 << 1) | ||
5650 | #define SKIPFUNC (1 << 2) | ||
5651 | #define SKIPFILE (1 << 3) | ||
5652 | #define SKIPEVAL (1 << 4) | ||
5653 | static int skipcount; /* number of levels to skip */ | ||
5654 | static int funcnest; /* depth of function calls */ | ||
5655 | |||
4909 | /* forward declarations - evaluation is fairly recursive business... */ | 5656 | /* forward declarations - evaluation is fairly recursive business... */ |
4910 | static void evalloop(union node *, int); | 5657 | static void evalloop(union node *, int); |
4911 | static void evalfor(union node *, int); | 5658 | static void evalfor(union node *, int); |
@@ -4915,7 +5662,6 @@ static void expredir(union node *); | |||
4915 | static void evalpipe(union node *, int); | 5662 | static void evalpipe(union node *, int); |
4916 | static void evalcommand(union node *, int); | 5663 | static void evalcommand(union node *, int); |
4917 | static int evalbltin(const struct builtincmd *, int, char **); | 5664 | static int evalbltin(const struct builtincmd *, int, char **); |
4918 | static int evalfun(struct funcnode *, int, char **, int); | ||
4919 | static void prehash(union node *); | 5665 | static void prehash(union node *); |
4920 | 5666 | ||
4921 | /* | 5667 | /* |
@@ -5270,6 +6016,84 @@ evalpipe(union node *n, int flags) | |||
5270 | INT_ON; | 6016 | INT_ON; |
5271 | } | 6017 | } |
5272 | 6018 | ||
6019 | static struct localvar *localvars; | ||
6020 | |||
6021 | /* | ||
6022 | * Called after a function returns. | ||
6023 | * Interrupts must be off. | ||
6024 | */ | ||
6025 | static void | ||
6026 | poplocalvars(void) | ||
6027 | { | ||
6028 | struct localvar *lvp; | ||
6029 | struct var *vp; | ||
6030 | |||
6031 | while ((lvp = localvars) != NULL) { | ||
6032 | localvars = lvp->next; | ||
6033 | vp = lvp->vp; | ||
6034 | TRACE(("poplocalvar %s", vp ? vp->text : "-")); | ||
6035 | if (vp == NULL) { /* $- saved */ | ||
6036 | memcpy(optlist, lvp->text, sizeof(optlist)); | ||
6037 | free((char*)lvp->text); | ||
6038 | optschanged(); | ||
6039 | } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { | ||
6040 | unsetvar(vp->text); | ||
6041 | } else { | ||
6042 | if (vp->func) | ||
6043 | (*vp->func)(strchrnul(lvp->text, '=') + 1); | ||
6044 | if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) | ||
6045 | free((char*)vp->text); | ||
6046 | vp->flags = lvp->flags; | ||
6047 | vp->text = lvp->text; | ||
6048 | } | ||
6049 | free(lvp); | ||
6050 | } | ||
6051 | } | ||
6052 | |||
6053 | static int | ||
6054 | evalfun(struct funcnode *func, int argc, char **argv, int flags) | ||
6055 | { | ||
6056 | volatile struct shparam saveparam; | ||
6057 | struct localvar *volatile savelocalvars; | ||
6058 | struct jmploc *volatile savehandler; | ||
6059 | struct jmploc jmploc; | ||
6060 | int e; | ||
6061 | |||
6062 | saveparam = shellparam; | ||
6063 | savelocalvars = localvars; | ||
6064 | e = setjmp(jmploc.loc); | ||
6065 | if (e) { | ||
6066 | goto funcdone; | ||
6067 | } | ||
6068 | INT_OFF; | ||
6069 | savehandler = exception_handler; | ||
6070 | exception_handler = &jmploc; | ||
6071 | localvars = NULL; | ||
6072 | shellparam.malloc = 0; | ||
6073 | func->count++; | ||
6074 | funcnest++; | ||
6075 | INT_ON; | ||
6076 | shellparam.nparam = argc - 1; | ||
6077 | shellparam.p = argv + 1; | ||
6078 | #if ENABLE_ASH_GETOPTS | ||
6079 | shellparam.optind = 1; | ||
6080 | shellparam.optoff = -1; | ||
6081 | #endif | ||
6082 | evaltree(&func->n, flags & EV_TESTED); | ||
6083 | funcdone: | ||
6084 | INT_OFF; | ||
6085 | funcnest--; | ||
6086 | freefunc(func); | ||
6087 | poplocalvars(); | ||
6088 | localvars = savelocalvars; | ||
6089 | freeparam(&shellparam); | ||
6090 | shellparam = saveparam; | ||
6091 | exception_handler = savehandler; | ||
6092 | INT_ON; | ||
6093 | evalskip &= ~SKIPFUNC; | ||
6094 | return e; | ||
6095 | } | ||
6096 | |||
5273 | #if ENABLE_ASH_CMDCMD | 6097 | #if ENABLE_ASH_CMDCMD |
5274 | static char ** | 6098 | static char ** |
5275 | parse_command_args(char **argv, const char **path) | 6099 | parse_command_args(char **argv, const char **path) |
@@ -5305,6 +6129,68 @@ parse_command_args(char **argv, const char **path) | |||
5305 | } | 6129 | } |
5306 | #endif | 6130 | #endif |
5307 | 6131 | ||
6132 | /* | ||
6133 | * Make a variable a local variable. When a variable is made local, it's | ||
6134 | * value and flags are saved in a localvar structure. The saved values | ||
6135 | * will be restored when the shell function returns. We handle the name | ||
6136 | * "-" as a special case. | ||
6137 | */ | ||
6138 | static void | ||
6139 | mklocal(char *name) | ||
6140 | { | ||
6141 | struct localvar *lvp; | ||
6142 | struct var **vpp; | ||
6143 | struct var *vp; | ||
6144 | |||
6145 | INT_OFF; | ||
6146 | lvp = ckmalloc(sizeof(struct localvar)); | ||
6147 | if (LONE_DASH(name)) { | ||
6148 | char *p; | ||
6149 | p = ckmalloc(sizeof(optlist)); | ||
6150 | lvp->text = memcpy(p, optlist, sizeof(optlist)); | ||
6151 | vp = NULL; | ||
6152 | } else { | ||
6153 | char *eq; | ||
6154 | |||
6155 | vpp = hashvar(name); | ||
6156 | vp = *findvar(vpp, name); | ||
6157 | eq = strchr(name, '='); | ||
6158 | if (vp == NULL) { | ||
6159 | if (eq) | ||
6160 | setvareq(name, VSTRFIXED); | ||
6161 | else | ||
6162 | setvar(name, NULL, VSTRFIXED); | ||
6163 | vp = *vpp; /* the new variable */ | ||
6164 | lvp->flags = VUNSET; | ||
6165 | } else { | ||
6166 | lvp->text = vp->text; | ||
6167 | lvp->flags = vp->flags; | ||
6168 | vp->flags |= VSTRFIXED|VTEXTFIXED; | ||
6169 | if (eq) | ||
6170 | setvareq(name, 0); | ||
6171 | } | ||
6172 | } | ||
6173 | lvp->vp = vp; | ||
6174 | lvp->next = localvars; | ||
6175 | localvars = lvp; | ||
6176 | INT_ON; | ||
6177 | } | ||
6178 | |||
6179 | /* | ||
6180 | * The "local" command. | ||
6181 | */ | ||
6182 | static int | ||
6183 | localcmd(int argc, char **argv) | ||
6184 | { | ||
6185 | char *name; | ||
6186 | |||
6187 | argv = argptr; | ||
6188 | while ((name = *argv++) != NULL) { | ||
6189 | mklocal(name); | ||
6190 | } | ||
6191 | return 0; | ||
6192 | } | ||
6193 | |||
5308 | /* Forward declarations for builtintab[] */ | 6194 | /* Forward declarations for builtintab[] */ |
5309 | #if JOBS | 6195 | #if JOBS |
5310 | static int fg_bgcmd(int, char **); | 6196 | static int fg_bgcmd(int, char **); |
@@ -5338,7 +6224,6 @@ static int jobscmd(int, char **); | |||
5338 | #if ENABLE_ASH_MATH_SUPPORT | 6224 | #if ENABLE_ASH_MATH_SUPPORT |
5339 | static int letcmd(int, char **); | 6225 | static int letcmd(int, char **); |
5340 | #endif | 6226 | #endif |
5341 | static int localcmd(int, char **); | ||
5342 | static int pwdcmd(int, char **); | 6227 | static int pwdcmd(int, char **); |
5343 | static int readcmd(int, char **); | 6228 | static int readcmd(int, char **); |
5344 | static int returncmd(int, char **); | 6229 | static int returncmd(int, char **); |
@@ -5356,19 +6241,15 @@ static int ulimitcmd(int, char **); | |||
5356 | static int killcmd(int, char **); | 6241 | static int killcmd(int, char **); |
5357 | #endif | 6242 | #endif |
5358 | 6243 | ||
5359 | #define BUILTIN_NOSPEC "0" | 6244 | #define BUILTIN_NOSPEC "0" |
5360 | #define BUILTIN_SPECIAL "1" | 6245 | #define BUILTIN_SPECIAL "1" |
5361 | #define BUILTIN_REGULAR "2" | 6246 | #define BUILTIN_REGULAR "2" |
5362 | #define BUILTIN_SPEC_REG "3" | 6247 | #define BUILTIN_SPEC_REG "3" |
5363 | #define BUILTIN_ASSIGN "4" | 6248 | #define BUILTIN_ASSIGN "4" |
5364 | #define BUILTIN_SPEC_ASSG "5" | 6249 | #define BUILTIN_SPEC_ASSG "5" |
5365 | #define BUILTIN_REG_ASSG "6" | 6250 | #define BUILTIN_REG_ASSG "6" |
5366 | #define BUILTIN_SPEC_REG_ASSG "7" | 6251 | #define BUILTIN_SPEC_REG_ASSG "7" |
5367 | 6252 | ||
5368 | #define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1) | ||
5369 | #define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2) | ||
5370 | #define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4) | ||
5371 | |||
5372 | /* make sure to keep these in proper order since it is searched via bsearch() */ | 6253 | /* make sure to keep these in proper order since it is searched via bsearch() */ |
5373 | static const struct builtincmd builtintab[] = { | 6254 | static const struct builtincmd builtintab[] = { |
5374 | { BUILTIN_SPEC_REG ".", dotcmd }, | 6255 | { BUILTIN_SPEC_REG ".", dotcmd }, |
@@ -5453,6 +6334,205 @@ static const struct builtincmd builtintab[] = { | |||
5453 | ENABLE_ASH_BUILTIN_ECHO) | 6334 | ENABLE_ASH_BUILTIN_ECHO) |
5454 | 6335 | ||
5455 | /* | 6336 | /* |
6337 | * Search the table of builtin commands. | ||
6338 | */ | ||
6339 | static struct builtincmd * | ||
6340 | find_builtin(const char *name) | ||
6341 | { | ||
6342 | struct builtincmd *bp; | ||
6343 | |||
6344 | bp = bsearch( | ||
6345 | name, builtintab, NUMBUILTINS, sizeof(builtintab[0]), | ||
6346 | pstrcmp | ||
6347 | ); | ||
6348 | return bp; | ||
6349 | } | ||
6350 | |||
6351 | /* | ||
6352 | * Resolve a command name. If you change this routine, you may have to | ||
6353 | * change the shellexec routine as well. | ||
6354 | */ | ||
6355 | static void | ||
6356 | find_command(char *name, struct cmdentry *entry, int act, const char *path) | ||
6357 | { | ||
6358 | struct tblentry *cmdp; | ||
6359 | int idx; | ||
6360 | int prev; | ||
6361 | char *fullname; | ||
6362 | struct stat statb; | ||
6363 | int e; | ||
6364 | int updatetbl; | ||
6365 | struct builtincmd *bcmd; | ||
6366 | |||
6367 | /* If name contains a slash, don't use PATH or hash table */ | ||
6368 | if (strchr(name, '/') != NULL) { | ||
6369 | entry->u.index = -1; | ||
6370 | if (act & DO_ABS) { | ||
6371 | while (stat(name, &statb) < 0) { | ||
6372 | #ifdef SYSV | ||
6373 | if (errno == EINTR) | ||
6374 | continue; | ||
6375 | #endif | ||
6376 | entry->cmdtype = CMDUNKNOWN; | ||
6377 | return; | ||
6378 | } | ||
6379 | } | ||
6380 | entry->cmdtype = CMDNORMAL; | ||
6381 | return; | ||
6382 | } | ||
6383 | |||
6384 | #if ENABLE_FEATURE_SH_STANDALONE_SHELL | ||
6385 | if (find_applet_by_name(name)) { | ||
6386 | entry->cmdtype = CMDNORMAL; | ||
6387 | entry->u.index = -1; | ||
6388 | return; | ||
6389 | } | ||
6390 | #endif | ||
6391 | |||
6392 | if (is_safe_applet(name)) { | ||
6393 | entry->cmdtype = CMDNORMAL; | ||
6394 | entry->u.index = -1; | ||
6395 | return; | ||
6396 | } | ||
6397 | |||
6398 | updatetbl = (path == pathval()); | ||
6399 | if (!updatetbl) { | ||
6400 | act |= DO_ALTPATH; | ||
6401 | if (strstr(path, "%builtin") != NULL) | ||
6402 | act |= DO_ALTBLTIN; | ||
6403 | } | ||
6404 | |||
6405 | /* If name is in the table, check answer will be ok */ | ||
6406 | cmdp = cmdlookup(name, 0); | ||
6407 | if (cmdp != NULL) { | ||
6408 | int bit; | ||
6409 | |||
6410 | switch (cmdp->cmdtype) { | ||
6411 | default: | ||
6412 | #if DEBUG | ||
6413 | abort(); | ||
6414 | #endif | ||
6415 | case CMDNORMAL: | ||
6416 | bit = DO_ALTPATH; | ||
6417 | break; | ||
6418 | case CMDFUNCTION: | ||
6419 | bit = DO_NOFUNC; | ||
6420 | break; | ||
6421 | case CMDBUILTIN: | ||
6422 | bit = DO_ALTBLTIN; | ||
6423 | break; | ||
6424 | } | ||
6425 | if (act & bit) { | ||
6426 | updatetbl = 0; | ||
6427 | cmdp = NULL; | ||
6428 | } else if (cmdp->rehash == 0) | ||
6429 | /* if not invalidated by cd, we're done */ | ||
6430 | goto success; | ||
6431 | } | ||
6432 | |||
6433 | /* If %builtin not in path, check for builtin next */ | ||
6434 | bcmd = find_builtin(name); | ||
6435 | if (bcmd && (IS_BUILTIN_REGULAR(bcmd) || ( | ||
6436 | act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0 | ||
6437 | ))) | ||
6438 | goto builtin_success; | ||
6439 | |||
6440 | /* We have to search path. */ | ||
6441 | prev = -1; /* where to start */ | ||
6442 | if (cmdp && cmdp->rehash) { /* doing a rehash */ | ||
6443 | if (cmdp->cmdtype == CMDBUILTIN) | ||
6444 | prev = builtinloc; | ||
6445 | else | ||
6446 | prev = cmdp->param.index; | ||
6447 | } | ||
6448 | |||
6449 | e = ENOENT; | ||
6450 | idx = -1; | ||
6451 | loop: | ||
6452 | while ((fullname = padvance(&path, name)) != NULL) { | ||
6453 | stunalloc(fullname); | ||
6454 | idx++; | ||
6455 | if (pathopt) { | ||
6456 | if (prefix(pathopt, "builtin")) { | ||
6457 | if (bcmd) | ||
6458 | goto builtin_success; | ||
6459 | continue; | ||
6460 | } else if (!(act & DO_NOFUNC) && | ||
6461 | prefix(pathopt, "func")) { | ||
6462 | /* handled below */ | ||
6463 | } else { | ||
6464 | /* ignore unimplemented options */ | ||
6465 | continue; | ||
6466 | } | ||
6467 | } | ||
6468 | /* if rehash, don't redo absolute path names */ | ||
6469 | if (fullname[0] == '/' && idx <= prev) { | ||
6470 | if (idx < prev) | ||
6471 | continue; | ||
6472 | TRACE(("searchexec \"%s\": no change\n", name)); | ||
6473 | goto success; | ||
6474 | } | ||
6475 | while (stat(fullname, &statb) < 0) { | ||
6476 | #ifdef SYSV | ||
6477 | if (errno == EINTR) | ||
6478 | continue; | ||
6479 | #endif | ||
6480 | if (errno != ENOENT && errno != ENOTDIR) | ||
6481 | e = errno; | ||
6482 | goto loop; | ||
6483 | } | ||
6484 | e = EACCES; /* if we fail, this will be the error */ | ||
6485 | if (!S_ISREG(statb.st_mode)) | ||
6486 | continue; | ||
6487 | if (pathopt) { /* this is a %func directory */ | ||
6488 | stalloc(strlen(fullname) + 1); | ||
6489 | readcmdfile(fullname); | ||
6490 | cmdp = cmdlookup(name, 0); | ||
6491 | if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION) | ||
6492 | ash_msg_and_raise_error("%s not defined in %s", name, fullname); | ||
6493 | stunalloc(fullname); | ||
6494 | goto success; | ||
6495 | } | ||
6496 | TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); | ||
6497 | if (!updatetbl) { | ||
6498 | entry->cmdtype = CMDNORMAL; | ||
6499 | entry->u.index = idx; | ||
6500 | return; | ||
6501 | } | ||
6502 | INT_OFF; | ||
6503 | cmdp = cmdlookup(name, 1); | ||
6504 | cmdp->cmdtype = CMDNORMAL; | ||
6505 | cmdp->param.index = idx; | ||
6506 | INT_ON; | ||
6507 | goto success; | ||
6508 | } | ||
6509 | |||
6510 | /* We failed. If there was an entry for this command, delete it */ | ||
6511 | if (cmdp && updatetbl) | ||
6512 | delete_cmd_entry(); | ||
6513 | if (act & DO_ERR) | ||
6514 | ash_msg("%s: %s", name, errmsg(e, "not found")); | ||
6515 | entry->cmdtype = CMDUNKNOWN; | ||
6516 | return; | ||
6517 | |||
6518 | builtin_success: | ||
6519 | if (!updatetbl) { | ||
6520 | entry->cmdtype = CMDBUILTIN; | ||
6521 | entry->u.cmd = bcmd; | ||
6522 | return; | ||
6523 | } | ||
6524 | INT_OFF; | ||
6525 | cmdp = cmdlookup(name, 1); | ||
6526 | cmdp->cmdtype = CMDBUILTIN; | ||
6527 | cmdp->param.cmd = bcmd; | ||
6528 | INT_ON; | ||
6529 | success: | ||
6530 | cmdp->rehash = 0; | ||
6531 | entry->cmdtype = cmdp->cmdtype; | ||
6532 | entry->u = cmdp->param; | ||
6533 | } | ||
6534 | |||
6535 | /* | ||
5456 | * Execute a simple command. | 6536 | * Execute a simple command. |
5457 | */ | 6537 | */ |
5458 | static int back_exitstatus; /* exit status of backquoted command */ | 6538 | static int back_exitstatus; /* exit status of backquoted command */ |
@@ -5732,95 +6812,6 @@ evalbltin(const struct builtincmd *cmd, int argc, char **argv) | |||
5732 | return i; | 6812 | return i; |
5733 | } | 6813 | } |
5734 | 6814 | ||
5735 | static struct localvar *localvars; | ||
5736 | |||
5737 | /* | ||
5738 | * Called after a function returns. | ||
5739 | * Interrupts must be off. | ||
5740 | */ | ||
5741 | static void | ||
5742 | poplocalvars(void) | ||
5743 | { | ||
5744 | struct localvar *lvp; | ||
5745 | struct var *vp; | ||
5746 | |||
5747 | while ((lvp = localvars) != NULL) { | ||
5748 | localvars = lvp->next; | ||
5749 | vp = lvp->vp; | ||
5750 | TRACE(("poplocalvar %s", vp ? vp->text : "-")); | ||
5751 | if (vp == NULL) { /* $- saved */ | ||
5752 | memcpy(optlist, lvp->text, sizeof(optlist)); | ||
5753 | free((char*)lvp->text); | ||
5754 | optschanged(); | ||
5755 | } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { | ||
5756 | unsetvar(vp->text); | ||
5757 | } else { | ||
5758 | if (vp->func) | ||
5759 | (*vp->func)(strchrnul(lvp->text, '=') + 1); | ||
5760 | if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) | ||
5761 | free((char*)vp->text); | ||
5762 | vp->flags = lvp->flags; | ||
5763 | vp->text = lvp->text; | ||
5764 | } | ||
5765 | free(lvp); | ||
5766 | } | ||
5767 | } | ||
5768 | |||
5769 | /* | ||
5770 | * Free a parse tree. | ||
5771 | */ | ||
5772 | static void | ||
5773 | freefunc(struct funcnode *f) | ||
5774 | { | ||
5775 | if (f && --f->count < 0) | ||
5776 | free(f); | ||
5777 | } | ||
5778 | |||
5779 | static int | ||
5780 | evalfun(struct funcnode *func, int argc, char **argv, int flags) | ||
5781 | { | ||
5782 | volatile struct shparam saveparam; | ||
5783 | struct localvar *volatile savelocalvars; | ||
5784 | struct jmploc *volatile savehandler; | ||
5785 | struct jmploc jmploc; | ||
5786 | int e; | ||
5787 | |||
5788 | saveparam = shellparam; | ||
5789 | savelocalvars = localvars; | ||
5790 | e = setjmp(jmploc.loc); | ||
5791 | if (e) { | ||
5792 | goto funcdone; | ||
5793 | } | ||
5794 | INT_OFF; | ||
5795 | savehandler = exception_handler; | ||
5796 | exception_handler = &jmploc; | ||
5797 | localvars = NULL; | ||
5798 | shellparam.malloc = 0; | ||
5799 | func->count++; | ||
5800 | funcnest++; | ||
5801 | INT_ON; | ||
5802 | shellparam.nparam = argc - 1; | ||
5803 | shellparam.p = argv + 1; | ||
5804 | #if ENABLE_ASH_GETOPTS | ||
5805 | shellparam.optind = 1; | ||
5806 | shellparam.optoff = -1; | ||
5807 | #endif | ||
5808 | evaltree(&func->n, flags & EV_TESTED); | ||
5809 | funcdone: | ||
5810 | INT_OFF; | ||
5811 | funcnest--; | ||
5812 | freefunc(func); | ||
5813 | poplocalvars(); | ||
5814 | localvars = savelocalvars; | ||
5815 | freeparam(&shellparam); | ||
5816 | shellparam = saveparam; | ||
5817 | exception_handler = savehandler; | ||
5818 | INT_ON; | ||
5819 | evalskip &= ~SKIPFUNC; | ||
5820 | return e; | ||
5821 | } | ||
5822 | |||
5823 | |||
5824 | static int | 6815 | static int |
5825 | goodname(const char *p) | 6816 | goodname(const char *p) |
5826 | { | 6817 | { |
@@ -5915,764 +6906,6 @@ execcmd(int argc, char **argv) | |||
5915 | } | 6906 | } |
5916 | 6907 | ||
5917 | 6908 | ||
5918 | /* ============ Executing commands */ | ||
5919 | |||
5920 | /* | ||
5921 | * When commands are first encountered, they are entered in a hash table. | ||
5922 | * This ensures that a full path search will not have to be done for them | ||
5923 | * on each invocation. | ||
5924 | * | ||
5925 | * We should investigate converting to a linear search, even though that | ||
5926 | * would make the command name "hash" a misnomer. | ||
5927 | */ | ||
5928 | |||
5929 | #define CMDTABLESIZE 31 /* should be prime */ | ||
5930 | #define ARB 1 /* actual size determined at run time */ | ||
5931 | |||
5932 | struct tblentry { | ||
5933 | struct tblentry *next; /* next entry in hash chain */ | ||
5934 | union param param; /* definition of builtin function */ | ||
5935 | short cmdtype; /* index identifying command */ | ||
5936 | char rehash; /* if set, cd done since entry created */ | ||
5937 | char cmdname[ARB]; /* name of command */ | ||
5938 | }; | ||
5939 | |||
5940 | static struct tblentry *cmdtable[CMDTABLESIZE]; | ||
5941 | static int builtinloc = -1; /* index in path of %builtin, or -1 */ | ||
5942 | |||
5943 | static void | ||
5944 | tryexec(char *cmd, char **argv, char **envp) | ||
5945 | { | ||
5946 | int repeated = 0; | ||
5947 | struct BB_applet *a; | ||
5948 | int argc = 0; | ||
5949 | char **c; | ||
5950 | |||
5951 | if (strchr(cmd, '/') == NULL | ||
5952 | && (a = find_applet_by_name(cmd)) != NULL | ||
5953 | && is_safe_applet(cmd) | ||
5954 | ) { | ||
5955 | c = argv; | ||
5956 | while (*c != NULL) { | ||
5957 | c++; argc++; | ||
5958 | } | ||
5959 | applet_name = cmd; | ||
5960 | exit(a->main(argc, argv)); | ||
5961 | } | ||
5962 | #if ENABLE_FEATURE_SH_STANDALONE_SHELL | ||
5963 | if (find_applet_by_name(cmd) != NULL) { | ||
5964 | /* re-exec ourselves with the new arguments */ | ||
5965 | execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp); | ||
5966 | /* If they called chroot or otherwise made the binary no longer | ||
5967 | * executable, fall through */ | ||
5968 | } | ||
5969 | #endif | ||
5970 | |||
5971 | repeat: | ||
5972 | #ifdef SYSV | ||
5973 | do { | ||
5974 | execve(cmd, argv, envp); | ||
5975 | } while (errno == EINTR); | ||
5976 | #else | ||
5977 | execve(cmd, argv, envp); | ||
5978 | #endif | ||
5979 | if (repeated++) { | ||
5980 | free(argv); | ||
5981 | } else if (errno == ENOEXEC) { | ||
5982 | char **ap; | ||
5983 | char **new; | ||
5984 | |||
5985 | for (ap = argv; *ap; ap++) | ||
5986 | ; | ||
5987 | ap = new = ckmalloc((ap - argv + 2) * sizeof(char *)); | ||
5988 | ap[1] = cmd; | ||
5989 | *ap = cmd = (char *)DEFAULT_SHELL; | ||
5990 | ap += 2; | ||
5991 | argv++; | ||
5992 | while ((*ap++ = *argv++)) | ||
5993 | ; | ||
5994 | argv = new; | ||
5995 | goto repeat; | ||
5996 | } | ||
5997 | } | ||
5998 | |||
5999 | /* | ||
6000 | * Exec a program. Never returns. If you change this routine, you may | ||
6001 | * have to change the find_command routine as well. | ||
6002 | */ | ||
6003 | #define environment() listvars(VEXPORT, VUNSET, 0) | ||
6004 | static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN; | ||
6005 | static void | ||
6006 | shellexec(char **argv, const char *path, int idx) | ||
6007 | { | ||
6008 | char *cmdname; | ||
6009 | int e; | ||
6010 | char **envp; | ||
6011 | int exerrno; | ||
6012 | |||
6013 | clearredir(1); | ||
6014 | envp = environment(); | ||
6015 | if (strchr(argv[0], '/') || is_safe_applet(argv[0]) | ||
6016 | #if ENABLE_FEATURE_SH_STANDALONE_SHELL | ||
6017 | || find_applet_by_name(argv[0]) | ||
6018 | #endif | ||
6019 | ) { | ||
6020 | tryexec(argv[0], argv, envp); | ||
6021 | e = errno; | ||
6022 | } else { | ||
6023 | e = ENOENT; | ||
6024 | while ((cmdname = padvance(&path, argv[0])) != NULL) { | ||
6025 | if (--idx < 0 && pathopt == NULL) { | ||
6026 | tryexec(cmdname, argv, envp); | ||
6027 | if (errno != ENOENT && errno != ENOTDIR) | ||
6028 | e = errno; | ||
6029 | } | ||
6030 | stunalloc(cmdname); | ||
6031 | } | ||
6032 | } | ||
6033 | |||
6034 | /* Map to POSIX errors */ | ||
6035 | switch (e) { | ||
6036 | case EACCES: | ||
6037 | exerrno = 126; | ||
6038 | break; | ||
6039 | case ENOENT: | ||
6040 | exerrno = 127; | ||
6041 | break; | ||
6042 | default: | ||
6043 | exerrno = 2; | ||
6044 | break; | ||
6045 | } | ||
6046 | exitstatus = exerrno; | ||
6047 | TRACE(("shellexec failed for %s, errno %d, suppressint %d\n", | ||
6048 | argv[0], e, suppressint )); | ||
6049 | ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found")); | ||
6050 | /* NOTREACHED */ | ||
6051 | } | ||
6052 | |||
6053 | static void | ||
6054 | printentry(struct tblentry *cmdp) | ||
6055 | { | ||
6056 | int idx; | ||
6057 | const char *path; | ||
6058 | char *name; | ||
6059 | |||
6060 | idx = cmdp->param.index; | ||
6061 | path = pathval(); | ||
6062 | do { | ||
6063 | name = padvance(&path, cmdp->cmdname); | ||
6064 | stunalloc(name); | ||
6065 | } while (--idx >= 0); | ||
6066 | out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); | ||
6067 | } | ||
6068 | |||
6069 | /* | ||
6070 | * Clear out command entries. The argument specifies the first entry in | ||
6071 | * PATH which has changed. | ||
6072 | */ | ||
6073 | static void | ||
6074 | clearcmdentry(int firstchange) | ||
6075 | { | ||
6076 | struct tblentry **tblp; | ||
6077 | struct tblentry **pp; | ||
6078 | struct tblentry *cmdp; | ||
6079 | |||
6080 | INT_OFF; | ||
6081 | for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) { | ||
6082 | pp = tblp; | ||
6083 | while ((cmdp = *pp) != NULL) { | ||
6084 | if ((cmdp->cmdtype == CMDNORMAL && | ||
6085 | cmdp->param.index >= firstchange) | ||
6086 | || (cmdp->cmdtype == CMDBUILTIN && | ||
6087 | builtinloc >= firstchange) | ||
6088 | ) { | ||
6089 | *pp = cmdp->next; | ||
6090 | free(cmdp); | ||
6091 | } else { | ||
6092 | pp = &cmdp->next; | ||
6093 | } | ||
6094 | } | ||
6095 | } | ||
6096 | INT_ON; | ||
6097 | } | ||
6098 | |||
6099 | /* | ||
6100 | * Locate a command in the command hash table. If "add" is nonzero, | ||
6101 | * add the command to the table if it is not already present. The | ||
6102 | * variable "lastcmdentry" is set to point to the address of the link | ||
6103 | * pointing to the entry, so that delete_cmd_entry can delete the | ||
6104 | * entry. | ||
6105 | * | ||
6106 | * Interrupts must be off if called with add != 0. | ||
6107 | */ | ||
6108 | static struct tblentry **lastcmdentry; | ||
6109 | |||
6110 | static struct tblentry * | ||
6111 | cmdlookup(const char *name, int add) | ||
6112 | { | ||
6113 | unsigned int hashval; | ||
6114 | const char *p; | ||
6115 | struct tblentry *cmdp; | ||
6116 | struct tblentry **pp; | ||
6117 | |||
6118 | p = name; | ||
6119 | hashval = (unsigned char)*p << 4; | ||
6120 | while (*p) | ||
6121 | hashval += (unsigned char)*p++; | ||
6122 | hashval &= 0x7FFF; | ||
6123 | pp = &cmdtable[hashval % CMDTABLESIZE]; | ||
6124 | for (cmdp = *pp; cmdp; cmdp = cmdp->next) { | ||
6125 | if (strcmp(cmdp->cmdname, name) == 0) | ||
6126 | break; | ||
6127 | pp = &cmdp->next; | ||
6128 | } | ||
6129 | if (add && cmdp == NULL) { | ||
6130 | cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB | ||
6131 | + strlen(name) + 1); | ||
6132 | cmdp->next = NULL; | ||
6133 | cmdp->cmdtype = CMDUNKNOWN; | ||
6134 | strcpy(cmdp->cmdname, name); | ||
6135 | } | ||
6136 | lastcmdentry = pp; | ||
6137 | return cmdp; | ||
6138 | } | ||
6139 | |||
6140 | /* | ||
6141 | * Delete the command entry returned on the last lookup. | ||
6142 | */ | ||
6143 | static void | ||
6144 | delete_cmd_entry(void) | ||
6145 | { | ||
6146 | struct tblentry *cmdp; | ||
6147 | |||
6148 | INT_OFF; | ||
6149 | cmdp = *lastcmdentry; | ||
6150 | *lastcmdentry = cmdp->next; | ||
6151 | if (cmdp->cmdtype == CMDFUNCTION) | ||
6152 | freefunc(cmdp->param.func); | ||
6153 | free(cmdp); | ||
6154 | INT_ON; | ||
6155 | } | ||
6156 | |||
6157 | /* | ||
6158 | * Add a new command entry, replacing any existing command entry for | ||
6159 | * the same name - except special builtins. | ||
6160 | */ | ||
6161 | static void | ||
6162 | addcmdentry(char *name, struct cmdentry *entry) | ||
6163 | { | ||
6164 | struct tblentry *cmdp; | ||
6165 | |||
6166 | cmdp = cmdlookup(name, 1); | ||
6167 | if (cmdp->cmdtype == CMDFUNCTION) { | ||
6168 | freefunc(cmdp->param.func); | ||
6169 | } | ||
6170 | cmdp->cmdtype = entry->cmdtype; | ||
6171 | cmdp->param = entry->u; | ||
6172 | cmdp->rehash = 0; | ||
6173 | } | ||
6174 | |||
6175 | static int | ||
6176 | hashcmd(int argc, char **argv) | ||
6177 | { | ||
6178 | struct tblentry **pp; | ||
6179 | struct tblentry *cmdp; | ||
6180 | int c; | ||
6181 | struct cmdentry entry; | ||
6182 | char *name; | ||
6183 | |||
6184 | while ((c = nextopt("r")) != '\0') { | ||
6185 | clearcmdentry(0); | ||
6186 | return 0; | ||
6187 | } | ||
6188 | if (*argptr == NULL) { | ||
6189 | for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) { | ||
6190 | for (cmdp = *pp; cmdp; cmdp = cmdp->next) { | ||
6191 | if (cmdp->cmdtype == CMDNORMAL) | ||
6192 | printentry(cmdp); | ||
6193 | } | ||
6194 | } | ||
6195 | return 0; | ||
6196 | } | ||
6197 | c = 0; | ||
6198 | while ((name = *argptr) != NULL) { | ||
6199 | cmdp = cmdlookup(name, 0); | ||
6200 | if (cmdp != NULL | ||
6201 | && (cmdp->cmdtype == CMDNORMAL | ||
6202 | || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) | ||
6203 | delete_cmd_entry(); | ||
6204 | find_command(name, &entry, DO_ERR, pathval()); | ||
6205 | if (entry.cmdtype == CMDUNKNOWN) | ||
6206 | c = 1; | ||
6207 | argptr++; | ||
6208 | } | ||
6209 | return c; | ||
6210 | } | ||
6211 | |||
6212 | /* | ||
6213 | * Resolve a command name. If you change this routine, you may have to | ||
6214 | * change the shellexec routine as well. | ||
6215 | */ | ||
6216 | static void | ||
6217 | find_command(char *name, struct cmdentry *entry, int act, const char *path) | ||
6218 | { | ||
6219 | struct tblentry *cmdp; | ||
6220 | int idx; | ||
6221 | int prev; | ||
6222 | char *fullname; | ||
6223 | struct stat statb; | ||
6224 | int e; | ||
6225 | int updatetbl; | ||
6226 | struct builtincmd *bcmd; | ||
6227 | |||
6228 | /* If name contains a slash, don't use PATH or hash table */ | ||
6229 | if (strchr(name, '/') != NULL) { | ||
6230 | entry->u.index = -1; | ||
6231 | if (act & DO_ABS) { | ||
6232 | while (stat(name, &statb) < 0) { | ||
6233 | #ifdef SYSV | ||
6234 | if (errno == EINTR) | ||
6235 | continue; | ||
6236 | #endif | ||
6237 | entry->cmdtype = CMDUNKNOWN; | ||
6238 | return; | ||
6239 | } | ||
6240 | } | ||
6241 | entry->cmdtype = CMDNORMAL; | ||
6242 | return; | ||
6243 | } | ||
6244 | |||
6245 | #if ENABLE_FEATURE_SH_STANDALONE_SHELL | ||
6246 | if (find_applet_by_name(name)) { | ||
6247 | entry->cmdtype = CMDNORMAL; | ||
6248 | entry->u.index = -1; | ||
6249 | return; | ||
6250 | } | ||
6251 | #endif | ||
6252 | |||
6253 | if (is_safe_applet(name)) { | ||
6254 | entry->cmdtype = CMDNORMAL; | ||
6255 | entry->u.index = -1; | ||
6256 | return; | ||
6257 | } | ||
6258 | |||
6259 | updatetbl = (path == pathval()); | ||
6260 | if (!updatetbl) { | ||
6261 | act |= DO_ALTPATH; | ||
6262 | if (strstr(path, "%builtin") != NULL) | ||
6263 | act |= DO_ALTBLTIN; | ||
6264 | } | ||
6265 | |||
6266 | /* If name is in the table, check answer will be ok */ | ||
6267 | cmdp = cmdlookup(name, 0); | ||
6268 | if (cmdp != NULL) { | ||
6269 | int bit; | ||
6270 | |||
6271 | switch (cmdp->cmdtype) { | ||
6272 | default: | ||
6273 | #if DEBUG | ||
6274 | abort(); | ||
6275 | #endif | ||
6276 | case CMDNORMAL: | ||
6277 | bit = DO_ALTPATH; | ||
6278 | break; | ||
6279 | case CMDFUNCTION: | ||
6280 | bit = DO_NOFUNC; | ||
6281 | break; | ||
6282 | case CMDBUILTIN: | ||
6283 | bit = DO_ALTBLTIN; | ||
6284 | break; | ||
6285 | } | ||
6286 | if (act & bit) { | ||
6287 | updatetbl = 0; | ||
6288 | cmdp = NULL; | ||
6289 | } else if (cmdp->rehash == 0) | ||
6290 | /* if not invalidated by cd, we're done */ | ||
6291 | goto success; | ||
6292 | } | ||
6293 | |||
6294 | /* If %builtin not in path, check for builtin next */ | ||
6295 | bcmd = find_builtin(name); | ||
6296 | if (bcmd && (IS_BUILTIN_REGULAR(bcmd) || ( | ||
6297 | act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0 | ||
6298 | ))) | ||
6299 | goto builtin_success; | ||
6300 | |||
6301 | /* We have to search path. */ | ||
6302 | prev = -1; /* where to start */ | ||
6303 | if (cmdp && cmdp->rehash) { /* doing a rehash */ | ||
6304 | if (cmdp->cmdtype == CMDBUILTIN) | ||
6305 | prev = builtinloc; | ||
6306 | else | ||
6307 | prev = cmdp->param.index; | ||
6308 | } | ||
6309 | |||
6310 | e = ENOENT; | ||
6311 | idx = -1; | ||
6312 | loop: | ||
6313 | while ((fullname = padvance(&path, name)) != NULL) { | ||
6314 | stunalloc(fullname); | ||
6315 | idx++; | ||
6316 | if (pathopt) { | ||
6317 | if (prefix(pathopt, "builtin")) { | ||
6318 | if (bcmd) | ||
6319 | goto builtin_success; | ||
6320 | continue; | ||
6321 | } else if (!(act & DO_NOFUNC) && | ||
6322 | prefix(pathopt, "func")) { | ||
6323 | /* handled below */ | ||
6324 | } else { | ||
6325 | /* ignore unimplemented options */ | ||
6326 | continue; | ||
6327 | } | ||
6328 | } | ||
6329 | /* if rehash, don't redo absolute path names */ | ||
6330 | if (fullname[0] == '/' && idx <= prev) { | ||
6331 | if (idx < prev) | ||
6332 | continue; | ||
6333 | TRACE(("searchexec \"%s\": no change\n", name)); | ||
6334 | goto success; | ||
6335 | } | ||
6336 | while (stat(fullname, &statb) < 0) { | ||
6337 | #ifdef SYSV | ||
6338 | if (errno == EINTR) | ||
6339 | continue; | ||
6340 | #endif | ||
6341 | if (errno != ENOENT && errno != ENOTDIR) | ||
6342 | e = errno; | ||
6343 | goto loop; | ||
6344 | } | ||
6345 | e = EACCES; /* if we fail, this will be the error */ | ||
6346 | if (!S_ISREG(statb.st_mode)) | ||
6347 | continue; | ||
6348 | if (pathopt) { /* this is a %func directory */ | ||
6349 | stalloc(strlen(fullname) + 1); | ||
6350 | readcmdfile(fullname); | ||
6351 | cmdp = cmdlookup(name, 0); | ||
6352 | if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION) | ||
6353 | ash_msg_and_raise_error("%s not defined in %s", name, fullname); | ||
6354 | stunalloc(fullname); | ||
6355 | goto success; | ||
6356 | } | ||
6357 | TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); | ||
6358 | if (!updatetbl) { | ||
6359 | entry->cmdtype = CMDNORMAL; | ||
6360 | entry->u.index = idx; | ||
6361 | return; | ||
6362 | } | ||
6363 | INT_OFF; | ||
6364 | cmdp = cmdlookup(name, 1); | ||
6365 | cmdp->cmdtype = CMDNORMAL; | ||
6366 | cmdp->param.index = idx; | ||
6367 | INT_ON; | ||
6368 | goto success; | ||
6369 | } | ||
6370 | |||
6371 | /* We failed. If there was an entry for this command, delete it */ | ||
6372 | if (cmdp && updatetbl) | ||
6373 | delete_cmd_entry(); | ||
6374 | if (act & DO_ERR) | ||
6375 | ash_msg("%s: %s", name, errmsg(e, "not found")); | ||
6376 | entry->cmdtype = CMDUNKNOWN; | ||
6377 | return; | ||
6378 | |||
6379 | builtin_success: | ||
6380 | if (!updatetbl) { | ||
6381 | entry->cmdtype = CMDBUILTIN; | ||
6382 | entry->u.cmd = bcmd; | ||
6383 | return; | ||
6384 | } | ||
6385 | INT_OFF; | ||
6386 | cmdp = cmdlookup(name, 1); | ||
6387 | cmdp->cmdtype = CMDBUILTIN; | ||
6388 | cmdp->param.cmd = bcmd; | ||
6389 | INT_ON; | ||
6390 | success: | ||
6391 | cmdp->rehash = 0; | ||
6392 | entry->cmdtype = cmdp->cmdtype; | ||
6393 | entry->u = cmdp->param; | ||
6394 | } | ||
6395 | |||
6396 | /* | ||
6397 | * Search the table of builtin commands. | ||
6398 | */ | ||
6399 | static struct builtincmd * | ||
6400 | find_builtin(const char *name) | ||
6401 | { | ||
6402 | struct builtincmd *bp; | ||
6403 | |||
6404 | bp = bsearch( | ||
6405 | name, builtintab, NUMBUILTINS, sizeof(builtintab[0]), | ||
6406 | pstrcmp | ||
6407 | ); | ||
6408 | return bp; | ||
6409 | } | ||
6410 | |||
6411 | /* | ||
6412 | * Called when a cd is done. Marks all commands so the next time they | ||
6413 | * are executed they will be rehashed. | ||
6414 | */ | ||
6415 | static void | ||
6416 | hashcd(void) | ||
6417 | { | ||
6418 | struct tblentry **pp; | ||
6419 | struct tblentry *cmdp; | ||
6420 | |||
6421 | for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) { | ||
6422 | for (cmdp = *pp; cmdp; cmdp = cmdp->next) { | ||
6423 | if (cmdp->cmdtype == CMDNORMAL || ( | ||
6424 | cmdp->cmdtype == CMDBUILTIN && | ||
6425 | !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) && | ||
6426 | builtinloc > 0 | ||
6427 | )) | ||
6428 | cmdp->rehash = 1; | ||
6429 | } | ||
6430 | } | ||
6431 | } | ||
6432 | |||
6433 | /* | ||
6434 | * Fix command hash table when PATH changed. | ||
6435 | * Called before PATH is changed. The argument is the new value of PATH; | ||
6436 | * pathval() still returns the old value at this point. | ||
6437 | * Called with interrupts off. | ||
6438 | */ | ||
6439 | static void | ||
6440 | changepath(const char *newval) | ||
6441 | { | ||
6442 | const char *old, *new; | ||
6443 | int idx; | ||
6444 | int firstchange; | ||
6445 | int idx_bltin; | ||
6446 | |||
6447 | old = pathval(); | ||
6448 | new = newval; | ||
6449 | firstchange = 9999; /* assume no change */ | ||
6450 | idx = 0; | ||
6451 | idx_bltin = -1; | ||
6452 | for (;;) { | ||
6453 | if (*old != *new) { | ||
6454 | firstchange = idx; | ||
6455 | if ((*old == '\0' && *new == ':') | ||
6456 | || (*old == ':' && *new == '\0')) | ||
6457 | firstchange++; | ||
6458 | old = new; /* ignore subsequent differences */ | ||
6459 | } | ||
6460 | if (*new == '\0') | ||
6461 | break; | ||
6462 | if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin")) | ||
6463 | idx_bltin = idx; | ||
6464 | if (*new == ':') { | ||
6465 | idx++; | ||
6466 | } | ||
6467 | new++, old++; | ||
6468 | } | ||
6469 | if (builtinloc < 0 && idx_bltin >= 0) | ||
6470 | builtinloc = idx_bltin; /* zap builtins */ | ||
6471 | if (builtinloc >= 0 && idx_bltin < 0) | ||
6472 | firstchange = 0; | ||
6473 | clearcmdentry(firstchange); | ||
6474 | builtinloc = idx_bltin; | ||
6475 | } | ||
6476 | |||
6477 | |||
6478 | /* | ||
6479 | * Make a copy of a parse tree. | ||
6480 | */ | ||
6481 | static struct funcnode * | ||
6482 | copyfunc(union node *n) | ||
6483 | { | ||
6484 | struct funcnode *f; | ||
6485 | size_t blocksize; | ||
6486 | |||
6487 | funcblocksize = offsetof(struct funcnode, n); | ||
6488 | funcstringsize = 0; | ||
6489 | calcsize(n); | ||
6490 | blocksize = funcblocksize; | ||
6491 | f = ckmalloc(blocksize + funcstringsize); | ||
6492 | funcblock = (char *) f + offsetof(struct funcnode, n); | ||
6493 | funcstring = (char *) f + blocksize; | ||
6494 | copynode(n); | ||
6495 | f->count = 0; | ||
6496 | return f; | ||
6497 | } | ||
6498 | |||
6499 | /* | ||
6500 | * Define a shell function. | ||
6501 | */ | ||
6502 | static void | ||
6503 | defun(char *name, union node *func) | ||
6504 | { | ||
6505 | struct cmdentry entry; | ||
6506 | |||
6507 | INT_OFF; | ||
6508 | entry.cmdtype = CMDFUNCTION; | ||
6509 | entry.u.func = copyfunc(func); | ||
6510 | addcmdentry(name, &entry); | ||
6511 | INT_ON; | ||
6512 | } | ||
6513 | |||
6514 | /* | ||
6515 | * Delete a function if it exists. | ||
6516 | */ | ||
6517 | static void | ||
6518 | unsetfunc(const char *name) | ||
6519 | { | ||
6520 | struct tblentry *cmdp; | ||
6521 | |||
6522 | cmdp = cmdlookup(name, 0); | ||
6523 | if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION) | ||
6524 | delete_cmd_entry(); | ||
6525 | } | ||
6526 | |||
6527 | |||
6528 | /* | ||
6529 | * Locate and print what a word is... | ||
6530 | */ | ||
6531 | #if ENABLE_ASH_CMDCMD | ||
6532 | static int | ||
6533 | describe_command(char *command, int describe_command_verbose) | ||
6534 | #else | ||
6535 | #define describe_command_verbose 1 | ||
6536 | static int | ||
6537 | describe_command(char *command) | ||
6538 | #endif | ||
6539 | { | ||
6540 | struct cmdentry entry; | ||
6541 | struct tblentry *cmdp; | ||
6542 | #if ENABLE_ASH_ALIAS | ||
6543 | const struct alias *ap; | ||
6544 | #endif | ||
6545 | const char *path = pathval(); | ||
6546 | |||
6547 | if (describe_command_verbose) { | ||
6548 | out1str(command); | ||
6549 | } | ||
6550 | |||
6551 | /* First look at the keywords */ | ||
6552 | if (findkwd(command)) { | ||
6553 | out1str(describe_command_verbose ? " is a shell keyword" : command); | ||
6554 | goto out; | ||
6555 | } | ||
6556 | |||
6557 | #if ENABLE_ASH_ALIAS | ||
6558 | /* Then look at the aliases */ | ||
6559 | ap = lookupalias(command, 0); | ||
6560 | if (ap != NULL) { | ||
6561 | if (describe_command_verbose) { | ||
6562 | out1fmt(" is an alias for %s", ap->val); | ||
6563 | } else { | ||
6564 | out1str("alias "); | ||
6565 | printalias(ap); | ||
6566 | return 0; | ||
6567 | } | ||
6568 | goto out; | ||
6569 | } | ||
6570 | #endif | ||
6571 | /* Then check if it is a tracked alias */ | ||
6572 | cmdp = cmdlookup(command, 0); | ||
6573 | if (cmdp != NULL) { | ||
6574 | entry.cmdtype = cmdp->cmdtype; | ||
6575 | entry.u = cmdp->param; | ||
6576 | } else { | ||
6577 | /* Finally use brute force */ | ||
6578 | find_command(command, &entry, DO_ABS, path); | ||
6579 | } | ||
6580 | |||
6581 | switch (entry.cmdtype) { | ||
6582 | case CMDNORMAL: { | ||
6583 | int j = entry.u.index; | ||
6584 | char *p; | ||
6585 | if (j == -1) { | ||
6586 | p = command; | ||
6587 | } else { | ||
6588 | do { | ||
6589 | p = padvance(&path, command); | ||
6590 | stunalloc(p); | ||
6591 | } while (--j >= 0); | ||
6592 | } | ||
6593 | if (describe_command_verbose) { | ||
6594 | out1fmt(" is%s %s", | ||
6595 | (cmdp ? " a tracked alias for" : nullstr), p | ||
6596 | ); | ||
6597 | } else { | ||
6598 | out1str(p); | ||
6599 | } | ||
6600 | break; | ||
6601 | } | ||
6602 | |||
6603 | case CMDFUNCTION: | ||
6604 | if (describe_command_verbose) { | ||
6605 | out1str(" is a shell function"); | ||
6606 | } else { | ||
6607 | out1str(command); | ||
6608 | } | ||
6609 | break; | ||
6610 | |||
6611 | case CMDBUILTIN: | ||
6612 | if (describe_command_verbose) { | ||
6613 | out1fmt(" is a %sshell builtin", | ||
6614 | IS_BUILTIN_SPECIAL(entry.u.cmd) ? | ||
6615 | "special " : nullstr | ||
6616 | ); | ||
6617 | } else { | ||
6618 | out1str(command); | ||
6619 | } | ||
6620 | break; | ||
6621 | |||
6622 | default: | ||
6623 | if (describe_command_verbose) { | ||
6624 | out1str(": not found\n"); | ||
6625 | } | ||
6626 | return 127; | ||
6627 | } | ||
6628 | out: | ||
6629 | outstr("\n", stdout); | ||
6630 | return 0; | ||
6631 | } | ||
6632 | |||
6633 | static int | ||
6634 | typecmd(int argc, char **argv) | ||
6635 | { | ||
6636 | int i; | ||
6637 | int err = 0; | ||
6638 | |||
6639 | for (i = 1; i < argc; i++) { | ||
6640 | #if ENABLE_ASH_CMDCMD | ||
6641 | err |= describe_command(argv[i], 1); | ||
6642 | #else | ||
6643 | err |= describe_command(argv[i]); | ||
6644 | #endif | ||
6645 | } | ||
6646 | return err; | ||
6647 | } | ||
6648 | |||
6649 | #if ENABLE_ASH_CMDCMD | ||
6650 | static int | ||
6651 | commandcmd(int argc, char **argv) | ||
6652 | { | ||
6653 | int c; | ||
6654 | enum { | ||
6655 | VERIFY_BRIEF = 1, | ||
6656 | VERIFY_VERBOSE = 2, | ||
6657 | } verify = 0; | ||
6658 | |||
6659 | while ((c = nextopt("pvV")) != '\0') | ||
6660 | if (c == 'V') | ||
6661 | verify |= VERIFY_VERBOSE; | ||
6662 | else if (c == 'v') | ||
6663 | verify |= VERIFY_BRIEF; | ||
6664 | #if DEBUG | ||
6665 | else if (c != 'p') | ||
6666 | abort(); | ||
6667 | #endif | ||
6668 | if (verify) | ||
6669 | return describe_command(*argptr, verify - VERIFY_BRIEF); | ||
6670 | |||
6671 | return 0; | ||
6672 | } | ||
6673 | #endif | ||
6674 | |||
6675 | |||
6676 | /* ============ input.c | 6909 | /* ============ input.c |
6677 | * | 6910 | * |
6678 | * This implements the input routines used by the parser. | 6911 | * This implements the input routines used by the parser. |
@@ -8542,211 +8775,6 @@ find_dot_file(char *name) | |||
8542 | /* NOTREACHED */ | 8775 | /* NOTREACHED */ |
8543 | } | 8776 | } |
8544 | 8777 | ||
8545 | static void | ||
8546 | calcsize(union node *n) | ||
8547 | { | ||
8548 | if (n == NULL) | ||
8549 | return; | ||
8550 | funcblocksize += nodesize[n->type]; | ||
8551 | switch (n->type) { | ||
8552 | case NCMD: | ||
8553 | calcsize(n->ncmd.redirect); | ||
8554 | calcsize(n->ncmd.args); | ||
8555 | calcsize(n->ncmd.assign); | ||
8556 | break; | ||
8557 | case NPIPE: | ||
8558 | sizenodelist(n->npipe.cmdlist); | ||
8559 | break; | ||
8560 | case NREDIR: | ||
8561 | case NBACKGND: | ||
8562 | case NSUBSHELL: | ||
8563 | calcsize(n->nredir.redirect); | ||
8564 | calcsize(n->nredir.n); | ||
8565 | break; | ||
8566 | case NAND: | ||
8567 | case NOR: | ||
8568 | case NSEMI: | ||
8569 | case NWHILE: | ||
8570 | case NUNTIL: | ||
8571 | calcsize(n->nbinary.ch2); | ||
8572 | calcsize(n->nbinary.ch1); | ||
8573 | break; | ||
8574 | case NIF: | ||
8575 | calcsize(n->nif.elsepart); | ||
8576 | calcsize(n->nif.ifpart); | ||
8577 | calcsize(n->nif.test); | ||
8578 | break; | ||
8579 | case NFOR: | ||
8580 | funcstringsize += strlen(n->nfor.var) + 1; | ||
8581 | calcsize(n->nfor.body); | ||
8582 | calcsize(n->nfor.args); | ||
8583 | break; | ||
8584 | case NCASE: | ||
8585 | calcsize(n->ncase.cases); | ||
8586 | calcsize(n->ncase.expr); | ||
8587 | break; | ||
8588 | case NCLIST: | ||
8589 | calcsize(n->nclist.body); | ||
8590 | calcsize(n->nclist.pattern); | ||
8591 | calcsize(n->nclist.next); | ||
8592 | break; | ||
8593 | case NDEFUN: | ||
8594 | case NARG: | ||
8595 | sizenodelist(n->narg.backquote); | ||
8596 | funcstringsize += strlen(n->narg.text) + 1; | ||
8597 | calcsize(n->narg.next); | ||
8598 | break; | ||
8599 | case NTO: | ||
8600 | case NCLOBBER: | ||
8601 | case NFROM: | ||
8602 | case NFROMTO: | ||
8603 | case NAPPEND: | ||
8604 | calcsize(n->nfile.fname); | ||
8605 | calcsize(n->nfile.next); | ||
8606 | break; | ||
8607 | case NTOFD: | ||
8608 | case NFROMFD: | ||
8609 | calcsize(n->ndup.vname); | ||
8610 | calcsize(n->ndup.next); | ||
8611 | break; | ||
8612 | case NHERE: | ||
8613 | case NXHERE: | ||
8614 | calcsize(n->nhere.doc); | ||
8615 | calcsize(n->nhere.next); | ||
8616 | break; | ||
8617 | case NNOT: | ||
8618 | calcsize(n->nnot.com); | ||
8619 | break; | ||
8620 | }; | ||
8621 | } | ||
8622 | |||
8623 | static void | ||
8624 | sizenodelist(struct nodelist *lp) | ||
8625 | { | ||
8626 | while (lp) { | ||
8627 | funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); | ||
8628 | calcsize(lp->n); | ||
8629 | lp = lp->next; | ||
8630 | } | ||
8631 | } | ||
8632 | |||
8633 | static union node * | ||
8634 | copynode(union node *n) | ||
8635 | { | ||
8636 | union node *new; | ||
8637 | |||
8638 | if (n == NULL) | ||
8639 | return NULL; | ||
8640 | new = funcblock; | ||
8641 | funcblock = (char *) funcblock + nodesize[n->type]; | ||
8642 | |||
8643 | switch (n->type) { | ||
8644 | case NCMD: | ||
8645 | new->ncmd.redirect = copynode(n->ncmd.redirect); | ||
8646 | new->ncmd.args = copynode(n->ncmd.args); | ||
8647 | new->ncmd.assign = copynode(n->ncmd.assign); | ||
8648 | break; | ||
8649 | case NPIPE: | ||
8650 | new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); | ||
8651 | new->npipe.backgnd = n->npipe.backgnd; | ||
8652 | break; | ||
8653 | case NREDIR: | ||
8654 | case NBACKGND: | ||
8655 | case NSUBSHELL: | ||
8656 | new->nredir.redirect = copynode(n->nredir.redirect); | ||
8657 | new->nredir.n = copynode(n->nredir.n); | ||
8658 | break; | ||
8659 | case NAND: | ||
8660 | case NOR: | ||
8661 | case NSEMI: | ||
8662 | case NWHILE: | ||
8663 | case NUNTIL: | ||
8664 | new->nbinary.ch2 = copynode(n->nbinary.ch2); | ||
8665 | new->nbinary.ch1 = copynode(n->nbinary.ch1); | ||
8666 | break; | ||
8667 | case NIF: | ||
8668 | new->nif.elsepart = copynode(n->nif.elsepart); | ||
8669 | new->nif.ifpart = copynode(n->nif.ifpart); | ||
8670 | new->nif.test = copynode(n->nif.test); | ||
8671 | break; | ||
8672 | case NFOR: | ||
8673 | new->nfor.var = nodeckstrdup(n->nfor.var); | ||
8674 | new->nfor.body = copynode(n->nfor.body); | ||
8675 | new->nfor.args = copynode(n->nfor.args); | ||
8676 | break; | ||
8677 | case NCASE: | ||
8678 | new->ncase.cases = copynode(n->ncase.cases); | ||
8679 | new->ncase.expr = copynode(n->ncase.expr); | ||
8680 | break; | ||
8681 | case NCLIST: | ||
8682 | new->nclist.body = copynode(n->nclist.body); | ||
8683 | new->nclist.pattern = copynode(n->nclist.pattern); | ||
8684 | new->nclist.next = copynode(n->nclist.next); | ||
8685 | break; | ||
8686 | case NDEFUN: | ||
8687 | case NARG: | ||
8688 | new->narg.backquote = copynodelist(n->narg.backquote); | ||
8689 | new->narg.text = nodeckstrdup(n->narg.text); | ||
8690 | new->narg.next = copynode(n->narg.next); | ||
8691 | break; | ||
8692 | case NTO: | ||
8693 | case NCLOBBER: | ||
8694 | case NFROM: | ||
8695 | case NFROMTO: | ||
8696 | case NAPPEND: | ||
8697 | new->nfile.fname = copynode(n->nfile.fname); | ||
8698 | new->nfile.fd = n->nfile.fd; | ||
8699 | new->nfile.next = copynode(n->nfile.next); | ||
8700 | break; | ||
8701 | case NTOFD: | ||
8702 | case NFROMFD: | ||
8703 | new->ndup.vname = copynode(n->ndup.vname); | ||
8704 | new->ndup.dupfd = n->ndup.dupfd; | ||
8705 | new->ndup.fd = n->ndup.fd; | ||
8706 | new->ndup.next = copynode(n->ndup.next); | ||
8707 | break; | ||
8708 | case NHERE: | ||
8709 | case NXHERE: | ||
8710 | new->nhere.doc = copynode(n->nhere.doc); | ||
8711 | new->nhere.fd = n->nhere.fd; | ||
8712 | new->nhere.next = copynode(n->nhere.next); | ||
8713 | break; | ||
8714 | case NNOT: | ||
8715 | new->nnot.com = copynode(n->nnot.com); | ||
8716 | break; | ||
8717 | }; | ||
8718 | new->type = n->type; | ||
8719 | return new; | ||
8720 | } | ||
8721 | |||
8722 | static struct nodelist * | ||
8723 | copynodelist(struct nodelist *lp) | ||
8724 | { | ||
8725 | struct nodelist *start; | ||
8726 | struct nodelist **lpp; | ||
8727 | |||
8728 | lpp = &start; | ||
8729 | while (lp) { | ||
8730 | *lpp = funcblock; | ||
8731 | funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); | ||
8732 | (*lpp)->n = copynode(lp->n); | ||
8733 | lp = lp->next; | ||
8734 | lpp = &(*lpp)->next; | ||
8735 | } | ||
8736 | *lpp = NULL; | ||
8737 | return start; | ||
8738 | } | ||
8739 | |||
8740 | static char * | ||
8741 | nodeckstrdup(char *s) | ||
8742 | { | ||
8743 | char *rtn = funcstring; | ||
8744 | |||
8745 | strcpy(funcstring, s); | ||
8746 | funcstring += strlen(s) + 1; | ||
8747 | return rtn; | ||
8748 | } | ||
8749 | |||
8750 | /* | 8778 | /* |
8751 | * Controls whether the shell is interactive or not. | 8779 | * Controls whether the shell is interactive or not. |
8752 | */ | 8780 | */ |
@@ -8819,7 +8847,6 @@ minus_o(char *name, int val) | |||
8819 | optlist[i] ? "on" : "off"); | 8847 | optlist[i] ? "on" : "off"); |
8820 | } | 8848 | } |
8821 | 8849 | ||
8822 | |||
8823 | static void | 8850 | static void |
8824 | setoption(int flag, int val) | 8851 | setoption(int flag, int val) |
8825 | { | 8852 | { |
@@ -11450,65 +11477,16 @@ exportcmd(int argc, char **argv) | |||
11450 | } | 11477 | } |
11451 | 11478 | ||
11452 | /* | 11479 | /* |
11453 | * Make a variable a local variable. When a variable is made local, it's | 11480 | * Delete a function if it exists. |
11454 | * value and flags are saved in a localvar structure. The saved values | ||
11455 | * will be restored when the shell function returns. We handle the name | ||
11456 | * "-" as a special case. | ||
11457 | */ | 11481 | */ |
11458 | static void | 11482 | static void |
11459 | mklocal(char *name) | 11483 | unsetfunc(const char *name) |
11460 | { | ||
11461 | struct localvar *lvp; | ||
11462 | struct var **vpp; | ||
11463 | struct var *vp; | ||
11464 | |||
11465 | INT_OFF; | ||
11466 | lvp = ckmalloc(sizeof(struct localvar)); | ||
11467 | if (LONE_DASH(name)) { | ||
11468 | char *p; | ||
11469 | p = ckmalloc(sizeof(optlist)); | ||
11470 | lvp->text = memcpy(p, optlist, sizeof(optlist)); | ||
11471 | vp = NULL; | ||
11472 | } else { | ||
11473 | char *eq; | ||
11474 | |||
11475 | vpp = hashvar(name); | ||
11476 | vp = *findvar(vpp, name); | ||
11477 | eq = strchr(name, '='); | ||
11478 | if (vp == NULL) { | ||
11479 | if (eq) | ||
11480 | setvareq(name, VSTRFIXED); | ||
11481 | else | ||
11482 | setvar(name, NULL, VSTRFIXED); | ||
11483 | vp = *vpp; /* the new variable */ | ||
11484 | lvp->flags = VUNSET; | ||
11485 | } else { | ||
11486 | lvp->text = vp->text; | ||
11487 | lvp->flags = vp->flags; | ||
11488 | vp->flags |= VSTRFIXED|VTEXTFIXED; | ||
11489 | if (eq) | ||
11490 | setvareq(name, 0); | ||
11491 | } | ||
11492 | } | ||
11493 | lvp->vp = vp; | ||
11494 | lvp->next = localvars; | ||
11495 | localvars = lvp; | ||
11496 | INT_ON; | ||
11497 | } | ||
11498 | |||
11499 | /* | ||
11500 | * The "local" command. | ||
11501 | */ | ||
11502 | static int | ||
11503 | localcmd(int argc, char **argv) | ||
11504 | { | 11484 | { |
11505 | char *name; | 11485 | struct tblentry *cmdp; |
11506 | 11486 | ||
11507 | argv = argptr; | 11487 | cmdp = cmdlookup(name, 0); |
11508 | while ((name = *argv++) != NULL) { | 11488 | if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION) |
11509 | mklocal(name); | 11489 | delete_cmd_entry(); |
11510 | } | ||
11511 | return 0; | ||
11512 | } | 11490 | } |
11513 | 11491 | ||
11514 | /* | 11492 | /* |