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
Seemingly Similar 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