Branch data Line data Source code
1 : : /* gshell.c - Shell-related utilities
2 : : *
3 : : * Copyright 2000 Red Hat, Inc.
4 : : * g_execvpe implementation based on GNU libc execvp:
5 : : * Copyright 1991, 92, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
6 : : *
7 : : * SPDX-License-Identifier: LGPL-2.1-or-later
8 : : *
9 : : * This library is free software; you can redistribute it and/or
10 : : * modify it under the terms of the GNU Lesser General Public
11 : : * License as published by the Free Software Foundation; either
12 : : * version 2.1 of the License, or (at your option) any later version.
13 : : *
14 : : * This library is distributed in the hope that it will be useful,
15 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : : * Lesser General Public License for more details.
18 : : *
19 : : * You should have received a copy of the GNU Lesser General Public License
20 : : * along with this library; if not, see <http://www.gnu.org/licenses/>.
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <string.h>
26 : :
27 : : #include "gshell.h"
28 : :
29 : : #include "gslist.h"
30 : : #include "gstrfuncs.h"
31 : : #include "gstring.h"
32 : : #include "gtestutils.h"
33 : : #include "glibintl.h"
34 : : #include "gthread.h"
35 : :
36 : : /**
37 : : * G_SHELL_ERROR:
38 : : *
39 : : * Error domain for shell functions.
40 : : *
41 : : * Errors in this domain will be from the #GShellError enumeration.
42 : : *
43 : : * See #GError for information on error domains.
44 : : **/
45 : :
46 : : /**
47 : : * GShellError:
48 : : * @G_SHELL_ERROR_BAD_QUOTING: Mismatched or otherwise mangled quoting.
49 : : * @G_SHELL_ERROR_EMPTY_STRING: String to be parsed was empty.
50 : : * @G_SHELL_ERROR_FAILED: Some other error.
51 : : *
52 : : * Error codes returned by shell functions.
53 : : **/
54 : 46 : G_DEFINE_QUARK (g-shell-error-quark, g_shell_error)
55 : :
56 : : /* Single quotes preserve the literal string exactly. escape
57 : : * sequences are not allowed; not even \' - if you want a '
58 : : * in the quoted text, you have to do something like 'foo'\''bar'
59 : : *
60 : : * Double quotes allow $ ` " \ and newline to be escaped with backslash.
61 : : * Otherwise double quotes preserve things literally.
62 : : */
63 : :
64 : : static gboolean
65 : 292 : unquote_string_inplace (gchar* str, gchar** end, GError** err)
66 : : {
67 : : gchar* dest;
68 : : gchar* s;
69 : : gchar quote_char;
70 : :
71 : 292 : g_return_val_if_fail(end != NULL, FALSE);
72 : 292 : g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
73 : 292 : g_return_val_if_fail(str != NULL, FALSE);
74 : :
75 : 292 : dest = s = str;
76 : :
77 : 292 : quote_char = *s;
78 : :
79 : 292 : if (!(*s == '"' || *s == '\''))
80 : : {
81 : 0 : g_set_error_literal (err,
82 : 0 : G_SHELL_ERROR,
83 : : G_SHELL_ERROR_BAD_QUOTING,
84 : 0 : _("Quoted text doesn’t begin with a quotation mark"));
85 : 0 : *end = str;
86 : 0 : return FALSE;
87 : : }
88 : :
89 : : /* Skip the initial quote mark */
90 : 292 : ++s;
91 : :
92 : 292 : if (quote_char == '"')
93 : : {
94 : 196 : while (*s)
95 : : {
96 : 194 : g_assert(s > dest); /* loop invariant */
97 : :
98 : 194 : switch (*s)
99 : : {
100 : 20 : case '"':
101 : : /* End of the string, return now */
102 : 34 : *dest = '\0';
103 : 34 : ++s;
104 : 34 : *end = s;
105 : 34 : return TRUE;
106 : : break;
107 : :
108 : 11 : case '\\':
109 : : /* Possible escaped quote or \ */
110 : 22 : ++s;
111 : 22 : switch (*s)
112 : : {
113 : 8 : case '"':
114 : : case '\\':
115 : : case '`':
116 : : case '$':
117 : : case '\n':
118 : 16 : *dest = *s;
119 : 16 : ++s;
120 : 16 : ++dest;
121 : 16 : break;
122 : :
123 : 3 : default:
124 : : /* not an escaped char */
125 : 6 : *dest = '\\';
126 : 6 : ++dest;
127 : : /* ++s already done. */
128 : 6 : break;
129 : : }
130 : 22 : break;
131 : :
132 : 105 : default:
133 : 138 : *dest = *s;
134 : 138 : ++dest;
135 : 138 : ++s;
136 : 138 : break;
137 : : }
138 : :
139 : 160 : g_assert(s > dest); /* loop invariant */
140 : : }
141 : 1 : }
142 : : else
143 : : {
144 : 9365 : while (*s)
145 : : {
146 : 9361 : g_assert(s > dest); /* loop invariant */
147 : :
148 : 9361 : if (*s == '\'')
149 : : {
150 : : /* End of the string, return now */
151 : 252 : *dest = '\0';
152 : 252 : ++s;
153 : 252 : *end = s;
154 : 252 : return TRUE;
155 : : }
156 : : else
157 : : {
158 : 9109 : *dest = *s;
159 : 9109 : ++dest;
160 : 9109 : ++s;
161 : : }
162 : :
163 : 9109 : g_assert(s > dest); /* loop invariant */
164 : : }
165 : : }
166 : :
167 : : /* If we reach here this means the close quote was never encountered */
168 : :
169 : 6 : *dest = '\0';
170 : :
171 : 9 : g_set_error_literal (err,
172 : 3 : G_SHELL_ERROR,
173 : : G_SHELL_ERROR_BAD_QUOTING,
174 : 3 : _("Unmatched quotation mark in command line or other shell-quoted text"));
175 : 6 : *end = s;
176 : 6 : return FALSE;
177 : 58 : }
178 : :
179 : : /**
180 : : * g_shell_quote:
181 : : * @unquoted_string: (type filename): a literal string
182 : : *
183 : : * Quotes a string so that the shell (/bin/sh) will interpret the
184 : : * quoted string to mean @unquoted_string.
185 : : *
186 : : * If you pass a filename or other untrusted input to [func@GLib.shell_parse_argv],
187 : : * you should first quote it with this function. This is sufficient to ensure
188 : : * untrusted input cannot ‘break out’ of the quotes. Beware: this only works
189 : : * because [func@GLib.shell_parse_argv] is not a real Unix shell. Quoting untrusted
190 : : * input is not an adequate security mechanism when using a real shell.
191 : : *
192 : : * The return value must be freed with g_free().
193 : : *
194 : : * The quoting style used is undefined (single or double quotes may be
195 : : * used).
196 : : *
197 : : * Returns: (type filename) (transfer full): quoted string
198 : : **/
199 : : gchar*
200 : 195 : g_shell_quote (const gchar *unquoted_string)
201 : : {
202 : : /* We always use single quotes, because the algorithm is cheesier.
203 : : * We could use double if we felt like it, that might be more
204 : : * human-readable.
205 : : */
206 : :
207 : : const gchar *p;
208 : : GString *dest;
209 : :
210 : 195 : g_return_val_if_fail (unquoted_string != NULL, NULL);
211 : :
212 : 195 : dest = g_string_new ("'");
213 : :
214 : 195 : p = unquoted_string;
215 : :
216 : : /* could speed this up a lot by appending chunks of text at a
217 : : * time.
218 : : */
219 : 8906 : while (*p)
220 : : {
221 : : /* Replace literal ' with a close ', a \', and an open ' */
222 : 8711 : if (*p == '\'')
223 : 12 : g_string_append (dest, "'\\''");
224 : : else
225 : 8703 : g_string_append_c (dest, *p);
226 : :
227 : 8711 : ++p;
228 : : }
229 : :
230 : : /* close the quote */
231 : 33 : g_string_append_c (dest, '\'');
232 : :
233 : 195 : return g_string_free (dest, FALSE);
234 : 33 : }
235 : :
236 : : /**
237 : : * g_shell_unquote:
238 : : * @quoted_string: (type filename): shell-quoted string
239 : : * @error: error return location or NULL
240 : : *
241 : : * Unquotes a string as the shell (/bin/sh) would.
242 : : *
243 : : * This function only handles quotes; if a string contains file globs,
244 : : * arithmetic operators, variables, backticks, redirections, or other
245 : : * special-to-the-shell features, the result will be different from the
246 : : * result a real shell would produce (the variables, backticks, etc.
247 : : * will be passed through literally instead of being expanded).
248 : : *
249 : : * This function is guaranteed to succeed if applied to the result of
250 : : * g_shell_quote(). If it fails, it returns %NULL and sets the
251 : : * error.
252 : : *
253 : : * The @quoted_string need not actually contain quoted or escaped text;
254 : : * g_shell_unquote() simply goes through the string and unquotes/unescapes
255 : : * anything that the shell would. Both single and double quotes are
256 : : * handled, as are escapes including escaped newlines.
257 : : *
258 : : * The return value must be freed with g_free().
259 : : *
260 : : * Possible errors are in the %G_SHELL_ERROR domain.
261 : : *
262 : : * Shell quoting rules are a bit strange. Single quotes preserve the
263 : : * literal string exactly. escape sequences are not allowed; not even
264 : : * `\'` - if you want a `'` in the quoted text, you have to do something
265 : : * like `'foo'\''bar'`. Double quotes allow `$`, ```, `"`, `\`, and
266 : : * newline to be escaped with backslash. Otherwise double quotes
267 : : * preserve things literally.
268 : : *
269 : : * Returns: (type filename): an unquoted string
270 : : **/
271 : : gchar*
272 : 1448 : g_shell_unquote (const gchar *quoted_string,
273 : : GError **error)
274 : : {
275 : : gchar *unquoted;
276 : : gchar *end;
277 : : gchar *start;
278 : : GString *retval;
279 : :
280 : 1448 : g_return_val_if_fail (quoted_string != NULL, NULL);
281 : :
282 : 1448 : unquoted = g_strdup (quoted_string);
283 : :
284 : 1448 : start = unquoted;
285 : 1448 : end = unquoted;
286 : 1448 : retval = g_string_new (NULL);
287 : :
288 : : /* The loop allows cases such as
289 : : * "foo"blah blah'bar'woo foo"baz"la la la\'\''foo'
290 : : */
291 : 4262 : while (*start)
292 : : {
293 : : /* Append all non-quoted chars, honoring backslash escape
294 : : */
295 : :
296 : 13885 : while (*start && !(*start == '"' || *start == '\''))
297 : : {
298 : 12413 : if (*start == '\\')
299 : : {
300 : : /* all characters can get escaped by backslash,
301 : : * except newline, which is removed if it follows
302 : : * a backslash outside of quotes
303 : : */
304 : :
305 : 22 : ++start;
306 : 22 : if (*start)
307 : : {
308 : 22 : if (*start != '\n')
309 : 20 : g_string_append_c (retval, *start);
310 : 22 : ++start;
311 : 11 : }
312 : 11 : }
313 : : else
314 : : {
315 : 12391 : g_string_append_c (retval, *start);
316 : 12391 : ++start;
317 : : }
318 : : }
319 : :
320 : 1472 : if (*start)
321 : : {
322 : 292 : if (!unquote_string_inplace (start, &end, error))
323 : : {
324 : 6 : goto error;
325 : : }
326 : : else
327 : : {
328 : 55 : g_string_append (retval, start);
329 : 286 : start = end;
330 : : }
331 : 55 : }
332 : : }
333 : :
334 : 1442 : g_free (unquoted);
335 : 1442 : return g_string_free (retval, FALSE);
336 : :
337 : 3 : error:
338 : 6 : g_assert (error == NULL || *error != NULL);
339 : :
340 : 6 : g_free (unquoted);
341 : 6 : g_string_free (retval, TRUE);
342 : 6 : return NULL;
343 : 100 : }
344 : :
345 : : /* g_parse_argv() does a semi-arbitrary weird subset of the way
346 : : * the shell parses a command line. We don't do variable expansion,
347 : : * don't understand that operators are tokens, don't do tilde expansion,
348 : : * don't do command substitution, no arithmetic expansion, IFS gets ignored,
349 : : * don't do filename globs, don't remove redirection stuff, etc.
350 : : *
351 : : * READ THE UNIX98 SPEC on "Shell Command Language" before changing
352 : : * the behavior of this code.
353 : : *
354 : : * Steps to parsing the argv string:
355 : : *
356 : : * - tokenize the string (but since we ignore operators,
357 : : * our tokenization may diverge from what the shell would do)
358 : : * note that tokenization ignores the internals of a quoted
359 : : * word and it always splits on spaces, not on IFS even
360 : : * if we used IFS. We also ignore "end of input indicator"
361 : : * (I guess this is control-D?)
362 : : *
363 : : * Tokenization steps, from UNIX98 with operator stuff removed,
364 : : * are:
365 : : *
366 : : * 1) "If the current character is backslash, single-quote or
367 : : * double-quote (\, ' or ") and it is not quoted, it will affect
368 : : * quoting for subsequent characters up to the end of the quoted
369 : : * text. The rules for quoting are as described in Quoting
370 : : * . During token recognition no substitutions will be actually
371 : : * performed, and the result token will contain exactly the
372 : : * characters that appear in the input (except for newline
373 : : * character joining), unmodified, including any embedded or
374 : : * enclosing quotes or substitution operators, between the quote
375 : : * mark and the end of the quoted text. The token will not be
376 : : * delimited by the end of the quoted field."
377 : : *
378 : : * 2) "If the current character is an unquoted newline character,
379 : : * the current token will be delimited."
380 : : *
381 : : * 3) "If the current character is an unquoted blank character, any
382 : : * token containing the previous character is delimited and the
383 : : * current character will be discarded."
384 : : *
385 : : * 4) "If the previous character was part of a word, the current
386 : : * character will be appended to that word."
387 : : *
388 : : * 5) "If the current character is a "#", it and all subsequent
389 : : * characters up to, but excluding, the next newline character
390 : : * will be discarded as a comment. The newline character that
391 : : * ends the line is not considered part of the comment. The
392 : : * "#" starts a comment only when it is at the beginning of a
393 : : * token. Since the search for the end-of-comment does not
394 : : * consider an escaped newline character specially, a comment
395 : : * cannot be continued to the next line."
396 : : *
397 : : * 6) "The current character will be used as the start of a new word."
398 : : *
399 : : *
400 : : * - for each token (word), perform portions of word expansion, namely
401 : : * field splitting (using default whitespace IFS) and quote
402 : : * removal. Field splitting may increase the number of words.
403 : : * Quote removal does not increase the number of words.
404 : : *
405 : : * "If the complete expansion appropriate for a word results in an
406 : : * empty field, that empty field will be deleted from the list of
407 : : * fields that form the completely expanded command, unless the
408 : : * original word contained single-quote or double-quote characters."
409 : : * - UNIX98 spec
410 : : *
411 : : *
412 : : */
413 : :
414 : : static inline void
415 : 21584 : ensure_token (GString **token)
416 : : {
417 : 21584 : if (*token == NULL)
418 : 1392 : *token = g_string_new (NULL);
419 : 21584 : }
420 : :
421 : : static void
422 : 1414 : delimit_token (GString **token,
423 : : GSList **retval)
424 : : {
425 : 1414 : if (*token == NULL)
426 : 22 : return;
427 : :
428 : 1392 : *retval = g_slist_prepend (*retval, g_string_free (*token, FALSE));
429 : :
430 : 1392 : *token = NULL;
431 : 82 : }
432 : :
433 : : static GSList*
434 : 529 : tokenize_command_line (const gchar *command_line,
435 : : GError **error)
436 : : {
437 : : gchar current_quote;
438 : : const gchar *p;
439 : 529 : GString *current_token = NULL;
440 : 529 : GSList *retval = NULL;
441 : : gboolean quoted;
442 : :
443 : 529 : current_quote = '\0';
444 : 529 : quoted = FALSE;
445 : 529 : p = command_line;
446 : :
447 : 23078 : while (*p)
448 : : {
449 : 22553 : if (current_quote == '\\')
450 : : {
451 : 12 : if (*p == '\n')
452 : : {
453 : : /* we append nothing; backslash-newline become nothing */
454 : 1 : }
455 : : else
456 : : {
457 : : /* we append the backslash and the current char,
458 : : * to be interpreted later after tokenization
459 : : */
460 : 10 : ensure_token (¤t_token);
461 : 10 : g_string_append_c (current_token, '\\');
462 : 10 : g_string_append_c (current_token, *p);
463 : : }
464 : :
465 : 12 : current_quote = '\0';
466 : 6 : }
467 : 22541 : else if (current_quote == '#')
468 : : {
469 : : /* Discard up to and including next newline */
470 : 52 : while (*p && *p != '\n')
471 : 46 : ++p;
472 : :
473 : 6 : current_quote = '\0';
474 : :
475 : 6 : if (*p == '\0')
476 : 4 : break;
477 : 1 : }
478 : 22535 : else if (current_quote)
479 : : {
480 : 8942 : if (*p == current_quote &&
481 : : /* check that it isn't an escaped double quote */
482 : 34 : !(current_quote == '"' && quoted))
483 : : {
484 : : /* close the quote */
485 : 222 : current_quote = '\0';
486 : 26 : }
487 : :
488 : : /* Everything inside quotes, and the close quote,
489 : : * gets appended literally.
490 : : */
491 : :
492 : 8935 : ensure_token (¤t_token);
493 : 8935 : g_string_append_c (current_token, *p);
494 : 651 : }
495 : : else
496 : : {
497 : 13600 : switch (*p)
498 : : {
499 : 0 : case '\n':
500 : 0 : delimit_token (¤t_token, &retval);
501 : 0 : break;
502 : :
503 : 880 : case ' ':
504 : : case '\t':
505 : : /* If the current token contains the previous char, delimit
506 : : * the current token. A nonzero length
507 : : * token should always contain the previous char.
508 : : */
509 : 939 : if (current_token &&
510 : 885 : current_token->len > 0)
511 : : {
512 : 885 : delimit_token (¤t_token, &retval);
513 : 49 : }
514 : :
515 : : /* discard all unquoted blanks (don't add them to a token) */
516 : 939 : break;
517 : :
518 : :
519 : : /* single/double quotes are appended to the token,
520 : : * escapes are maybe appended next time through the loop,
521 : : * comment chars are never appended.
522 : : */
523 : :
524 : 198 : case '\'':
525 : : case '"':
526 : 226 : ensure_token (¤t_token);
527 : 226 : g_string_append_c (current_token, *p);
528 : :
529 : : G_GNUC_FALLTHROUGH;
530 : 205 : case '\\':
531 : 240 : current_quote = *p;
532 : 240 : break;
533 : :
534 : 5 : case '#':
535 : 10 : if (p == command_line)
536 : : { /* '#' was the first char */
537 : 6 : current_quote = *p;
538 : 6 : break;
539 : : }
540 : 4 : switch(*(p-1))
541 : : {
542 : 1 : case ' ':
543 : : case '\n':
544 : : case '\0':
545 : 2 : current_quote = *p;
546 : 2 : break;
547 : 1 : default:
548 : 2 : ensure_token (¤t_token);
549 : 2 : g_string_append_c (current_token, *p);
550 : 2 : break;
551 : : }
552 : 4 : break;
553 : :
554 : 12156 : default:
555 : : /* Combines rules 4) and 6) - if we have a token, append to it,
556 : : * otherwise create a new token.
557 : : */
558 : 12411 : ensure_token (¤t_token);
559 : 12411 : g_string_append_c (current_token, *p);
560 : 12411 : break;
561 : : }
562 : : }
563 : :
564 : : /* We need to count consecutive backslashes mod 2,
565 : : * to detect escaped doublequotes.
566 : : */
567 : 22549 : if (*p != '\\')
568 : 22510 : quoted = FALSE;
569 : : else
570 : 39 : quoted = !quoted;
571 : :
572 : 22549 : ++p;
573 : : }
574 : :
575 : 529 : delimit_token (¤t_token, &retval);
576 : :
577 : 529 : if (current_quote)
578 : : {
579 : 8 : if (current_quote == '\\')
580 : 3 : g_set_error (error,
581 : 1 : G_SHELL_ERROR,
582 : : G_SHELL_ERROR_BAD_QUOTING,
583 : 1 : _("Text ended just after a “\\” character."
584 : : " (The text was “%s”)"),
585 : 1 : command_line);
586 : 6 : else if (current_quote == '#')
587 : 3 : g_set_error (error,
588 : 1 : G_SHELL_ERROR,
589 : : G_SHELL_ERROR_EMPTY_STRING,
590 : 1 : _("Text was empty (or contained only whitespace)"));
591 : : else
592 : 6 : g_set_error (error,
593 : 2 : G_SHELL_ERROR,
594 : : G_SHELL_ERROR_BAD_QUOTING,
595 : 2 : _("Text ended before matching quote was found for %c."
596 : : " (The text was “%s”)"),
597 : 2 : current_quote, command_line);
598 : :
599 : 8 : goto error;
600 : : }
601 : :
602 : 521 : if (retval == NULL)
603 : : {
604 : 12 : g_set_error_literal (error,
605 : 4 : G_SHELL_ERROR,
606 : : G_SHELL_ERROR_EMPTY_STRING,
607 : 4 : _("Text was empty (or contained only whitespace)"));
608 : :
609 : 8 : goto error;
610 : : }
611 : :
612 : : /* we appended backward */
613 : 513 : retval = g_slist_reverse (retval);
614 : :
615 : 513 : return retval;
616 : :
617 : 8 : error:
618 : 16 : g_assert (error == NULL || *error != NULL);
619 : :
620 : 16 : g_slist_free_full (retval, g_free);
621 : :
622 : 16 : return NULL;
623 : 33 : }
624 : :
625 : : /**
626 : : * g_shell_parse_argv:
627 : : * @command_line: (type filename): command line to parse
628 : : * @argcp: (out) (optional): return location for number of args
629 : : * @argvp: (out) (optional) (array length=argcp zero-terminated=1) (element-type filename):
630 : : * return location for array of args
631 : : * @error: (optional): return location for error
632 : : *
633 : : * Parses a command line into an argument vector, in much the same way
634 : : * the shell would, but without many of the expansions the shell would
635 : : * perform (variable expansion, globs, operators, filename expansion,
636 : : * etc. are not supported).
637 : : *
638 : : * The results are defined to be the same as those you would get from
639 : : * a UNIX98 `/bin/sh`, as long as the input contains none of the
640 : : * unsupported shell expansions. If the input does contain such expansions,
641 : : * they are passed through literally.
642 : : *
643 : : * Possible errors are those from the %G_SHELL_ERROR domain.
644 : : *
645 : : * In particular, if @command_line is an empty string (or a string containing
646 : : * only whitespace), %G_SHELL_ERROR_EMPTY_STRING will be returned. It’s
647 : : * guaranteed that @argvp will be a non-empty array if this function returns
648 : : * successfully.
649 : : *
650 : : * When constructing @command_line, quote any filenames or potentially
651 : : * untrusted input using [func@GLib.shell_quote].
652 : : *
653 : : * Free the returned vector with g_strfreev().
654 : : *
655 : : * Returns: %TRUE on success, %FALSE if error set
656 : : **/
657 : : gboolean
658 : 529 : g_shell_parse_argv (const gchar *command_line,
659 : : gint *argcp,
660 : : gchar ***argvp,
661 : : GError **error)
662 : : {
663 : : /* Code based on poptParseArgvString() from libpopt */
664 : 529 : gint argc = 0;
665 : 529 : gchar **argv = NULL;
666 : 529 : GSList *tokens = NULL;
667 : : gint i;
668 : : GSList *tmp_list;
669 : :
670 : 529 : g_return_val_if_fail (command_line != NULL, FALSE);
671 : :
672 : 529 : tokens = tokenize_command_line (command_line, error);
673 : 529 : if (tokens == NULL)
674 : 16 : return FALSE;
675 : :
676 : : /* Because we can't have introduced any new blank space into the
677 : : * tokens (we didn't do any new expansions), we don't need to
678 : : * perform field splitting. If we were going to honor IFS or do any
679 : : * expansions, we would have to do field splitting on each word
680 : : * here. Also, if we were going to do any expansion we would need to
681 : : * remove any zero-length words that didn't contain quotes
682 : : * originally; but since there's no expansion we know all words have
683 : : * nonzero length, unless they contain quotes.
684 : : *
685 : : * So, we simply remove quotes, and don't do any field splitting or
686 : : * empty word removal, since we know there was no way to introduce
687 : : * such things.
688 : : */
689 : :
690 : 513 : argc = g_slist_length (tokens);
691 : 513 : argv = g_new0 (gchar*, argc + 1);
692 : 513 : i = 0;
693 : 513 : tmp_list = tokens;
694 : 1893 : while (tmp_list)
695 : : {
696 : 1380 : argv[i] = g_shell_unquote (tmp_list->data, error);
697 : :
698 : : /* Since we already checked that quotes matched up in the
699 : : * tokenizer, this shouldn't be possible to reach I guess.
700 : : */
701 : 1380 : if (argv[i] == NULL)
702 : 0 : goto failed;
703 : :
704 : 1380 : tmp_list = g_slist_next (tmp_list);
705 : 1380 : ++i;
706 : : }
707 : :
708 : 513 : g_slist_free_full (tokens, g_free);
709 : :
710 : 513 : g_assert (argc > 0);
711 : 513 : g_assert (argv != NULL && argv[0] != NULL);
712 : :
713 : 513 : if (argcp)
714 : 402 : *argcp = argc;
715 : :
716 : 513 : if (argvp)
717 : 513 : *argvp = argv;
718 : : else
719 : 0 : g_strfreev (argv);
720 : :
721 : 513 : return TRUE;
722 : :
723 : 0 : failed:
724 : :
725 : 0 : g_assert (error == NULL || *error != NULL);
726 : 0 : g_strfreev (argv);
727 : 0 : g_slist_free_full (tokens, g_free);
728 : :
729 : 0 : return FALSE;
730 : 33 : }
|