diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 78 |
1 files changed, 63 insertions, 15 deletions
diff --git a/shell/hush.c b/shell/hush.c index cdc3a8618..ceb8cbb0a 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -907,6 +907,9 @@ struct globals { | |||
907 | unsigned depth_break_continue; | 907 | unsigned depth_break_continue; |
908 | unsigned depth_of_loop; | 908 | unsigned depth_of_loop; |
909 | #endif | 909 | #endif |
910 | #if ENABLE_HUSH_GETOPTS | ||
911 | unsigned getopt_count; | ||
912 | #endif | ||
910 | const char *ifs; | 913 | const char *ifs; |
911 | const char *cwd; | 914 | const char *cwd; |
912 | struct variable *top_var; | 915 | struct variable *top_var; |
@@ -2214,6 +2217,10 @@ static int set_local_var(char *str, unsigned flags) | |||
2214 | cur->flg_export = 1; | 2217 | cur->flg_export = 1; |
2215 | if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') | 2218 | if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') |
2216 | cmdedit_update_prompt(); | 2219 | cmdedit_update_prompt(); |
2220 | #if ENABLE_HUSH_GETOPTS | ||
2221 | if (strncmp(cur->varstr, "OPTIND=", 7) == 0) | ||
2222 | G.getopt_count = 0; | ||
2223 | #endif | ||
2217 | if (cur->flg_export) { | 2224 | if (cur->flg_export) { |
2218 | if (flags & SETFLAG_UNEXPORT) { | 2225 | if (flags & SETFLAG_UNEXPORT) { |
2219 | cur->flg_export = 0; | 2226 | cur->flg_export = 0; |
@@ -2244,6 +2251,10 @@ static int unset_local_var_len(const char *name, int name_len) | |||
2244 | 2251 | ||
2245 | if (!name) | 2252 | if (!name) |
2246 | return EXIT_SUCCESS; | 2253 | return EXIT_SUCCESS; |
2254 | #if ENABLE_HUSH_GETOPTS | ||
2255 | if (name_len == 6 && strncmp(name, "OPTIND", 6) == 0) | ||
2256 | G.getopt_count = 0; | ||
2257 | #endif | ||
2247 | var_pp = &G.top_var; | 2258 | var_pp = &G.top_var; |
2248 | while ((cur = *var_pp) != NULL) { | 2259 | while ((cur = *var_pp) != NULL) { |
2249 | if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { | 2260 | if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { |
@@ -9889,7 +9900,8 @@ Test that VAR is a valid variable name? | |||
9889 | */ | 9900 | */ |
9890 | char cbuf[2]; | 9901 | char cbuf[2]; |
9891 | const char *cp, *optstring, *var; | 9902 | const char *cp, *optstring, *var; |
9892 | int c, exitcode; | 9903 | int c, n, exitcode, my_opterr; |
9904 | unsigned count; | ||
9893 | 9905 | ||
9894 | optstring = *++argv; | 9906 | optstring = *++argv; |
9895 | if (!optstring || !(var = *++argv)) { | 9907 | if (!optstring || !(var = *++argv)) { |
@@ -9897,17 +9909,18 @@ Test that VAR is a valid variable name? | |||
9897 | return EXIT_FAILURE; | 9909 | return EXIT_FAILURE; |
9898 | } | 9910 | } |
9899 | 9911 | ||
9900 | c = 0; | 9912 | if (argv[1]) |
9913 | argv[0] = G.global_argv[0]; /* for error messages in getopt() */ | ||
9914 | else | ||
9915 | argv = G.global_argv; | ||
9916 | cbuf[1] = '\0'; | ||
9917 | |||
9918 | my_opterr = 0; | ||
9901 | if (optstring[0] != ':') { | 9919 | if (optstring[0] != ':') { |
9902 | cp = get_local_var_value("OPTERR"); | 9920 | cp = get_local_var_value("OPTERR"); |
9903 | /* 0 if "OPTERR=0", 1 otherwise */ | 9921 | /* 0 if "OPTERR=0", 1 otherwise */ |
9904 | c = (!cp || NOT_LONE_CHAR(cp, '0')); | 9922 | my_opterr = (!cp || NOT_LONE_CHAR(cp, '0')); |
9905 | } | 9923 | } |
9906 | opterr = c; | ||
9907 | cp = get_local_var_value("OPTIND"); | ||
9908 | optind = cp ? atoi(cp) : 0; | ||
9909 | optarg = NULL; | ||
9910 | cbuf[1] = '\0'; | ||
9911 | 9924 | ||
9912 | /* getopts stops on first non-option. Add "+" to force that */ | 9925 | /* getopts stops on first non-option. Add "+" to force that */ |
9913 | /*if (optstring[0] != '+')*/ { | 9926 | /*if (optstring[0] != '+')*/ { |
@@ -9916,11 +9929,47 @@ Test that VAR is a valid variable name? | |||
9916 | optstring = s; | 9929 | optstring = s; |
9917 | } | 9930 | } |
9918 | 9931 | ||
9919 | if (argv[1]) | 9932 | /* Naively, now we should just |
9920 | argv[0] = G.global_argv[0]; /* for error messages */ | 9933 | * cp = get_local_var_value("OPTIND"); |
9921 | else | 9934 | * optind = cp ? atoi(cp) : 0; |
9922 | argv = G.global_argv; | 9935 | * optarg = NULL; |
9923 | c = getopt(string_array_len(argv), argv, optstring); | 9936 | * opterr = my_opterr; |
9937 | * c = getopt(string_array_len(argv), argv, optstring); | ||
9938 | * and be done? Not so fast... | ||
9939 | * Unlike normal getopt() usage in C programs, here | ||
9940 | * each successive call will (usually) have the same argv[] CONTENTS, | ||
9941 | * but not the ADDRESSES. Worse yet, it's possible that between | ||
9942 | * invocations of "getopts", there will be calls to shell builtins | ||
9943 | * which use getopt() internally. Example: | ||
9944 | * while getopts "abc" RES -a -bc -abc de; do | ||
9945 | * unset -ff func | ||
9946 | * done | ||
9947 | * This would not work correctly: getopt() call inside "unset" | ||
9948 | * modifies internal libc state which is tracking position in | ||
9949 | * multi-option strings ("-abc"). At best, it can skip options | ||
9950 | * or return the same option infinitely. With glibc implementation | ||
9951 | * of getopt(), it would use outright invalid pointers and return | ||
9952 | * garbage even _without_ "unset" mangling internal state. | ||
9953 | * | ||
9954 | * We resort to resetting getopt() state and calling it N times, | ||
9955 | * until we get Nth result (or failure). | ||
9956 | * (N == G.getopt_count is reset to 0 whenever OPTIND is [un]set). | ||
9957 | */ | ||
9958 | optind = 0; /* reset getopt() state */ | ||
9959 | count = 0; | ||
9960 | n = string_array_len(argv); | ||
9961 | do { | ||
9962 | optarg = NULL; | ||
9963 | opterr = (count < G.getopt_count) ? 0 : my_opterr; | ||
9964 | c = getopt(n, argv, optstring); | ||
9965 | if (c < 0) | ||
9966 | break; | ||
9967 | count++; | ||
9968 | } while (count <= G.getopt_count); | ||
9969 | |||
9970 | /* Set OPTIND. Prevent resetting of the magic counter! */ | ||
9971 | set_local_var_from_halves("OPTIND", utoa(optind)); | ||
9972 | G.getopt_count = count; /* "next time, give me N+1'th result" */ | ||
9924 | 9973 | ||
9925 | /* Set OPTARG */ | 9974 | /* Set OPTARG */ |
9926 | /* Always set or unset, never left as-is, even on exit/error: | 9975 | /* Always set or unset, never left as-is, even on exit/error: |
@@ -9949,10 +9998,9 @@ Test that VAR is a valid variable name? | |||
9949 | c = '?'; | 9998 | c = '?'; |
9950 | } | 9999 | } |
9951 | 10000 | ||
9952 | /* Set VAR and OPTIND */ | 10001 | /* Set VAR */ |
9953 | cbuf[0] = c; | 10002 | cbuf[0] = c; |
9954 | set_local_var_from_halves(var, cbuf); | 10003 | set_local_var_from_halves(var, cbuf); |
9955 | set_local_var_from_halves("OPTIND", utoa(optind)); | ||
9956 | 10004 | ||
9957 | return exitcode; | 10005 | return exitcode; |
9958 | } | 10006 | } |