argv.c revision 38889
1/* Create and destroy argument vectors (argv's)
2   Copyright (C) 1992 Free Software Foundation, Inc.
3   Written by Fred Fish @ Cygnus Support
4
5This file is part of the libiberty library.
6Libiberty is free software; you can redistribute it and/or
7modify it under the terms of the GNU Library General Public
8License as published by the Free Software Foundation; either
9version 2 of the License, or (at your option) any later version.
10
11Libiberty is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14Library General Public License for more details.
15
16You should have received a copy of the GNU Library General Public
17License along with libiberty; see the file COPYING.LIB.  If
18not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, 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#include "ansidecl.h"
26#include "libiberty.h"
27
28#ifdef isspace
29#undef isspace
30#endif
31#define isspace(ch) ((ch) == ' ' || (ch) == '\t')
32
33/*  Routines imported from standard C runtime libraries. */
34
35#ifdef __STDC__
36
37#include <stddef.h>
38extern void *memcpy (void *s1, const void *s2, size_t n);	/* 4.11.2.1 */
39extern size_t strlen (const char *s);				/* 4.11.6.3 */
40extern void *malloc (size_t size);				/* 4.10.3.3 */
41extern void *realloc (void *ptr, size_t size);			/* 4.10.3.4 */
42extern void free (void *ptr);					/* 4.10.3.2 */
43extern char *strdup (const char *s);				/* Non-ANSI */
44
45#else	/* !__STDC__ */
46
47#if !defined _WIN32 || defined __GNUC__
48extern char *memcpy ();		/* Copy memory region */
49extern int strlen ();		/* Count length of string */
50extern char *malloc ();		/* Standard memory allocater */
51extern char *realloc ();	/* Standard memory reallocator */
52extern void free ();		/* Free malloc'd memory */
53extern char *strdup ();		/* Duplicate a string */
54#endif
55
56#endif	/* __STDC__ */
57
58#include "alloca-conf.h"
59
60#ifndef NULL
61#define NULL 0
62#endif
63
64#ifndef EOS
65#define EOS '\0'
66#endif
67
68#define INITIAL_MAXARGC 8	/* Number of args + NULL in initial argv */
69
70
71/*
72
73NAME
74
75	dupargv -- duplicate an argument vector
76
77SYNOPSIS
78
79	char **dupargv (vector)
80	char **vector;
81
82DESCRIPTION
83
84	Duplicate an argument vector.  Simply scans through the
85	vector, duplicating each argument argument until the
86	terminating NULL is found.
87
88RETURNS
89
90	Returns a pointer to the argument vector if
91	successful. Returns NULL if there is insufficient memory to
92	complete building the argument vector.
93
94*/
95
96char **
97dupargv (argv)
98     char **argv;
99{
100  int argc;
101  char **copy;
102
103  if (argv == NULL)
104    return NULL;
105
106  /* the vector */
107  for (argc = 0; argv[argc] != NULL; argc++);
108  copy = (char **) malloc ((argc + 1) * sizeof (char *));
109  if (copy == NULL)
110    return NULL;
111
112  /* the strings */
113  for (argc = 0; argv[argc] != NULL; argc++)
114    {
115      int len = strlen (argv[argc]);
116      copy[argc] = malloc (sizeof (char *) * (len + 1));
117      if (copy[argc] == NULL)
118	{
119	  freeargv (copy);
120	  return NULL;
121	}
122      strcpy (copy[argc], argv[argc]);
123    }
124  copy[argc] = NULL;
125  return copy;
126}
127
128/*
129
130NAME
131
132	freeargv -- free an argument vector
133
134SYNOPSIS
135
136	void freeargv (vector)
137	char **vector;
138
139DESCRIPTION
140
141	Free an argument vector that was built using buildargv.  Simply scans
142	through the vector, freeing the memory for each argument until the
143	terminating NULL is found, and then frees the vector itself.
144
145RETURNS
146
147	No value.
148
149*/
150
151void freeargv (vector)
152char **vector;
153{
154  register char **scan;
155
156  if (vector != NULL)
157    {
158      for (scan = vector; *scan != NULL; scan++)
159	{
160	  free (*scan);
161	}
162      free (vector);
163    }
164}
165
166/*
167
168NAME
169
170	buildargv -- build an argument vector from a string
171
172SYNOPSIS
173
174	char **buildargv (sp)
175	char *sp;
176
177DESCRIPTION
178
179	Given a pointer to a string, parse the string extracting fields
180	separated by whitespace and optionally enclosed within either single
181	or double quotes (which are stripped off), and build a vector of
182	pointers to copies of the string for each field.  The input string
183	remains unchanged.
184
185	All of the memory for the pointer array and copies of the string
186	is obtained from malloc.  All of the memory can be returned to the
187	system with the single function call freeargv, which takes the
188	returned result of buildargv, as it's argument.
189
190	The memory for the argv array is dynamically expanded as necessary.
191
192RETURNS
193
194	Returns a pointer to the argument vector if successful. Returns NULL
195	if the input string pointer is NULL or if there is insufficient
196	memory to complete building the argument vector.
197
198NOTES
199
200	In order to provide a working buffer for extracting arguments into,
201	with appropriate stripping of quotes and translation of backslash
202	sequences, we allocate a working buffer at least as long as the input
203	string.  This ensures that we always have enough space in which to
204	work, since the extracted arg is never larger than the input string.
205
206	If the input is a null string (as opposed to a NULL pointer), then
207	buildarg returns an argv that has one arg, a null string.
208
209	Argv is always kept terminated with a NULL arg pointer, so it can
210	be passed to freeargv at any time, or returned, as appropriate.
211*/
212
213char **buildargv (input)
214char *input;
215{
216  char *arg;
217  char *copybuf;
218  int squote = 0;
219  int dquote = 0;
220  int bsquote = 0;
221  int argc = 0;
222  int maxargc = 0;
223  char **argv = NULL;
224  char **nargv;
225
226  if (input != NULL)
227    {
228      copybuf = alloca (strlen (input) + 1);
229      /* Is a do{}while to always execute the loop once.  Always return an
230	 argv, even for null strings.  See NOTES above, test case below. */
231      do
232	{
233	  /* Pick off argv[argc] */
234	  while (isspace (*input))
235	    {
236	      input++;
237	    }
238	  if ((maxargc == 0) || (argc >= (maxargc - 1)))
239	    {
240	      /* argv needs initialization, or expansion */
241	      if (argv == NULL)
242		{
243		  maxargc = INITIAL_MAXARGC;
244		  nargv = (char **) malloc (maxargc * sizeof (char *));
245		}
246	      else
247		{
248		  maxargc *= 2;
249		  nargv = (char **) realloc (argv, maxargc * sizeof (char *));
250		}
251	      if (nargv == NULL)
252		{
253		  if (argv != NULL)
254		    {
255		      freeargv (argv);
256		      argv = NULL;
257		    }
258		  break;
259		}
260	      argv = nargv;
261	      argv[argc] = NULL;
262	    }
263	  /* Begin scanning arg */
264	  arg = copybuf;
265	  while (*input != EOS)
266	    {
267	      if (isspace (*input) && !squote && !dquote && !bsquote)
268		{
269		  break;
270		}
271	      else
272		{
273		  if (bsquote)
274		    {
275		      bsquote = 0;
276		      *arg++ = *input;
277		    }
278		  else if (*input == '\\')
279		    {
280		      bsquote = 1;
281		    }
282		  else if (squote)
283		    {
284		      if (*input == '\'')
285			{
286			  squote = 0;
287			}
288		      else
289			{
290			  *arg++ = *input;
291			}
292		    }
293		  else if (dquote)
294		    {
295		      if (*input == '"')
296			{
297			  dquote = 0;
298			}
299		      else
300			{
301			  *arg++ = *input;
302			}
303		    }
304		  else
305		    {
306		      if (*input == '\'')
307			{
308			  squote = 1;
309			}
310		      else if (*input == '"')
311			{
312			  dquote = 1;
313			}
314		      else
315			{
316			  *arg++ = *input;
317			}
318		    }
319		  input++;
320		}
321	    }
322	  *arg = EOS;
323	  argv[argc] = strdup (copybuf);
324	  if (argv[argc] == NULL)
325	    {
326	      freeargv (argv);
327	      argv = NULL;
328	      break;
329	    }
330	  argc++;
331	  argv[argc] = NULL;
332
333	  while (isspace (*input))
334	    {
335	      input++;
336	    }
337	}
338      while (*input != EOS);
339    }
340  return (argv);
341}
342
343#ifdef MAIN
344
345/* Simple little test driver. */
346
347static char *tests[] =
348{
349  "a simple command line",
350  "arg 'foo' is single quoted",
351  "arg \"bar\" is double quoted",
352  "arg \"foo bar\" has embedded whitespace",
353  "arg 'Jack said \\'hi\\'' has single quotes",
354  "arg 'Jack said \\\"hi\\\"' has double quotes",
355  "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",
356
357  /* This should be expanded into only one argument.  */
358  "trailing-whitespace ",
359
360  "",
361  NULL
362};
363
364main ()
365{
366  char **argv;
367  char **test;
368  char **targs;
369
370  for (test = tests; *test != NULL; test++)
371    {
372      printf ("buildargv(\"%s\")\n", *test);
373      if ((argv = buildargv (*test)) == NULL)
374	{
375	  printf ("failed!\n\n");
376	}
377      else
378	{
379	  for (targs = argv; *targs != NULL; targs++)
380	    {
381	      printf ("\t\"%s\"\n", *targs);
382	    }
383	  printf ("\n");
384	}
385      freeargv (argv);
386    }
387
388}
389
390#endif	/* MAIN */
391