Line data Source code
1 : /* argcv.c - simple functions for parsing input based on whitespace
2 : Copyright (C) 1999, 2000, 2001, 2005, 2007 Free Software Foundation, Inc.
3 :
4 : This library is free software; you can redistribute it and/or
5 : modify it under the terms of the GNU Lesser General Public
6 : License as published by the Free Software Foundation; either
7 : version 3 of the License, or (at your option) any later version.
8 :
9 : This library is distributed in the hope that it will be useful,
10 : but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : Lesser General Public License for more details.
13 :
14 : You should have received a copy of the GNU Lesser General
15 : Public License along with this library; if not, write to the
16 : Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 : Boston, MA 02110-1301 USA */
18 :
19 : #ifdef HAVE_CONFIG_H
20 : # include <config.h>
21 : #endif
22 :
23 : #include <ctype.h>
24 : #include <errno.h>
25 : #include <argcv.h>
26 :
27 : /*
28 : * takes a string and splits it into several strings, breaking at ' '
29 : * command is the string to split
30 : * the number of strings is placed into argc
31 : * the split strings are put into argv
32 : * returns 0 on success, nonzero on failure
33 : */
34 :
35 : #define isws(c) ((c)==' '||(c)=='\t'||(c)=='\n')
36 : #define isdelim(c,delim) (strchr(delim,(c))!=NULL)
37 :
38 : static int
39 0 : argcv_scan (int len, const char *command, const char *delim, const char* cmnt,
40 : int *start, int *end, int *save)
41 : {
42 0 : int i = 0;
43 :
44 : for (;;)
45 : {
46 0 : i = *save;
47 :
48 0 : if (i >= len)
49 0 : return i + 1;
50 :
51 : /* Skip initial whitespace */
52 0 : while (i < len && isws (command[i]))
53 0 : i++;
54 0 : *start = i;
55 :
56 0 : if (!isdelim (command[i], delim))
57 : {
58 0 : while (i < len)
59 : {
60 0 : if (command[i] == '\\')
61 : {
62 0 : if (++i == len)
63 0 : break;
64 0 : i++;
65 0 : continue;
66 : }
67 :
68 0 : if (command[i] == '\'' || command[i] == '"')
69 0 : {
70 : int j;
71 0 : for (j = i+1; j < len && command[j] != command[i]; j++)
72 0 : if (command[j] == '\\')
73 0 : j++;
74 0 : if (j < len)
75 0 : i = j+1;
76 : else
77 0 : i++;
78 : }
79 0 : else if (isws (command[i]) || isdelim (command[i], delim))
80 : break;
81 : else
82 0 : i++; /* skip the escaped character */
83 : }
84 0 : i--;
85 : }
86 :
87 0 : *end = i;
88 0 : *save = i + 1;
89 :
90 : /* If we have a token, and it starts with a comment character, skip
91 : to the newline and restart the token search. */
92 0 : if (*save <= len)
93 : {
94 0 : if (cmnt && strchr (cmnt, command[*start]) != NULL)
95 : {
96 0 : i = *save;
97 0 : while (i < len && command[i] != '\n')
98 0 : i++;
99 :
100 0 : *save = i;
101 0 : continue;
102 : }
103 : }
104 0 : break;
105 0 : }
106 0 : return *save;
107 : }
108 :
109 : static char quote_transtab[] = "\\\\a\ab\bf\fn\nr\rt\t";
110 :
111 : int
112 0 : argcv_unquote_char (int c)
113 : {
114 : char *p;
115 :
116 0 : for (p = quote_transtab; *p; p += 2)
117 : {
118 0 : if (*p == c)
119 0 : return p[1];
120 : }
121 0 : return c;
122 : }
123 :
124 : int
125 0 : argcv_quote_char (int c)
126 : {
127 : char *p;
128 :
129 0 : for (p = quote_transtab + sizeof(quote_transtab) - 2;
130 0 : p > quote_transtab; p -= 2)
131 : {
132 0 : if (*p == c)
133 0 : return p[-1];
134 : }
135 0 : return -1;
136 : }
137 :
138 : #define to_num(c) \
139 : (isdigit(c) ? c - '0' : (isxdigit(c) ? toupper(c) - 'A' + 10 : 255 ))
140 :
141 : static int
142 0 : xtonum (int *pval, const char *src, int base, int cnt)
143 : {
144 : int i, val;
145 :
146 0 : for (i = 0, val = 0; i < cnt; i++, src++)
147 : {
148 0 : int n = *(unsigned char*)src;
149 0 : if (n > 127 || (n = to_num(n)) >= base)
150 : break;
151 0 : val = val*base + n;
152 : }
153 0 : *pval = val;
154 0 : return i;
155 : }
156 :
157 : size_t
158 0 : argcv_quoted_length (const char *str, int *quote)
159 : {
160 0 : size_t len = 0;
161 :
162 0 : *quote = 0;
163 0 : for (; *str; str++)
164 : {
165 0 : if (*str == ' ')
166 : {
167 0 : len++;
168 0 : *quote = 1;
169 : }
170 0 : else if (*str == '"' || *str == '\'')
171 : {
172 0 : len += 2;
173 0 : *quote = 1;
174 : }
175 0 : else if (*str != '\t' && *str != '\\' && isprint (*str))
176 0 : len++;
177 0 : else if (argcv_quote_char (*str) != -1)
178 0 : len += 2;
179 : else
180 0 : len += 4;
181 : }
182 0 : return len;
183 : }
184 :
185 : void
186 0 : argcv_unquote_copy (char *dst, const char *src, size_t n)
187 : {
188 0 : int i = 0;
189 : int c;
190 0 : int expect_delim = 0;
191 :
192 0 : while (i < n)
193 : {
194 0 : switch (src[i])
195 : {
196 : case '\'':
197 : case '"':
198 0 : if (!expect_delim)
199 : {
200 : const char *p;
201 :
202 0 : for (p = src+i+1; *p && *p != src[i]; p++)
203 0 : if (*p == '\\')
204 0 : p++;
205 0 : if (*p)
206 0 : expect_delim = src[i++];
207 : else
208 0 : *dst++ = src[i++];
209 : }
210 0 : else if (expect_delim == src[i])
211 0 : ++i;
212 : else
213 0 : *dst++ = src[i++];
214 0 : break;
215 :
216 : case '\\':
217 0 : ++i;
218 0 : if (src[i] == 'x' || src[i] == 'X')
219 : {
220 0 : if (n - i < 2)
221 : {
222 0 : *dst++ = '\\';
223 0 : *dst++ = src[i++];
224 : }
225 : else
226 : {
227 0 : int off = xtonum(&c, src + i + 1, 16, 2);
228 0 : if (off == 0)
229 : {
230 0 : *dst++ = '\\';
231 0 : *dst++ = src[i++];
232 : }
233 : else
234 : {
235 0 : *dst++ = c;
236 0 : i += off + 1;
237 : }
238 : }
239 : }
240 0 : else if ((unsigned char)src[i] < 128 && isdigit(src[i]))
241 : {
242 0 : if (n - i < 1)
243 : {
244 0 : *dst++ = '\\';
245 0 : *dst++ = src[i++];
246 : }
247 : else
248 : {
249 0 : int off = xtonum(&c, src+i, 8, 3);
250 0 : if (off == 0)
251 : {
252 0 : *dst++ = '\\';
253 0 : *dst++ = src[i++];
254 : }
255 : else
256 : {
257 0 : *dst++ = c;
258 0 : i += off;
259 : }
260 : }
261 : }
262 : else
263 0 : *dst++ = argcv_unquote_char (src[i++]);
264 0 : break;
265 :
266 : default:
267 0 : *dst++ = src[i++];
268 : }
269 : }
270 0 : *dst = 0;
271 0 : }
272 :
273 : void
274 0 : argcv_quote_copy (char *dst, const char *src)
275 : {
276 0 : for (; *src; src++)
277 : {
278 0 : if (*src == '"' || *src == '\'')
279 : {
280 0 : *dst++ = '\\';
281 0 : *dst++ = *src;
282 : }
283 0 : else if (*src != '\t' && *src != '\\' && isprint(*src))
284 0 : *dst++ = *src;
285 : else
286 : {
287 0 : int c = argcv_quote_char (*src);
288 0 : *dst++ = '\\';
289 0 : if (c != -1)
290 0 : *dst++ = c;
291 : else
292 : {
293 : char tmp[4];
294 0 : snprintf (tmp, sizeof tmp, "%03o", *(unsigned char*)src);
295 0 : memcpy (dst, tmp, 3);
296 0 : dst += 3;
297 : }
298 : }
299 : }
300 0 : }
301 :
302 : int
303 0 : argcv_get (const char *command, const char *delim, const char *cmnt,
304 : int *argc, char ***argv)
305 : {
306 0 : int len = strlen (command);
307 0 : int i = 0;
308 : int start, end, save;
309 :
310 0 : *argv = NULL;
311 :
312 : /* Count number of arguments */
313 0 : *argc = 0;
314 0 : save = 0;
315 :
316 0 : while (argcv_scan (len, command, delim, cmnt, &start, &end, &save) <= len)
317 0 : (*argc)++;
318 :
319 0 : *argv = calloc ((*argc + 1), sizeof (char *));
320 0 : if (*argv == NULL)
321 0 : return ENOMEM;
322 :
323 0 : i = 0;
324 0 : save = 0;
325 0 : for (i = 0; i < *argc; i++)
326 : {
327 : int n;
328 0 : argcv_scan (len, command, delim, cmnt, &start, &end, &save);
329 :
330 0 : if ((command[start] == '"' || command[end] == '\'')
331 0 : && command[end] == command[start])
332 : {
333 0 : start++;
334 0 : end--;
335 : }
336 0 : n = end - start + 1;
337 0 : (*argv)[i] = calloc (n+1, sizeof (char));
338 0 : if ((*argv)[i] == NULL)
339 0 : return ENOMEM;
340 0 : argcv_unquote_copy ((*argv)[i], &command[start], n);
341 0 : (*argv)[i][n] = 0;
342 : }
343 0 : (*argv)[i] = NULL;
344 0 : return 0;
345 : }
346 :
347 : /*
348 : * frees all elements of an argv array
349 : * argc is the number of elements
350 : * argv is the array
351 : */
352 : int
353 0 : argcv_free (int argc, char **argv)
354 : {
355 0 : while (--argc >= 0)
356 0 : if (argv[argc])
357 0 : free (argv[argc]);
358 0 : free (argv);
359 0 : return 0;
360 : }
361 :
362 : /* Take a argv an make string separated by ' '. */
363 :
364 : int
365 0 : argcv_string (int argc, char **argv, char **pstring)
366 : {
367 : size_t i, j, len;
368 : char *buffer;
369 :
370 : /* No need. */
371 0 : if (pstring == NULL)
372 0 : return EINVAL;
373 :
374 0 : buffer = malloc (1);
375 0 : if (buffer == NULL)
376 0 : return ENOMEM;
377 0 : *buffer = '\0';
378 :
379 0 : for (len = i = j = 0; i < argc; i++)
380 : {
381 : int quote;
382 : int toklen;
383 :
384 0 : toklen = argcv_quoted_length (argv[i], "e);
385 :
386 0 : len += toklen + 2;
387 0 : if (quote)
388 0 : len += 2;
389 :
390 0 : buffer = realloc (buffer, len);
391 0 : if (buffer == NULL)
392 0 : return ENOMEM;
393 :
394 0 : if (i != 0)
395 0 : buffer[j++] = ' ';
396 0 : if (quote)
397 0 : buffer[j++] = '"';
398 0 : argcv_quote_copy (buffer + j, argv[i]);
399 0 : j += toklen;
400 0 : if (quote)
401 0 : buffer[j++] = '"';
402 : }
403 :
404 0 : for (; j > 0 && isspace (buffer[j-1]); j--)
405 : ;
406 0 : buffer[j] = 0;
407 0 : if (pstring)
408 0 : *pstring = buffer;
409 0 : return 0;
410 : }
411 :
|