1/** \ingroup popt
2 * \file popt/poptparse.c
3 */
4
5/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
6   file accompanying popt source distributions, available from
7   ftp://ftp.rpm.org/pub/rpm/dist. */
8
9#include "system.h"
10
11#define POPT_ARGV_ARRAY_GROW_DELTA 5
12
13/*@-boundswrite@*/
14int poptDupArgv(int argc, const char **argv,
15		int * argcPtr, const char *** argvPtr)
16{
17    size_t nb = (argc + 1) * sizeof(*argv);
18    const char ** argv2;
19    char * dst;
20    int i;
21
22    if (argc <= 0 || argv == NULL)	/* XXX can't happen */
23	return POPT_ERROR_NOARG;
24    for (i = 0; i < argc; i++) {
25	if (argv[i] == NULL)
26	    return POPT_ERROR_NOARG;
27	nb += strlen(argv[i]) + 1;
28    }
29
30    dst = malloc(nb);
31    if (dst == NULL)			/* XXX can't happen */
32	return POPT_ERROR_MALLOC;
33    argv2 = (void *) dst;
34    dst += (argc + 1) * sizeof(*argv);
35
36    /*@-branchstate@*/
37    for (i = 0; i < argc; i++) {
38	argv2[i] = dst;
39	dst += strlen(strcpy(dst, argv[i])) + 1;
40    }
41    /*@=branchstate@*/
42    argv2[argc] = NULL;
43
44    if (argvPtr) {
45	*argvPtr = argv2;
46    } else {
47	free(argv2);
48	argv2 = NULL;
49    }
50    if (argcPtr)
51	*argcPtr = argc;
52    return 0;
53}
54/*@=boundswrite@*/
55
56/*@-bounds@*/
57int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
58{
59    const char * src;
60    char quote = '\0';
61    int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
62    const char ** argv = malloc(sizeof(*argv) * argvAlloced);
63    int argc = 0;
64    int buflen = strlen(s) + 1;
65    char * buf = memset(alloca(buflen), 0, buflen);
66    int rc = POPT_ERROR_MALLOC;
67
68    if (argv == NULL) return rc;
69    argv[argc] = buf;
70
71    for (src = s; *src != '\0'; src++) {
72	if (quote == *src) {
73	    quote = '\0';
74	} else if (quote != '\0') {
75	    if (*src == '\\') {
76		src++;
77		if (!*src) {
78		    rc = POPT_ERROR_BADQUOTE;
79		    goto exit;
80		}
81		if (*src != quote) *buf++ = '\\';
82	    }
83	    *buf++ = *src;
84	} else if (isspace(*src)) {
85	    if (*argv[argc] != '\0') {
86		buf++, argc++;
87		if (argc == argvAlloced) {
88		    argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
89		    argv = realloc(argv, sizeof(*argv) * argvAlloced);
90		    if (argv == NULL) goto exit;
91		}
92		argv[argc] = buf;
93	    }
94	} else switch (*src) {
95	  case '"':
96	  case '\'':
97	    quote = *src;
98	    /*@switchbreak@*/ break;
99	  case '\\':
100	    src++;
101	    if (!*src) {
102		rc = POPT_ERROR_BADQUOTE;
103		goto exit;
104	    }
105	    /*@fallthrough@*/
106	  default:
107	    *buf++ = *src;
108	    /*@switchbreak@*/ break;
109	}
110    }
111
112    if (strlen(argv[argc])) {
113	argc++, buf++;
114    }
115
116    rc = poptDupArgv(argc, argv, argcPtr, argvPtr);
117
118exit:
119    if (argv) free(argv);
120    return rc;
121}
122/*@=bounds@*/
123
124/* still in the dev stage.
125 * return values, perhaps 1== file erro
126 * 2== line to long
127 * 3== umm.... more?
128 */
129int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ int flags)
130{
131    char line[999];
132    char * argstr;
133    char * p;
134    char * q;
135    char * x;
136    int t;
137    int argvlen = 0;
138    size_t maxlinelen = sizeof(line);
139    size_t linelen;
140    int maxargvlen = 480;
141    int linenum = 0;
142
143    *argstrp = NULL;
144
145    /*   |   this_is   =   our_line
146     *	     p             q      x
147     */
148
149    if (fp == NULL)
150	return POPT_ERROR_NULLARG;
151
152    argstr = calloc(maxargvlen, sizeof(*argstr));
153    if (argstr == NULL) return POPT_ERROR_MALLOC;
154
155    while (fgets(line, (int)maxlinelen, fp) != NULL) {
156	linenum++;
157	p = line;
158
159	/* loop until first non-space char or EOL */
160	while( *p != '\0' && isspace(*p) )
161	    p++;
162
163	linelen = strlen(p);
164	if (linelen >= maxlinelen-1)
165	    return POPT_ERROR_OVERFLOW;	/* XXX line too long */
166
167	if (*p == '\0' || *p == '\n') continue;	/* line is empty */
168	if (*p == '#') continue;		/* comment line */
169
170	q = p;
171
172	while (*q != '\0' && (!isspace(*q)) && *q != '=')
173	    q++;
174
175	if (isspace(*q)) {
176	    /* a space after the name, find next non space */
177	    *q++='\0';
178	    while( *q != '\0' && isspace((int)*q) ) q++;
179	}
180	if (*q == '\0') {
181	    /* single command line option (ie, no name=val, just name) */
182	    q[-1] = '\0';		/* kill off newline from fgets() call */
183	    argvlen += (t = q - p) + (sizeof(" --")-1);
184	    if (argvlen >= maxargvlen) {
185		maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
186		argstr = realloc(argstr, maxargvlen);
187		if (argstr == NULL) return POPT_ERROR_MALLOC;
188	    }
189	    strcat(argstr, " --");
190	    strcat(argstr, p);
191	    continue;
192	}
193	if (*q != '=')
194	    continue;	/* XXX for now, silently ignore bogus line */
195
196	/* *q is an equal sign. */
197	*q++ = '\0';
198
199	/* find next non-space letter of value */
200	while (*q != '\0' && isspace(*q))
201	    q++;
202	if (*q == '\0')
203	    continue;	/* XXX silently ignore missing value */
204
205	/* now, loop and strip all ending whitespace */
206	x = p + linelen;
207	while (isspace(*--x))
208	    *x = 0;	/* null out last char if space (including fgets() NL) */
209
210	/* rest of line accept */
211	t = x - p;
212	argvlen += t + (sizeof("' --='")-1);
213	if (argvlen >= maxargvlen) {
214	    maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
215	    argstr = realloc(argstr, maxargvlen);
216	    if (argstr == NULL) return POPT_ERROR_MALLOC;
217	}
218	strcat(argstr, " --");
219	strcat(argstr, p);
220	strcat(argstr, "=\"");
221	strcat(argstr, q);
222	strcat(argstr, "\"");
223    }
224
225    *argstrp = argstr;
226    return 0;
227}
228