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