aboutsummaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/hush.c')
-rw-r--r--shell/hush.c78
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}