static void script (int prompt);
static void cmdline (char *argv[], int optind, int argc);
static struct parsed_command parse_command_line (char *buf, int *exit_on_error_rtn);
+static int parse_quoted_string (char *p);
static int execute_and_inline (const char *cmd, int exit_on_error);
static void initialize_readline (void);
static void cleanup_readline (void);
*/
if (*p == '"') {
p++;
- len = strcspn (p, "\"");
- if (p[len] == '\0') {
- fprintf (stderr, _("%s: unterminated double quote\n"), program_name);
+ len = parse_quoted_string (p);
+ if (len == -1) {
pcmd.status = -1;
return pcmd;
}
pcmd.status = -1;
return pcmd;
}
- p[len] = '\0';
pend = p[len+1] ? &p[len+2] : &p[len+1];
} else if (*p == '\'') {
p++;
return pcmd;
}
+static int
+hexdigit (char d)
+{
+ switch (d) {
+ case '0'...'9': return d - '0';
+ case 'a'...'f': return d - 'a' + 10;
+ case 'A'...'F': return d - 'A' + 10;
+ default: return -1;
+ }
+}
+
+/* Parse double-quoted strings, replacing backslash escape sequences
+ * with the true character. Since the string is returned in place,
+ * the escapes must make the string shorter.
+ */
+static int
+parse_quoted_string (char *p)
+{
+ char *start = p;
+
+ for (; *p && *p != '"'; p++) {
+ if (*p == '\\') {
+ int m = 1, c;
+
+ switch (p[1]) {
+ case '\\': break;
+ case 'a': *p = '\a'; break;
+ case 'b': *p = '\b'; break;
+ case 'f': *p = '\f'; break;
+ case 'n': *p = '\n'; break;
+ case 'r': *p = '\r'; break;
+ case 't': *p = '\t'; break;
+ case 'v': *p = '\v'; break;
+ case '"': *p = '"'; break;
+ case '\'': *p = '\''; break;
+ case '?': *p = '?'; break;
+
+ case '0'...'7': /* octal escape - always 3 digits */
+ m = 3;
+ if (p[2] >= '0' && p[2] <= '7' &&
+ p[3] >= '0' && p[3] <= '7') {
+ c = (p[1] - '0') * 0100 + (p[2] - '0') * 010 + (p[3] - '0');
+ if (c < 1 || c > 255)
+ goto error;
+ *p = c;
+ }
+ else
+ goto error;
+ break;
+
+ case 'x': /* hex escape - always 2 digits */
+ m = 3;
+ if (c_isxdigit (p[2]) && c_isxdigit (p[3])) {
+ c = hexdigit (p[2]) * 0x10 + hexdigit (p[3]);
+ if (c < 1 || c > 255)
+ goto error;
+ *p = c;
+ }
+ else
+ goto error;
+ break;
+
+ default:
+ error:
+ fprintf (stderr, _("%s: invalid escape sequence in string (starting at offset %d)\n"),
+ program_name, (int) (p - start));
+ return -1;
+ }
+ memmove (p+1, p+1+m, strlen (p+1+m) + 1);
+ }
+ }
+
+ if (!*p) {
+ fprintf (stderr, _("%s: unterminated double quote\n"), program_name);
+ return -1;
+ }
+
+ *p = '\0';
+ return p - start;
+}
+
/* Used to handle "<!" (execute command and inline result). */
static int
execute_and_inline (const char *cmd, int global_exit_on_error)
command "/bin/echo 'foo bar'"
command "/bin/echo \'foo\'"
+=head2 ESCAPE SEQUENCES IN DOUBLE QUOTED ARGUMENTS
+
+In double-quoted arguments (only) use backslash to insert special
+characters:
+
+=over 4
+
+=item C<\a>
+
+Alert (bell) character.
+
+=item C<\b>
+
+Backspace character.
+
+=item C<\f>
+
+Form feed character.
+
+=item C<\n>
+
+Newline character.
+
+=item C<\r>
+
+Carriage return character.
+
+=item C<\t>
+
+Horizontal tab character.
+
+=item C<\v>
+
+Vertical tab character.
+
+=item C<\">
+
+A literal double quote character.
+
+=item C<\ooo>
+
+A character with octal value I<ooo>. There must be precisely 3 octal
+digits (unlike C).
+
+=item C<\xhh>
+
+A character with hex value I<hh>. There must be precisely 2 hex
+digits.
+
+In the current implementation C<\000> and C<\x00> cannot be used
+in strings.
+
+=item C<\\>
+
+A literal backslash character.
+
+=back
+
=head1 OPTIONAL ARGUMENTS
Some commands take optional arguments. These arguments appear in this
test-find0.sh \
test-guestfish-a.sh \
test-guestfish-d.sh \
+ test-guestfish-escapes.sh \
test-guestfish-tilde.sh \
test-inspect-fstab.sh \
test-launch-race.pl \
--- /dev/null
+#!/bin/bash -
+# libguestfs
+# Copyright (C) 2011 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Test guestfish string escapes.
+
+set -e
+
+rm -f test.output test.error
+
+../fish/guestfish <<'EOF' 2>test.error | od > test.output
+echo ""
+echo " "
+echo " "
+echo "\n"
+echo "\r"
+echo "\n\n"
+echo "\x01"
+echo "\001"
+echo "\100"
+
+# These are invalid:
+-echo "\x00"
+-echo "\000"
+-echo "\x"
+-echo "\x0"
+-echo "\7"
+-echo "\77"
+-echo "\777"
+-echo "\"
+-echo "\\\"
+-echo "
+-echo """
+EOF
+
+if [ "$(cat test.error)" != "\
+guestfish: invalid escape sequence in string (starting at offset 0)
+guestfish: invalid escape sequence in string (starting at offset 0)
+guestfish: invalid escape sequence in string (starting at offset 0)
+guestfish: invalid escape sequence in string (starting at offset 0)
+guestfish: invalid escape sequence in string (starting at offset 0)
+guestfish: invalid escape sequence in string (starting at offset 0)
+guestfish: invalid escape sequence in string (starting at offset 0)
+guestfish: unterminated double quote
+guestfish: unterminated double quote
+guestfish: unterminated double quote
+guestfish: command arguments not separated by whitespace" ]; then
+ echo "unexpected stderr from guestfish:"
+ cat test.error
+ echo "[end of stderr]"
+ exit 1
+fi
+
+if [ "$(cat test.output)" != "\
+0000000 020012 020012 005040 005012 005015 005012 000412 000412
+0000020 040012 000012
+0000023" ]; then
+ echo "unexpected stdout from guestfish:"
+ cat test.output
+ echo "[end of stdout]"
+ exit 1
+fi
+
+rm -f test.output test.error