glib: don't quote quark names for G_DEFINE_QUARK
[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               
522             case '#':
523             case '\\':
524               current_quote = *p;
525               break;
526
527             default:
528               /* Combines rules 4) and 6) - if we have a token, append to it,
529                * otherwise create a new token.
530                */
531               ensure_token (&current_token);
532               g_string_append_c (current_token, *p);
533               break;
534             }
535         }
536
537       /* We need to count consecutive backslashes mod 2, 
538        * to detect escaped doublequotes.
539        */
540       if (*p != '\\')
541         quoted = FALSE;
542       else
543         quoted = !quoted;
544
545       ++p;
546     }
547
548   delimit_token (&current_token, &retval);
549
550   if (current_quote)
551     {
552       if (current_quote == '\\')
553         g_set_error (error,
554                      G_SHELL_ERROR,
555                      G_SHELL_ERROR_BAD_QUOTING,
556                      _("Text ended just after a '\\' character."
557                        " (The text was '%s')"),
558                      command_line);
559       else
560         g_set_error (error,
561                      G_SHELL_ERROR,
562                      G_SHELL_ERROR_BAD_QUOTING,
563                      _("Text ended before matching quote was found for %c."
564                        " (The text was '%s')"),
565                      current_quote, command_line);
566       
567       goto error;
568     }
569
570   if (retval == NULL)
571     {
572       g_set_error_literal (error,
573                            G_SHELL_ERROR,
574                            G_SHELL_ERROR_EMPTY_STRING,
575                            _("Text was empty (or contained only whitespace)"));
576
577       goto error;
578     }
579   
580   /* we appended backward */
581   retval = g_slist_reverse (retval);
582
583   return retval;
584
585  error:
586   g_assert (error == NULL || *error != NULL);
587
588   g_slist_free_full (retval, g_free);
589
590   return NULL;
591 }
592
593 /**
594  * g_shell_parse_argv:
595  * @command_line: command line to parse
596  * @argcp: (out): return location for number of args
597  * @argvp: (out) (array length=argcp zero-terminated=1): return location for array of args
598  * @error: return location for error
599  * 
600  * Parses a command line into an argument vector, in much the same way
601  * the shell would, but without many of the expansions the shell would
602  * perform (variable expansion, globs, operators, filename expansion,
603  * etc. are not supported). The results are defined to be the same as
604  * those you would get from a UNIX98 /bin/sh, as long as the input
605  * contains none of the unsupported shell expansions. If the input
606  * does contain such expansions, they are passed through
607  * literally. Possible errors are those from the #G_SHELL_ERROR
608  * domain. Free the returned vector with g_strfreev().
609  * 
610  * Return value: %TRUE on success, %FALSE if error set
611  **/
612 gboolean
613 g_shell_parse_argv (const gchar *command_line,
614                     gint        *argcp,
615                     gchar     ***argvp,
616                     GError     **error)
617 {
618   /* Code based on poptParseArgvString() from libpopt */
619   gint argc = 0;
620   gchar **argv = NULL;
621   GSList *tokens = NULL;
622   gint i;
623   GSList *tmp_list;
624   
625   g_return_val_if_fail (command_line != NULL, FALSE);
626
627   tokens = tokenize_command_line (command_line, error);
628   if (tokens == NULL)
629     return FALSE;
630
631   /* Because we can't have introduced any new blank space into the
632    * tokens (we didn't do any new expansions), we don't need to
633    * perform field splitting. If we were going to honor IFS or do any
634    * expansions, we would have to do field splitting on each word
635    * here. Also, if we were going to do any expansion we would need to
636    * remove any zero-length words that didn't contain quotes
637    * originally; but since there's no expansion we know all words have
638    * nonzero length, unless they contain quotes.
639    * 
640    * So, we simply remove quotes, and don't do any field splitting or
641    * empty word removal, since we know there was no way to introduce
642    * such things.
643    */
644
645   argc = g_slist_length (tokens);
646   argv = g_new0 (gchar*, argc + 1);
647   i = 0;
648   tmp_list = tokens;
649   while (tmp_list)
650     {
651       argv[i] = g_shell_unquote (tmp_list->data, error);
652
653       /* Since we already checked that quotes matched up in the
654        * tokenizer, this shouldn't be possible to reach I guess.
655        */
656       if (argv[i] == NULL)
657         goto failed;
658
659       tmp_list = g_slist_next (tmp_list);
660       ++i;
661     }
662   
663   g_slist_free_full (tokens, g_free);
664   
665   if (argcp)
666     *argcp = argc;
667
668   if (argvp)
669     *argvp = argv;
670   else
671     g_strfreev (argv);
672
673   return TRUE;
674
675  failed:
676
677   g_assert (error == NULL || *error != NULL);
678   g_strfreev (argv);
679   g_slist_free_full (tokens, g_free);
680   
681   return FALSE;
682 }