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