X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=libiberty%2Fargv.c;h=c544dad73eec8f18c2725fd25b88a033fec223fc;hb=3819af136deb7ba6bc2058d7848cf80b33dbe51c;hp=79241b6dce7867763af5af3e2b75d31c8eac55f9;hpb=48e79b0a531fae383ad638cdd243c2012647451a;p=deliverable%2Fbinutils-gdb.git diff --git a/libiberty/argv.c b/libiberty/argv.c index 79241b6dce..c544dad73e 100644 --- a/libiberty/argv.c +++ b/libiberty/argv.c @@ -1,5 +1,5 @@ /* Create and destroy argument vectors (argv's) - Copyright (C) 1992, 2001 Free Software Foundation, Inc. + Copyright (C) 1992-2017 Free Software Foundation, Inc. Written by Fred Fish @ Cygnus Support This file is part of the libiberty library. @@ -35,6 +35,13 @@ Boston, MA 02110-1301, USA. */ #include #include #include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif #ifndef NULL #define NULL 0 @@ -49,7 +56,7 @@ Boston, MA 02110-1301, USA. */ /* -@deftypefn Extension char** dupargv (char **@var{vector}) +@deftypefn Extension char** dupargv (char * const *@var{vector}) Duplicate an argument vector. Simply scans through @var{vector}, duplicating each argument until the terminating @code{NULL} is found. @@ -62,7 +69,7 @@ argument vector. */ char ** -dupargv (char **argv) +dupargv (char * const *argv) { int argc; char **copy; @@ -72,22 +79,11 @@ dupargv (char **argv) /* the vector */ for (argc = 0; argv[argc] != NULL; argc++); - copy = (char **) malloc ((argc + 1) * sizeof (char *)); - if (copy == NULL) - return NULL; - + copy = (char **) xmalloc ((argc + 1) * sizeof (char *)); + /* the strings */ for (argc = 0; argv[argc] != NULL; argc++) - { - int len = strlen (argv[argc]); - copy[argc] = (char *) malloc (len + 1); - if (copy[argc] == NULL) - { - freeargv (copy); - return NULL; - } - strcpy (copy[argc], argv[argc]); - } + copy[argc] = xstrdup (argv[argc]); copy[argc] = NULL; return copy; } @@ -119,6 +115,24 @@ void freeargv (char **vector) } } +static void +consume_whitespace (const char **input) +{ + while (ISSPACE (**input)) + { + (*input)++; + } +} + +static int +only_whitespace (const char* input) +{ + while (*input != EOS && ISSPACE (*input)) + input++; + + return (*input == EOS); +} + /* @deftypefn Extension char** buildargv (char *@var{sp}) @@ -131,7 +145,7 @@ remains unchanged. The last element of the vector is followed by a @code{NULL} element. All of the memory for the pointer array and copies of the string -is obtained from @code{malloc}. All of the memory can be returned to the +is obtained from @code{xmalloc}. All of the memory can be returned to the system with the single function call @code{freeargv}, which takes the returned result of @code{buildargv}, as it's argument. @@ -173,37 +187,26 @@ char **buildargv (const char *input) if (input != NULL) { - copybuf = (char *) alloca (strlen (input) + 1); + copybuf = (char *) xmalloc (strlen (input) + 1); /* Is a do{}while to always execute the loop once. Always return an argv, even for null strings. See NOTES above, test case below. */ do { /* Pick off argv[argc] */ - while (ISBLANK (*input)) - { - input++; - } + consume_whitespace (&input); + if ((maxargc == 0) || (argc >= (maxargc - 1))) { /* argv needs initialization, or expansion */ if (argv == NULL) { maxargc = INITIAL_MAXARGC; - nargv = (char **) malloc (maxargc * sizeof (char *)); + nargv = (char **) xmalloc (maxargc * sizeof (char *)); } else { maxargc *= 2; - nargv = (char **) realloc (argv, maxargc * sizeof (char *)); - } - if (nargv == NULL) - { - if (argv != NULL) - { - freeargv (argv); - argv = NULL; - } - break; + nargv = (char **) xrealloc (argv, maxargc * sizeof (char *)); } argv = nargv; argv[argc] = NULL; @@ -268,28 +271,76 @@ char **buildargv (const char *input) } } *arg = EOS; - argv[argc] = strdup (copybuf); - if (argv[argc] == NULL) - { - freeargv (argv); - argv = NULL; - break; - } + argv[argc] = xstrdup (copybuf); argc++; argv[argc] = NULL; - while (ISSPACE (*input)) - { - input++; - } + consume_whitespace (&input); } while (*input != EOS); + + free (copybuf); } return (argv); } /* +@deftypefn Extension int writeargv (char * const *@var{argv}, FILE *@var{file}) + +Write each member of ARGV, handling all necessary quoting, to the file +named by FILE, separated by whitespace. Return 0 on success, non-zero +if an error occurred while writing to FILE. + +@end deftypefn + +*/ + +int +writeargv (char * const *argv, FILE *f) +{ + int status = 0; + + if (f == NULL) + return 1; + + while (*argv != NULL) + { + const char *arg = *argv; + + while (*arg != EOS) + { + char c = *arg; + + if (ISSPACE(c) || c == '\\' || c == '\'' || c == '"') + if (EOF == fputc ('\\', f)) + { + status = 1; + goto done; + } + + if (EOF == fputc (c, f)) + { + status = 1; + goto done; + } + arg++; + } + + if (EOF == fputc ('\n', f)) + { + status = 1; + goto done; + } + argv++; + } + + done: + return status; +} + +/* + @deftypefn Extension void expandargv (int *@var{argcp}, char ***@var{argvp}) The @var{argcp} and @code{argvp} arguments are pointers to the usual @@ -312,14 +363,15 @@ operating system to free the memory when the program exits. */ void -expandargv (argcp, argvp) - int *argcp; - char ***argvp; +expandargv (int *argcp, char ***argvp) { /* The argument we are currently processing. */ int i = 0; /* Non-zero if ***argvp has been dynamically allocated. */ int argv_dynamic = 0; + /* Limit the number of response files that we parse in order + to prevent infinite recursion. */ + unsigned int iteration_limit = 2000; /* Loop over the arguments, handling response files. We always skip ARGVP[0], as that is the name of the program being run. */ while (++i < *argcp) @@ -328,8 +380,12 @@ expandargv (argcp, argvp) const char *filename; /* The response file. */ FILE *f; - /* The number of characters in the response file. */ + /* An upper bound on the number of characters in the response + file. */ long pos; + /* The number of characters in the response file, when actually + read. */ + size_t len; /* A dynamically allocated buffer used to hold options read from a response file. */ char *buffer; @@ -337,11 +393,29 @@ expandargv (argcp, argvp) response file. */ char **file_argv; /* The number of options read from the response file, if any. */ - size_t file_argc; + size_t file_argc; +#ifdef S_ISDIR + struct stat sb; +#endif /* We are only interested in options of the form "@file". */ filename = (*argvp)[i]; if (filename[0] != '@') continue; + /* If we have iterated too many times then stop. */ + if (-- iteration_limit == 0) + { + fprintf (stderr, "%s: error: too many @-files encountered\n", (*argvp)[0]); + xexit (1); + } +#ifdef S_ISDIR + if (stat (filename+1, &sb) < 0) + continue; + if (S_ISDIR(sb.st_mode)) + { + fprintf (stderr, "%s: error: @-file refers to a directory\n", (*argvp)[0]); + xexit (1); + } +#endif /* Read the contents of the file. */ f = fopen (++filename, "r"); if (!f) @@ -354,25 +428,32 @@ expandargv (argcp, argvp) if (fseek (f, 0L, SEEK_SET) == -1) goto error; buffer = (char *) xmalloc (pos * sizeof (char) + 1); - if (fread (buffer, sizeof (char), pos, f) != (size_t) pos) + len = fread (buffer, sizeof (char), pos, f); + if (len != (size_t) pos + /* On Windows, fread may return a value smaller than POS, + due to CR/LF->CR translation when reading text files. + That does not in-and-of itself indicate failure. */ + && ferror (f)) goto error; /* Add a NUL terminator. */ - buffer[pos] = '\0'; - /* Parse the string. */ - file_argv = buildargv (buffer); - /* If *ARGVP is not already dynamically allocated, copy it. */ - if (!argv_dynamic) + buffer[len] = '\0'; + /* If the file is empty or contains only whitespace, buildargv would + return a single empty argument. In this context we want no arguments, + instead. */ + if (only_whitespace (buffer)) { - *argvp = dupargv (*argvp); - if (!*argvp) - { - fputs ("\nout of memory\n", stderr); - xexit (1); - } + file_argv = (char **) xmalloc (sizeof (char *)); + file_argv[0] = NULL; } + else + /* Parse the string. */ + file_argv = buildargv (buffer); + /* If *ARGVP is not already dynamically allocated, copy it. */ + if (!argv_dynamic) + *argvp = dupargv (*argvp); /* Count the number of arguments. */ file_argc = 0; - while (file_argv[file_argc] && *file_argv[file_argc]) + while (file_argv[file_argc]) ++file_argc; /* Now, insert FILE_ARGV into ARGV. The "+1" below handles the NULL terminator at the end of ARGV. */ @@ -399,6 +480,29 @@ expandargv (argcp, argvp) } } +/* + +@deftypefn Extension int countargv (char * const *@var{argv}) + +Return the number of elements in @var{argv}. +Returns zero if @var{argv} is NULL. + +@end deftypefn + +*/ + +int +countargv (char * const *argv) +{ + int argc; + + if (argv == NULL) + return 0; + for (argc = 0; argv[argc] != NULL; argc++) + continue; + return argc; +} + #ifdef MAIN /* Simple little test driver. */