Coverage Report

Created: 2023-08-28 06:23

/src/binutils-gdb/libiberty/argv.c
Line
Count
Source (jump to first uncovered line)
1
/* Create and destroy argument vectors (argv's)
2
   Copyright (C) 1992-2023 Free Software Foundation, Inc.
3
   Written by Fred Fish @ Cygnus Support
4
5
This file is part of the libiberty library.
6
Libiberty is free software; you can redistribute it and/or
7
modify it under the terms of the GNU Library General Public
8
License as published by the Free Software Foundation; either
9
version 2 of the License, or (at your option) any later version.
10
11
Libiberty is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
Library General Public License for more details.
15
16
You should have received a copy of the GNU Library General Public
17
License along with libiberty; see the file COPYING.LIB.  If
18
not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
19
Boston, MA 02110-1301, USA.  */
20
21
22
/*  Create and destroy argument vectors.  An argument vector is simply an
23
    array of string pointers, terminated by a NULL pointer. */
24
25
#ifdef HAVE_CONFIG_H
26
#include "config.h"
27
#endif
28
#include "ansidecl.h"
29
#include "libiberty.h"
30
#include "safe-ctype.h"
31
32
/*  Routines imported from standard C runtime libraries. */
33
34
#include <stddef.h>
35
#include <string.h>
36
#include <stdlib.h>
37
#include <stdio.h>
38
#include <sys/types.h>
39
#ifdef HAVE_UNISTD_H
40
#include <unistd.h>
41
#endif
42
#if HAVE_SYS_STAT_H
43
#include <sys/stat.h>
44
#endif
45
46
#ifndef NULL
47
#define NULL 0
48
#endif
49
50
#ifndef EOS
51
0
#define EOS '\0'
52
#endif
53
54
0
#define INITIAL_MAXARGC 8  /* Number of args + NULL in initial argv */
55
56
57
/*
58
59
@deftypefn Extension char** dupargv (char * const *@var{vector})
60
61
Duplicate an argument vector.  Simply scans through @var{vector},
62
duplicating each argument until the terminating @code{NULL} is found.
63
Returns a pointer to the argument vector if successful.  Returns
64
@code{NULL} if there is insufficient memory to complete building the
65
argument vector.
66
67
@end deftypefn
68
69
*/
70
71
char **
72
dupargv (char * const *argv)
73
0
{
74
0
  int argc;
75
0
  char **copy;
76
  
77
0
  if (argv == NULL)
78
0
    return NULL;
79
  
80
  /* the vector */
81
0
  for (argc = 0; argv[argc] != NULL; argc++);
82
0
  copy = (char **) xmalloc ((argc + 1) * sizeof (char *));
83
84
  /* the strings */
85
0
  for (argc = 0; argv[argc] != NULL; argc++)
86
0
    copy[argc] = xstrdup (argv[argc]);
87
0
  copy[argc] = NULL;
88
0
  return copy;
89
0
}
90
91
/*
92
93
@deftypefn Extension void freeargv (char **@var{vector})
94
95
Free an argument vector that was built using @code{buildargv}.  Simply
96
scans through @var{vector}, freeing the memory for each argument until
97
the terminating @code{NULL} is found, and then frees @var{vector}
98
itself.
99
100
@end deftypefn
101
102
*/
103
104
void freeargv (char **vector)
105
0
{
106
0
  register char **scan;
107
108
0
  if (vector != NULL)
109
0
    {
110
0
      for (scan = vector; *scan != NULL; scan++)
111
0
  {
112
0
    free (*scan);
113
0
  }
114
0
      free (vector);
115
0
    }
116
0
}
117
118
static void
119
consume_whitespace (const char **input)
120
0
{
121
0
  while (ISSPACE (**input))
122
0
    {
123
0
      (*input)++;
124
0
    }
125
0
}
126
127
static int
128
only_whitespace (const char* input)
129
0
{
130
0
  while (*input != EOS && ISSPACE (*input))
131
0
    input++;
132
133
0
  return (*input == EOS);
134
0
}
135
136
/*
137
138
@deftypefn Extension char** buildargv (char *@var{sp})
139
140
Given a pointer to a string, parse the string extracting fields
141
separated by whitespace and optionally enclosed within either single
142
or double quotes (which are stripped off), and build a vector of
143
pointers to copies of the string for each field.  The input string
144
remains unchanged.  The last element of the vector is followed by a
145
@code{NULL} element.
146
147
All of the memory for the pointer array and copies of the string
148
is obtained from @code{xmalloc}.  All of the memory can be returned to the
149
system with the single function call @code{freeargv}, which takes the
150
returned result of @code{buildargv}, as it's argument.
151
152
Returns a pointer to the argument vector if successful.  Returns
153
@code{NULL} if @var{sp} is @code{NULL} or if there is insufficient
154
memory to complete building the argument vector.
155
156
If the input is a null string (as opposed to a @code{NULL} pointer),
157
then buildarg returns an argument vector that has one arg, a null
158
string.
159
160
@end deftypefn
161
162
The memory for the argv array is dynamically expanded as necessary.
163
164
In order to provide a working buffer for extracting arguments into,
165
with appropriate stripping of quotes and translation of backslash
166
sequences, we allocate a working buffer at least as long as the input
167
string.  This ensures that we always have enough space in which to
168
work, since the extracted arg is never larger than the input string.
169
170
The argument vector is always kept terminated with a @code{NULL} arg
171
pointer, so it can be passed to @code{freeargv} at any time, or
172
returned, as appropriate.
173
174
*/
175
176
char **buildargv (const char *input)
177
0
{
178
0
  char *arg;
179
0
  char *copybuf;
180
0
  int squote = 0;
181
0
  int dquote = 0;
182
0
  int bsquote = 0;
183
0
  int argc = 0;
184
0
  int maxargc = 0;
185
0
  char **argv = NULL;
186
0
  char **nargv;
187
188
0
  if (input != NULL)
189
0
    {
190
0
      copybuf = (char *) xmalloc (strlen (input) + 1);
191
      /* Is a do{}while to always execute the loop once.  Always return an
192
   argv, even for null strings.  See NOTES above, test case below. */
193
0
      do
194
0
  {
195
    /* Pick off argv[argc] */
196
0
    consume_whitespace (&input);
197
198
0
    if ((maxargc == 0) || (argc >= (maxargc - 1)))
199
0
      {
200
        /* argv needs initialization, or expansion */
201
0
        if (argv == NULL)
202
0
    {
203
0
      maxargc = INITIAL_MAXARGC;
204
0
      nargv = (char **) xmalloc (maxargc * sizeof (char *));
205
0
    }
206
0
        else
207
0
    {
208
0
      maxargc *= 2;
209
0
      nargv = (char **) xrealloc (argv, maxargc * sizeof (char *));
210
0
    }
211
0
        argv = nargv;
212
0
        argv[argc] = NULL;
213
0
      }
214
    /* Begin scanning arg */
215
0
    arg = copybuf;
216
0
    while (*input != EOS)
217
0
      {
218
0
        if (ISSPACE (*input) && !squote && !dquote && !bsquote)
219
0
    {
220
0
      break;
221
0
    }
222
0
        else
223
0
    {
224
0
      if (bsquote)
225
0
        {
226
0
          bsquote = 0;
227
0
          *arg++ = *input;
228
0
        }
229
0
      else if (*input == '\\')
230
0
        {
231
0
          bsquote = 1;
232
0
        }
233
0
      else if (squote)
234
0
        {
235
0
          if (*input == '\'')
236
0
      {
237
0
        squote = 0;
238
0
      }
239
0
          else
240
0
      {
241
0
        *arg++ = *input;
242
0
      }
243
0
        }
244
0
      else if (dquote)
245
0
        {
246
0
          if (*input == '"')
247
0
      {
248
0
        dquote = 0;
249
0
      }
250
0
          else
251
0
      {
252
0
        *arg++ = *input;
253
0
      }
254
0
        }
255
0
      else
256
0
        {
257
0
          if (*input == '\'')
258
0
      {
259
0
        squote = 1;
260
0
      }
261
0
          else if (*input == '"')
262
0
      {
263
0
        dquote = 1;
264
0
      }
265
0
          else
266
0
      {
267
0
        *arg++ = *input;
268
0
      }
269
0
        }
270
0
      input++;
271
0
    }
272
0
      }
273
0
    *arg = EOS;
274
0
    argv[argc] = xstrdup (copybuf);
275
0
    argc++;
276
0
    argv[argc] = NULL;
277
278
0
    consume_whitespace (&input);
279
0
  }
280
0
      while (*input != EOS);
281
282
0
      free (copybuf);
283
0
    }
284
0
  return (argv);
285
0
}
286
287
/*
288
289
@deftypefn Extension int writeargv (char * const *@var{argv}, FILE *@var{file})
290
291
Write each member of ARGV, handling all necessary quoting, to the file
292
associated with FILE, separated by whitespace.  Return 0 on success,
293
non-zero if an error occurred while writing to FILE.
294
295
@end deftypefn
296
297
*/
298
299
int
300
writeargv (char * const *argv, FILE *f)
301
0
{
302
0
  if (f == NULL)
303
0
    return 1;
304
305
0
  while (*argv != NULL)
306
0
    {
307
0
      const char *arg = *argv;
308
309
0
      while (*arg != EOS)
310
0
        {
311
0
          char c = *arg;
312
313
0
          if (ISSPACE(c) || c == '\\' || c == '\'' || c == '"')
314
0
            if (EOF == fputc ('\\', f))
315
0
              return 1;
316
317
0
          if (EOF == fputc (c, f))
318
0
            return 1;
319
    
320
0
          arg++;
321
0
        }
322
323
      /* Write out a pair of quotes for an empty argument.  */
324
0
      if (arg == *argv)
325
0
        if (EOF == fputs ("\"\"", f))
326
0
          return 1;
327
328
0
      if (EOF == fputc ('\n', f))
329
0
        return 1;
330
      
331
0
      argv++;
332
0
    }
333
334
0
  return 0;
335
0
}
336
337
/*
338
339
@deftypefn Extension void expandargv (int *@var{argcp}, char ***@var{argvp})
340
341
The @var{argcp} and @code{argvp} arguments are pointers to the usual
342
@code{argc} and @code{argv} arguments to @code{main}.  This function
343
looks for arguments that begin with the character @samp{@@}.  Any such
344
arguments are interpreted as ``response files''.  The contents of the
345
response file are interpreted as additional command line options.  In
346
particular, the file is separated into whitespace-separated strings;
347
each such string is taken as a command-line option.  The new options
348
are inserted in place of the option naming the response file, and
349
@code{*argcp} and @code{*argvp} will be updated.  If the value of
350
@code{*argvp} is modified by this function, then the new value has
351
been dynamically allocated and can be deallocated by the caller with
352
@code{freeargv}.  However, most callers will simply call
353
@code{expandargv} near the beginning of @code{main} and allow the
354
operating system to free the memory when the program exits.
355
356
@end deftypefn
357
358
*/
359
360
void
361
expandargv (int *argcp, char ***argvp)
362
0
{
363
  /* The argument we are currently processing.  */
364
0
  int i = 0;
365
  /* To check if ***argvp has been dynamically allocated.  */
366
0
  char ** const original_argv = *argvp;
367
  /* Limit the number of response files that we parse in order
368
     to prevent infinite recursion.  */
369
0
  unsigned int iteration_limit = 2000;
370
  /* Loop over the arguments, handling response files.  We always skip
371
     ARGVP[0], as that is the name of the program being run.  */
372
0
  while (++i < *argcp)
373
0
    {
374
      /* The name of the response file.  */
375
0
      const char *filename;
376
      /* The response file.  */
377
0
      FILE *f;
378
      /* An upper bound on the number of characters in the response
379
   file.  */
380
0
      long pos;
381
      /* The number of characters in the response file, when actually
382
   read.  */
383
0
      size_t len;
384
      /* A dynamically allocated buffer used to hold options read from a
385
   response file.  */
386
0
      char *buffer;
387
      /* Dynamically allocated storage for the options read from the
388
   response file.  */
389
0
      char **file_argv;
390
      /* The number of options read from the response file, if any.  */
391
0
      size_t file_argc;
392
0
#ifdef S_ISDIR
393
0
      struct stat sb;
394
0
#endif
395
      /* We are only interested in options of the form "@file".  */
396
0
      filename = (*argvp)[i];
397
0
      if (filename[0] != '@')
398
0
  continue;
399
      /* If we have iterated too many times then stop.  */
400
0
      if (-- iteration_limit == 0)
401
0
  {
402
0
    fprintf (stderr, "%s: error: too many @-files encountered\n", (*argvp)[0]);
403
0
    xexit (1);
404
0
  }
405
0
#ifdef S_ISDIR
406
0
      if (stat (filename+1, &sb) < 0)
407
0
  continue;
408
0
      if (S_ISDIR(sb.st_mode))
409
0
  {
410
0
    fprintf (stderr, "%s: error: @-file refers to a directory\n", (*argvp)[0]);
411
0
    xexit (1);
412
0
  }
413
0
#endif
414
      /* Read the contents of the file.  */
415
0
      f = fopen (++filename, "r");
416
0
      if (!f)
417
0
  continue;
418
0
      if (fseek (f, 0L, SEEK_END) == -1)
419
0
  goto error;
420
0
      pos = ftell (f);
421
0
      if (pos == -1)
422
0
  goto error;
423
0
      if (fseek (f, 0L, SEEK_SET) == -1)
424
0
  goto error;
425
0
      buffer = (char *) xmalloc (pos * sizeof (char) + 1);
426
0
      len = fread (buffer, sizeof (char), pos, f);
427
0
      if (len != (size_t) pos
428
    /* On Windows, fread may return a value smaller than POS,
429
       due to CR/LF->CR translation when reading text files.
430
       That does not in-and-of itself indicate failure.  */
431
0
    && ferror (f))
432
0
  {
433
0
    free (buffer);
434
0
    goto error;
435
0
  }
436
      /* Add a NUL terminator.  */
437
0
      buffer[len] = '\0';
438
      /* If the file is empty or contains only whitespace, buildargv would
439
   return a single empty argument.  In this context we want no arguments,
440
   instead.  */
441
0
      if (only_whitespace (buffer))
442
0
  {
443
0
    file_argv = (char **) xmalloc (sizeof (char *));
444
0
    file_argv[0] = NULL;
445
0
  }
446
0
      else
447
  /* Parse the string.  */
448
0
  file_argv = buildargv (buffer);
449
      /* If *ARGVP is not already dynamically allocated, copy it.  */
450
0
      if (*argvp == original_argv)
451
0
  *argvp = dupargv (*argvp);
452
      /* Count the number of arguments.  */
453
0
      file_argc = 0;
454
0
      while (file_argv[file_argc])
455
0
  ++file_argc;
456
      /* Free the original option's memory.  */
457
0
      free ((*argvp)[i]);
458
      /* Now, insert FILE_ARGV into ARGV.  The "+1" below handles the
459
   NULL terminator at the end of ARGV.  */ 
460
0
      *argvp = ((char **) 
461
0
    xrealloc (*argvp, 
462
0
        (*argcp + file_argc + 1) * sizeof (char *)));
463
0
      memmove (*argvp + i + file_argc, *argvp + i + 1, 
464
0
         (*argcp - i) * sizeof (char *));
465
0
      memcpy (*argvp + i, file_argv, file_argc * sizeof (char *));
466
      /* The original option has been replaced by all the new
467
   options.  */
468
0
      *argcp += file_argc - 1;
469
      /* Free up memory allocated to process the response file.  We do
470
   not use freeargv because the individual options in FILE_ARGV
471
   are now in the main ARGV.  */
472
0
      free (file_argv);
473
0
      free (buffer);
474
      /* Rescan all of the arguments just read to support response
475
   files that include other response files.  */
476
0
      --i;
477
0
    error:
478
      /* We're all done with the file now.  */
479
0
      fclose (f);
480
0
    }
481
0
}
482
483
/*
484
485
@deftypefn Extension int countargv (char * const *@var{argv})
486
487
Return the number of elements in @var{argv}.
488
Returns zero if @var{argv} is NULL.
489
490
@end deftypefn
491
492
*/
493
494
int
495
countargv (char * const *argv)
496
0
{
497
0
  int argc;
498
499
0
  if (argv == NULL)
500
0
    return 0;
501
0
  for (argc = 0; argv[argc] != NULL; argc++)
502
0
    continue;
503
0
  return argc;
504
0
}
505
506
#ifdef MAIN
507
508
/* Simple little test driver. */
509
510
static const char *const tests[] =
511
{
512
  "a simple command line",
513
  "arg 'foo' is single quoted",
514
  "arg \"bar\" is double quoted",
515
  "arg \"foo bar\" has embedded whitespace",
516
  "arg 'Jack said \\'hi\\'' has single quotes",
517
  "arg 'Jack said \\\"hi\\\"' has double quotes",
518
  "a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9",
519
  
520
  /* This should be expanded into only one argument.  */
521
  "trailing-whitespace ",
522
523
  "",
524
  NULL
525
};
526
527
int
528
main (void)
529
{
530
  char **argv;
531
  const char *const *test;
532
  char **targs;
533
534
  for (test = tests; *test != NULL; test++)
535
    {
536
      printf ("buildargv(\"%s\")\n", *test);
537
      if ((argv = buildargv (*test)) == NULL)
538
  {
539
    printf ("failed!\n\n");
540
  }
541
      else
542
  {
543
    for (targs = argv; *targs != NULL; targs++)
544
      {
545
        printf ("\t\"%s\"\n", *targs);
546
      }
547
    printf ("\n");
548
  }
549
      freeargv (argv);
550
    }
551
552
  return 0;
553
}
554
555
#endif  /* MAIN */