From 03cd08cca2cdbff4fa6ce254eb219376727c8bdb Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Sat, 10 Sep 1994 07:01:10 +0000 Subject: (decode_switches): Loop until optind hits ARGC, not just until getopt_long returns EOF. Initialize C to zero before loop; in loop if C is EOF, set optarg from ARGV[optind++], else call getopt_long. (decode_env_switches): Use variable_expand instead of allocated_variable_expand. Allocate a fresh buffer to copy split words into; scan characters by hand to break words and debackslashify. (shell_quote): New function. (define_makeflags): Allocate doubled space for switch args, and command variable names and values; use shell_quote to quote those things. --- main.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/main.c b/main.c index 954a00c..10f7eca 100644 --- a/main.c +++ b/main.c @@ -1155,9 +1155,26 @@ decode_switches (argc, argv, env) /* Reset getopt's state. */ optind = 0; - while ((c = getopt_long (argc, argv, - options, long_options, (int *) 0)) != EOF) + c = 0; + while (optind < argc) { + if (c == EOF) + { + /* There are no more options according to getting getopt, but + there are some arguments left. Since we have asked for + non-option arguments to be returned in order, I think this + only happens when there is a "--" argument to prevent later + argument from being options. Since getopt has finished its + job, just update its state variables for the next argument and + set C as if it had returned 1, indicating a non-option + argument. */ + optarg = argv[optind++]; + c = 1; + } + else + /* Parse the next argument. */ + c = getopt_long (argc, argv, options, long_options, (int *) 0); + if (c == 1) { /* Non-option argument. It might be a variable definition. */ @@ -1402,7 +1419,7 @@ decode_env_switches (envar, len) unsigned int len; { char *varref = (char *) alloca (2 + len + 2); - char *value; + char *value, *p; int argc; char **argv; @@ -1412,7 +1429,7 @@ decode_env_switches (envar, len) bcopy (envar, &varref[2], len); varref[2 + len] = ')'; varref[2 + len + 1] = '\0'; - value = allocated_variable_expand (varref); + value = variable_expand (varref); /* Skip whitespace, and check for an empty value. */ value = next_token (value); @@ -1423,12 +1440,34 @@ decode_env_switches (envar, len) /* Allocate a vector that is definitely big enough. */ argv = (char **) alloca ((1 + len + 1) * sizeof (char *)); + /* Allocate a buffer to copy the value into while we split it into words + and unquote it. We must use permanent storage for this because + decode_switches may store pointers into the passed argument words. */ + p = (char *) xmalloc (2 * len); + /* getopt will look at the arguments starting at ARGV[1]. Prepend a spacer word. */ argv[0] = 0; argc = 1; - while ((argv[argc] = find_next_token (&value, (unsigned int *) 0)) != 0) - ++argc; + argv[argc] = p; + while (*value != '\0') + { + if (*value == '\\') + ++value; /* Skip the backslash. */ + else if (isblank (*value)) + { + /* End of the word. */ + *p++ = '\0'; + argv[++argc] = p; + do + ++value; + while (isblank (*value)); + continue; + } + *p++ = *value++; + } + *p = '\0'; + argv[++argc] = 0; if (argc == 2 && argv[1][0] != '-') { @@ -1463,6 +1502,29 @@ decode_env_switches (envar, len) decode_switches (argc, argv, 1); } +/* Quote the string IN so that it will be interpreted as a single word with + no magic by the shell. Write the result into OUT, returning the address + of the next character to be written. Allocating space for OUT twice the + length of IN is always sufficient. */ + +static +#ifdef __GNUC__ +__inline__ +#endif +char * +shell_quote (out, in) + char *out, *in; +{ + while (*in != '\0') + { + if (index ("^;'\"*?[]$<>(){}|&~`\\ \t\r\n\f\v", *in) != 0) + *out++ = '\\'; + *out++ = *in++; + } + + return out; +} + /* Define the MAKEFLAGS and MFLAGS variables to reflect the settings of the command switches. Include options with args if ALL is nonzero. Don't include options with the `no_makefile' flag set if MAKEFILE. */ @@ -1503,7 +1565,7 @@ define_makeflags (all, makefile) if (new->arg == 0) \ ++flagslen; /* Just a single flag letter. */ \ else \ - flagslen += 1 + 1 + 1 + 1 + new->arglen; /* " -x foo" */ \ + flagslen += 1 + 1 + 1 + 1 + 2 * new->arglen; /* " -x foo" */ \ if (!isalnum (cs->c)) \ /* This switch has no single-letter version, so we use the long. */ \ flagslen += 2 + strlen (cs->long_name); \ @@ -1597,11 +1659,11 @@ define_makeflags (all, makefile) for (cv = command_variables; cv != 0; cv = cv->next) { v = cv->variable; - flagslen += strlen (v->name); + flagslen += 2 * strlen (v->name); if (! v->recursive) ++flagslen; ++flagslen; - flagslen += strlen (v->value); + flagslen += 2 * strlen (v->value); } } @@ -1672,17 +1734,12 @@ define_makeflags (all, makefile) } for (cv = command_variables; cv != 0; cv = cv->next) { - /* XXX Quoting? */ - unsigned int len; v = cv->variable; - len = strlen (v->name); - bcopy (v->name, p, len); - p += len; + p = shell_quote (p, v->name); if (! v->recursive) *p++ = ':'; - len = strlen (v->value); - bcopy (v->value, p, len); - p += len; + *p++ = '='; + p = shell_quote (p, v->value); *p++ = ' '; ++words; } -- cgit v1.2.3