Helper functions for future support of backslash escaped spaces in filenames.
There are a few tests too. Changed according to review remarks and fixed few
other mistakes.
Maros Zatko (4):
fish: copy parse_quoted_string and hexdigit from fish.h to rl.c
fish: rl.{c,h} - escaping functions for readline
fish: basic tests for readline escaping
autotools: add fish/test
Makefile.am | 1 +
configure.ac | 1 +
fish/rl.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++
fish/rl.h | 25 ++++++++
fish/test/Makefile.am | 34 +++++++++++
fish/test/testquoting.c | 96 +++++++++++++++++++++++++++++
6 files changed, 314 insertions(+)
create mode 100644 fish/rl.c
create mode 100644 fish/rl.h
create mode 100644 fish/test/Makefile.am
create mode 100644 fish/test/testquoting.c
--
1.9.3
Maros Zatko
2014-Nov-13 15:34 UTC
[Libguestfs] [PATCH 1/4] fish: copy parse_quoted_string and hexdigit from fish.h to rl.c
---
fish/rl.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)
create mode 100644 fish/rl.c
diff --git a/fish/rl.c b/fish/rl.c
new file mode 100644
index 0000000..c98ce8e
--- /dev/null
+++ b/fish/rl.c
@@ -0,0 +1,94 @@
+/* guestfish - guest filesystem shell
+ * Copyright (C) 2009-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA.
+ */
+
+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;
+ }
+}
+
+static ssize_t
+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;
+}
--
1.9.3
Maros Zatko
2014-Nov-13 15:34 UTC
[Libguestfs] [PATCH 2/4] fish: rl.{c, h} - escaping functions for readline
Two auxiliary functions for readline to support space character escaping
in filenames in future.
Escaping function used to be parse_quoted_string and there is its
un-escaping counterpart.
---
fish/rl.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
fish/rl.h | 25 +++++++++++++++++++
2 files changed, 99 insertions(+), 11 deletions(-)
create mode 100644 fish/rl.h
diff --git a/fish/rl.c b/fish/rl.c
index c98ce8e..bdf2eb1 100644
--- a/fish/rl.c
+++ b/fish/rl.c
@@ -16,7 +16,21 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA.
*/
-static int
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gettext.h>
+#include <errno.h>
+
+#include <c-ctype.h>
+
+#include "guestfs.h"
+#include "guestfs-internal-frontend.h"
+#include "rl.h"
+
+int
hexdigit (char d)
{
switch (d) {
@@ -27,12 +41,14 @@ hexdigit (char d)
}
}
-static ssize_t
-parse_quoted_string (char *p)
+/* backslash unescape for readline */
+char *
+bs_unescape_filename (const char *str)
{
- char *start = p;
+ char *p = strdup (str);
+ const char *start = p;
- for (; *p && *p != '"'; p++) {
+ for (; *p; p++) {
if (*p == '\\') {
int m = 1, c;
@@ -48,6 +64,7 @@ parse_quoted_string (char *p)
case '"': *p = '"'; break;
case '\'': *p = '\''; break;
case '?': *p = '?'; break;
+ case ' ': *p = ' '; break;
case '0'...'7': /* octal escape - always 3
digits */
m = 3;
@@ -78,17 +95,63 @@ parse_quoted_string (char *p)
error:
fprintf (stderr, _("%s: invalid escape sequence in string
(starting at offset %d)\n"),
program_name, (int) (p - start));
- return -1;
+ return NULL;
}
memmove (p+1, p+1+m, strlen (p+1+m) + 1);
}
}
- if (!*p) {
- fprintf (stderr, _("%s: unterminated double quote\n"),
program_name);
- return -1;
+ return (char *)start;
+}
+
+/* backslash scape */
+char *
+bs_escape_filename (const char *p)
+{
+ const char *start = p;
+ /* four times original length - if all chars are unprintable
+ * new string would be \xXY\xWZ */
+ char *n = malloc (strlen (p) * 4 + 1);
+ char *nstart = n;
+
+ for (; *p; p++, n++) {
+ int m = 1;
+
+ switch (*p) {
+ case '\\': break;
+ case '\a': *(n++) = '\\'; *n = 'a'; break;
+ case '\b': *(n++) = '\\'; *n = 'b'; break;
+ case '\f': *(n++) = '\\'; *n = 'f'; break;
+ case '\n': *(n++) = '\\'; *n = 'n'; break;
+ case '\r': *(n++) = '\\'; *n = 'r'; break;
+ case '\t': *(n++) = '\\'; *n = 't'; break;
+ case '\v': *(n++) = '\\'; *n = 'v'; break;
+ case '"': *(n++) = '\\'; *n = '"';
break;
+ case '\'': *(n++) = '\\'; *n = '\'';
break;
+ case '?': *(n++) = '\\'; *n = '?'; break;
+ case ' ': *(n++) = '\\'; *n = ' '; break;
+
+ default:
+ /* Hexadecimal escape unprintable character. This violates identity
+ * after composition of bs_escape_filename after bs_unescape_filename
+ * (i.e. can escape some characters differently). */
+ if (!c_isprint (*p)) {
+ int r = sprintf (n, "\\x%x", (int) (*p & 0xff)) - 1;
+ if (r < 0) {
+ return NULL;
+ }
+ n += r;
+ } else {
+ *n = *p;
+ }
+ break;
+ error:
+ fprintf (stderr, _("%s: invalid escape sequence in string
(starting at offset %d)\n"),
+ program_name, (int) (p - start));
+ return NULL;
+ }
}
- *p = '\0';
- return p - start;
+ *n = '\0';
+ return nstart;
}
diff --git a/fish/rl.h b/fish/rl.h
new file mode 100644
index 0000000..8ac8da2
--- /dev/null
+++ b/fish/rl.h
@@ -0,0 +1,25 @@
+/* guestfish - guest filesystem shell
+ * Copyright (C) 2009-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA.
+ */
+
+#ifndef _FISH_RL_H
+#define _FISH_RL_H
+
+extern char * bs_escape_filename (const char *p);
+extern char * bs_unescape_filename (const char *p);
+
+#endif /* !_FISH_RL_H */
--
1.9.3
Maros Zatko
2014-Nov-13 15:34 UTC
[Libguestfs] [PATCH 3/4] fish: basic tests for readline escaping
---
fish/test/Makefile.am | 34 ++++++++++++++++++
fish/test/testquoting.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 130 insertions(+)
create mode 100644 fish/test/Makefile.am
create mode 100644 fish/test/testquoting.c
diff --git a/fish/test/Makefile.am b/fish/test/Makefile.am
new file mode 100644
index 0000000..05e868f
--- /dev/null
+++ b/fish/test/Makefile.am
@@ -0,0 +1,34 @@
+# libguestfs
+# Copyright (C) 2009-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+include $(top_srcdir)/subdir-rules.mk
+
+check_PROGRAMS = testquoting
+
+testquoting_SOURCES = \
+ testquoting.c \
+ $(top_srcdir)/fish/rl.c
+
+testquoting_CPPFLAGS = \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ -I$(top_srcdir)/fish -I$(top_builddir)/fish \
+ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib
+
+TESTS_ENVIRONMENT = $(top_builddir)/run --test
+
+TESTS = \
+ testquoting
diff --git a/fish/test/testquoting.c b/fish/test/testquoting.c
new file mode 100644
index 0000000..9a5e43f
--- /dev/null
+++ b/fish/test/testquoting.c
@@ -0,0 +1,96 @@
+/* guestfish - guest filesystem shell
+ * Copyright (C) 2009-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#include "guestfs.h"
+#include "guestfs-internal-frontend.h"
+#include "../rl.h"
+
+struct string_test_data {
+ const char *in;
+ const char *out;
+ int pass;
+};
+
+struct string_test_data escape_tests[] = {
+ { "", "", 1 },
+ { " ", "\\ ", 1 },
+ { "singleword", "singleword", 1 },
+ { "more than one word\n", "more\\ than\\ one\\ word\\n",
1 },
+ { "more than one word\n", "more\\ than\\ one\\ word\\n",
1 },
+ { "\xac\xec\x8", "\\xac\\xec\\b", 1 },
+};
+
+size_t nr_escape_tests = sizeof (escape_tests) / sizeof (*escape_tests);
+
+struct string_test_data unescape_tests[] = {
+ { "", "", 1 },
+ { "\\ ", " ", 1 },
+ { "singleword", "singleword", 1 },
+ { "more\\ than\\ one\\ word\\n", "more than one word\n",
1 },
+ { "more\\ than\\ one\\ word\\n", "more than one word\n",
1 },
+ { "\\xac\\xec\\b", "\xac\xec\x8", 1 },
+};
+
+size_t nr_unescape_tests = sizeof (unescape_tests) / sizeof (*unescape_tests);
+
+int
+run_with_test_data (char *(*f) (const char *),
+ struct string_test_data *data, size_t len)
+{
+ int i = 0, nr_failed = 0;
+
+ for (; i < len; i++) {
+ char *r = f(data[i].in);
+ if (((r != NULL) && STREQ (r, data[i].out)) != data[i].pass) {
+ printf ("%d ", i);
+ nr_failed ++;
+ }
+ if (r != NULL) {
+ free (r);
+ }
+ }
+ printf ("%s\n", nr_failed == 0 ? "none" : "");
+ return nr_failed;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int nr_failed = 0;
+
+ printf ("Escaping tests failed ids: ");
+ nr_failed += run_with_test_data (
+ bs_escape_filename, escape_tests, nr_escape_tests);
+
+ printf ("Un-escaping tests failed ids: ");
+ nr_failed += run_with_test_data (
+ bs_unescape_filename, unescape_tests, nr_unescape_tests);
+
+ if (nr_failed > 0) {
+ printf ("***** %zu / %zu tests FAILED *****\n", nr_failed,
+ nr_escape_tests + nr_unescape_tests);
+ }
+
+ return nr_failed > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
--
1.9.3
---
Makefile.am | 1 +
configure.ac | 1 +
2 files changed, 2 insertions(+)
diff --git a/Makefile.am b/Makefile.am
index d55d8d6..629b787 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -78,6 +78,7 @@ SUBDIRS += test-tool
# Guestfish.
SUBDIRS += fish
+SUBDIRS += fish/test
# virt-tools in C.
SUBDIRS += align cat diff df edit format inspector make-fs rescue
diff --git a/configure.ac b/configure.ac
index 5fc09c2..0b415c2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1704,6 +1704,7 @@ AC_CONFIG_FILES([Makefile
erlang/examples/Makefile
examples/Makefile
fish/Makefile
+ fish/test/Makefile
format/Makefile
fuse/Makefile
generator/Makefile
--
1.9.3
Maybe Matching Threads
- [PATCH 0/3] v2 readline escaping functions
- [PATCH 0/3] WIP readline escaping functions
- [PATCH 2/3] fish: basic tests for readline escaping
- [PATCH] tests/c-api: Allow the C API tests to run in parallel.
- [PATCH 1/3] fish: rl.{c, h} - escaping functions for readline