hook gvariant vectors up to kdbus
[platform/upstream/glib.git] / glib / gshell.c
index 399aec4..a1c7e79 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
-#include "glib.h"
+#include "config.h"
+
 #include <string.h>
 
-#ifdef _
-#warning "FIXME remove gettext hack"
-#endif
+#include "gshell.h"
 
+#include "gslist.h"
+#include "gstrfuncs.h"
+#include "gstring.h"
+#include "gtestutils.h"
 #include "glibintl.h"
+#include "gthread.h"
 
-GQuark
-g_shell_error_quark (void)
-{
-  static GQuark quark = 0;
-  if (quark == 0)
-    quark = g_quark_from_static_string ("g-shell-error-quark");
-  return quark;
-}
+/**
+ * SECTION:shell
+ * @title: Shell-related Utilities
+ * @short_description: shell-like commandline handling
+ *
+ * GLib provides the functions g_shell_quote() and g_shell_unquote()
+ * to handle shell-like quoting in strings. The function g_shell_parse_argv()
+ * parses a string similar to the way a POSIX shell (/bin/sh) would.
+ *
+ * Note that string handling in shells has many obscure and historical
+ * corner-cases which these functions do not necessarily reproduce. They
+ * are good enough in practice, though.
+ */
+
+/**
+ * G_SHELL_ERROR:
+ *
+ * Error domain for shell functions. Errors in this domain will be from
+ * the #GShellError enumeration. See #GError for information on error
+ * domains.
+ **/
+
+/**
+ * GShellError:
+ * @G_SHELL_ERROR_BAD_QUOTING: Mismatched or otherwise mangled quoting.
+ * @G_SHELL_ERROR_EMPTY_STRING: String to be parsed was empty.
+ * @G_SHELL_ERROR_FAILED: Some other error.
+ *
+ * Error codes returned by shell functions.
+ **/
+G_DEFINE_QUARK (g-shell-error-quark, g_shell_error)
 
 /* Single quotes preserve the literal string exactly. escape
  * sequences are not allowed; not even \' - if you want a '
@@ -63,8 +90,8 @@ unquote_string_inplace (gchar* str, gchar** end, GError** err)
   
   if (!(*s == '"' || *s == '\''))
     {
-      if (err)
-        *err = g_error_new(G_SHELL_ERROR,
+      g_set_error_literal (err,
+                           G_SHELL_ERROR,
                            G_SHELL_ERROR_BAD_QUOTING,
                            _("Quoted text doesn't begin with a quotation mark"));
       *end = str;
@@ -153,8 +180,8 @@ unquote_string_inplace (gchar* str, gchar** end, GError** err)
 
   *dest = '\0';
   
-  if (err)
-    *err = g_error_new(G_SHELL_ERROR,
+  g_set_error_literal (err,
+                       G_SHELL_ERROR,
                        G_SHELL_ERROR_BAD_QUOTING,
                        _("Unmatched quotation mark in command line or other shell-quoted text"));
   *end = s;
@@ -172,7 +199,7 @@ unquote_string_inplace (gchar* str, gchar** end, GError** err)
  * quoting style used is undefined (single or double quotes may be
  * used).
  * 
- * Return value: quoted string
+ * Returns: quoted string
  **/
 gchar*
 g_shell_quote (const gchar *unquoted_string)
@@ -238,7 +265,7 @@ g_shell_quote (const gchar *unquoted_string)
  * be escaped with backslash. Otherwise double quotes preserve things
  * literally.
  *
- * Return value: an unquoted string
+ * Returns: an unquoted string
  **/
 gchar*
 g_shell_unquote (const gchar *quoted_string,
@@ -255,7 +282,7 @@ g_shell_unquote (const gchar *quoted_string,
 
   start = unquoted;
   end = unquoted;
-  retval = g_string_new ("");
+  retval = g_string_new (NULL);
 
   /* The loop allows cases such as
    * "foo"blah blah'bar'woo foo"baz"la la la\'\''foo'
@@ -303,6 +330,7 @@ g_shell_unquote (const gchar *quoted_string,
         }
     }
 
+  g_free (unquoted);
   return g_string_free (retval, FALSE);
   
  error:
@@ -386,7 +414,7 @@ static inline void
 ensure_token (GString **token)
 {
   if (*token == NULL)
-    *token = g_string_new ("");
+    *token = g_string_new (NULL);
 }
 
 static void
@@ -409,10 +437,12 @@ tokenize_command_line (const gchar *command_line,
   const gchar *p;
   GString *current_token = NULL;
   GSList *retval = NULL;
-  
+  gboolean quoted;
+
   current_quote = '\0';
+  quoted = FALSE;
   p = command_line;
-
   while (*p)
     {
       if (current_quote == '\\')
@@ -448,7 +478,7 @@ tokenize_command_line (const gchar *command_line,
         {
           if (*p == current_quote &&
               /* check that it isn't an escaped double quote */
-              !(current_quote == '"' && p != command_line && *(p - 1) == '\\'))
+              !(current_quote == '"' && quoted))
             {
               /* close the quote */
               current_quote = '\0';
@@ -496,12 +526,30 @@ tokenize_command_line (const gchar *command_line,
               g_string_append_c (current_token, *p);
 
               /* FALL THRU */
-              
-            case '#':
             case '\\':
               current_quote = *p;
               break;
 
+            case '#':
+              if (p == command_line)
+               { /* '#' was the first char */
+                  current_quote = *p;
+                  break;
+                }
+              switch(*(p-1))
+                {
+                  case ' ':
+                  case '\n':
+                  case '\0':
+                    current_quote = *p;
+                    break;
+                  default:
+                    ensure_token (&current_token);
+                    g_string_append_c (current_token, *p);
+                   break;
+                }
+              break;
+
             default:
               /* Combines rules 4) and 6) - if we have a token, append to it,
                * otherwise create a new token.
@@ -512,6 +560,14 @@ tokenize_command_line (const gchar *command_line,
             }
         }
 
+      /* We need to count consecutive backslashes mod 2, 
+       * to detect escaped doublequotes.
+       */
+      if (*p != '\\')
+       quoted = FALSE;
+      else
+       quoted = !quoted;
+
       ++p;
     }
 
@@ -523,7 +579,7 @@ tokenize_command_line (const gchar *command_line,
         g_set_error (error,
                      G_SHELL_ERROR,
                      G_SHELL_ERROR_BAD_QUOTING,
-                     _("Text ended just after a '\' character."
+                     _("Text ended just after a '\\' character."
                        " (The text was '%s')"),
                      command_line);
       else
@@ -539,10 +595,10 @@ tokenize_command_line (const gchar *command_line,
 
   if (retval == NULL)
     {
-      g_set_error (error,
-                   G_SHELL_ERROR,
-                   G_SHELL_ERROR_EMPTY_STRING,
-                   _("Text was empty (or contained only whitespace)"));
+      g_set_error_literal (error,
+                           G_SHELL_ERROR,
+                           G_SHELL_ERROR_EMPTY_STRING,
+                           _("Text was empty (or contained only whitespace)"));
 
       goto error;
     }
@@ -554,12 +610,8 @@ tokenize_command_line (const gchar *command_line,
 
  error:
   g_assert (error == NULL || *error != NULL);
-  
-  if (retval)
-    {
-      g_slist_foreach (retval, (GFunc)g_free, NULL);
-      g_slist_free (retval);
-    }
+
+  g_slist_free_full (retval, g_free);
 
   return NULL;
 }
@@ -567,9 +619,10 @@ tokenize_command_line (const gchar *command_line,
 /**
  * g_shell_parse_argv:
  * @command_line: command line to parse
- * @argcp: return location for number of args
- * @argvp: return location for array of args
- * @error: return location for error
+ * @argcp: (out) (optional): return location for number of args, or %NULL
+ * @argvp: (out) (optional) (array length=argcp zero-terminated=1): return
+ *   location for array of args, or %NULL
+ * @error: (optional): return location for error, or %NULL
  * 
  * Parses a command line into an argument vector, in much the same way
  * the shell would, but without many of the expansions the shell would
@@ -581,7 +634,7 @@ tokenize_command_line (const gchar *command_line,
  * literally. Possible errors are those from the #G_SHELL_ERROR
  * domain. Free the returned vector with g_strfreev().
  * 
- * Return value: %TRUE on success, %FALSE if error set
+ * Returns: %TRUE on success, %FALSE if error set
  **/
 gboolean
 g_shell_parse_argv (const gchar *command_line,
@@ -634,8 +687,7 @@ g_shell_parse_argv (const gchar *command_line,
       ++i;
     }
   
-  g_slist_foreach (tokens, (GFunc)g_free, NULL);
-  g_slist_free (tokens);
+  g_slist_free_full (tokens, g_free);
   
   if (argcp)
     *argcp = argc;
@@ -651,8 +703,7 @@ g_shell_parse_argv (const gchar *command_line,
 
   g_assert (error == NULL || *error != NULL);
   g_strfreev (argv);
-  g_slist_foreach (tokens, (GFunc) g_free, NULL);
-  g_slist_free (tokens);
+  g_slist_free_full (tokens, g_free);
   
   return FALSE;
 }