shell quoting
- From: Havoc Pennington <hp redhat com>
- To: gtk-devel-list gnome org
- Subject: shell quoting
- Date: 05 Sep 2000 19:27:20 -0400
Hi,
Code to quote/unquote strings for passing the string to a shell as an
argument. Mostly useful for filenames.
I had to write this for g_exec_*, so if we include g_exec_* this code
is in GLib; we may as well expose it publically.
I haven't tested it yet, I'll submit it as part of the gexec patch
later, this post is just so we can discuss whether to include
shell-style quoting in the public API.
Exposed API is:
gchar* g_shell_quote (const gchar *unquoted_string);
gchar* g_shell_unquote (const gchar *quoted_string,
GError **error);
Havoc
/* Single quotes preserve the literal string exactly. escape
* sequences are not allowed; not even \' - if you want a '
* in the quoted text, you have to do something like 'foo'\''bar'
*
* Double quotes allow $ ` " \ and newline to be escaped with backslash.
* Otherwise double quotes preserve things literally.
*/
gboolean
unquote_string_inplace (gchar* str, gchar** end, GError** err)
{
gchar* dest;
gchar* s;
gchar quote_char;
g_return_val_if_fail(end != NULL, FALSE);
g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
g_return_val_if_fail(str != NULL, FALSE);
dest = s = str;
quote_char = *s;
if (!(*s == '"' || *s == '\''))
{
if (err)
*err = g_error_new(G_EXEC_ERROR,
G_EXEC_ERROR_PARSE,
_("Quoted text doesn't begin with a quotation mark"));
*end = str;
return FALSE;
}
/* Skip the initial quote mark */
++s;
if (quote_char == '"')
{
while (*s)
{
g_assert(s > dest); /* loop invariant */
switch (*s)
{
case '"':
/* End of the string, return now */
*dest = '\0';
++s;
*end = s;
return TRUE;
break;
case '\\':
/* Possible escaped quote or \ */
++s;
switch (*s)
{
case '"':
case '\\':
case '`':
case '$':
*dest = *s;
++s;
++dest;
break;
default:
/* not an escaped char */
*dest = '\\';
++dest;
/* ++s already done. */
break;
}
break;
default:
*dest = *s;
++dest;
++s;
break;
}
g_assert(s > dest); /* loop invariant */
}
}
else
{
while (*s)
{
g_assert(s > dest); /* loop invariant */
if (*s == '\'')
{
/* End of the string, return now */
*dest = '\0';
++s;
*end = s;
return TRUE;
}
else
{
*dest = *s;
++dest;
++s;
}
g_assert(s > dest); /* loop invariant */
}
}
/* If we reach here this means the close quote was never encountered */
*dest = '\0';
if (err)
*err = g_error_new(G_EXEC_ERROR,
G_EXEC_ERROR_PARSE,
_("Unmatched quotation mark in command line"));
*end = s;
return FALSE;
}
gchar*
g_shell_quote (const gchar *unquoted_string)
{
/* We always use single quotes, because the algorithm is cheesier.
* We could use double if we felt like it.
*/
const gchar *p;
GString *dest;
g_return_val_if_fail (unquoted_string != NULL, NULL);
dest = g_string_new ("'");
p = unquoted_string;
/* could speed this up a lot by appending chunks of text at a
* time.
*/
while (*p)
{
/* Replace literal ' with a close ', a \', and a open ' */
if (*p == '\'')
g_string_append (dest, "'\''");
else
g_string_append_c (dest, *p);
++p;
}
/* close the quote */
g_string_append_c (dest, '\'');
return g_string_free (dest, FALSE);
}
gchar*
g_shell_unquote (const gchar *quoted_string,
GError **error)
{
gchar *unquoted;
gchar *end;
g_return_val_if_fail (quoted_string != NULL, NULL);
unquoted = g_strdup (quoted_string);
if (!unquote_string_inplace (unquoted, &end, error))
{
g_free (unquoted);
return NULL;
}
else
return unquoted;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]