Quotearg part 4: add tests, fix c-maybe colon quoting.
authorEric Blake <ebb9@byu.net>
Tue, 12 Feb 2008 18:42:29 +0000 (11:42 -0700)
committerEric Blake <ebb9@byu.net>
Wed, 13 Feb 2008 02:04:29 +0000 (19:04 -0700)
* lib/quotearg.h: Improve documentation.
* lib/quotearg.c (quotearg_buffer_restyled): Don't add extra
escapes when adding outer quotes.  When quoting trigraphs, use
valid C notation.  When quoting NUL, omit extra characters if next
character is not digit.  Alter prototype.
(quotearg_buffer, quotearg_alloc_mem, quotearg_n_options): Adjust
callers.
* modules/quotearg-tests: New module.
* tests/test-quotearg.c: New test.

Signed-off-by: Eric Blake <ebb9@byu.net>
ChangeLog
lib/quotearg.c
lib/quotearg.h
modules/quotearg-tests [new file with mode: 0644]
tests/test-quotearg.c [new file with mode: 0644]

index 2da0617..ebf9e91 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2008-02-12  Eric Blake  <ebb9@byu.net>
+
+       Quotearg part 4: add tests, fix c-maybe colon quoting.
+       * lib/quotearg.h: Improve documentation.
+       * lib/quotearg.c (quotearg_buffer_restyled): Don't add extra
+       escapes when adding outer quotes.  When quoting trigraphs, use
+       valid C notation.  When quoting NUL, omit extra characters if next
+       character is not digit.  Alter prototype.
+       (quotearg_buffer, quotearg_alloc_mem, quotearg_n_options): Adjust
+       callers.
+       * modules/quotearg-tests: New module.
+       * tests/test-quotearg.c: New test.
+
 2008-02-07  Eric Blake  <ebb9@byu.net>
 
        Quotearg part 3: add flag to control outer quote elision.
 2008-02-07  Eric Blake  <ebb9@byu.net>
 
        Quotearg part 3: add flag to control outer quote elision.
index b6237b0..c2de86f 100644 (file)
@@ -163,6 +163,17 @@ set_quoting_flags (struct quoting_options *o, int i)
   return r;
 }
 
   return r;
 }
 
+/* Return quoting options for STYLE, with no extra quoting.  */
+static struct quoting_options
+quoting_options_from_style (enum quoting_style style)
+{
+  struct quoting_options o;
+  o.style = style;
+  o.flags = 0;
+  memset (o.quote_these_too, 0, sizeof o.quote_these_too);
+  return o;
+}
+
 /* MSGID approximates a quotation mark.  Return its translation if it
    has one; otherwise, return either it or "\"", depending on S.  */
 static char const *
 /* MSGID approximates a quotation mark.  Return its translation if it
    has one; otherwise, return either it or "\"", depending on S.  */
 static char const *
@@ -175,8 +186,8 @@ gettext_quote (char const *msgid, enum quoting_style s)
 }
 
 /* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
 }
 
 /* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
-   argument ARG (of size ARGSIZE), using QUOTING_STYLE, FLAGS, and the
-   remaining part of O to control quoting.
+   argument ARG (of size ARGSIZE), using QUOTING_STYLE, FLAGS, and
+   QUOTE_THESE_TOO to control quoting.
    Terminate the output with a null character, and return the written
    size of the output, not counting the terminating null.
    If BUFFERSIZE is too small to store the output string, return the
    Terminate the output with a null character, and return the written
    size of the output, not counting the terminating null.
    If BUFFERSIZE is too small to store the output string, return the
@@ -184,14 +195,14 @@ gettext_quote (char const *msgid, enum quoting_style s)
    If ARGSIZE is SIZE_MAX, use the string length of the argument for ARGSIZE.
 
    This function acts like quotearg_buffer (BUFFER, BUFFERSIZE, ARG,
    If ARGSIZE is SIZE_MAX, use the string length of the argument for ARGSIZE.
 
    This function acts like quotearg_buffer (BUFFER, BUFFERSIZE, ARG,
-   ARGSIZE, O), except it uses QUOTING_STYLE and FLAGS instead of the
-   quoting style specified by O, and O may not be null.  */
+   ARGSIZE, O), except it breaks O into its component pieces and is
+   not careful about errno.  */
 
 static size_t
 quotearg_buffer_restyled (char *buffer, size_t buffersize,
                          char const *arg, size_t argsize,
                          enum quoting_style quoting_style, int flags,
 
 static size_t
 quotearg_buffer_restyled (char *buffer, size_t buffersize,
                          char const *arg, size_t argsize,
                          enum quoting_style quoting_style, int flags,
-                         struct quoting_options const *o)
+                         unsigned int *quote_these_too)
 {
   size_t i;
   size_t len = 0;
 {
   size_t i;
   size_t len = 0;
@@ -306,8 +317,11 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
              if (elide_outer_quotes)
                goto force_outer_quoting_style;
              STORE ('\\');
              if (elide_outer_quotes)
                goto force_outer_quoting_style;
              STORE ('\\');
-             STORE ('0');
-             STORE ('0');
+             if (i + 1 < argsize && '0' <= arg[i + 1] && arg[i + 1] <= '9')
+               {
+                 STORE ('0');
+                 STORE ('0');
+               }
              c = '0';
            }
          else if (flags & QA_ELIDE_NULL_BYTES)
              c = '0';
            }
          else if (flags & QA_ELIDE_NULL_BYTES)
@@ -336,7 +350,8 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
                    c = arg[i + 2];
                    i += 2;
                    STORE ('?');
                    c = arg[i + 2];
                    i += 2;
                    STORE ('?');
-                   STORE ('\\');
+                   STORE ('"');
+                   STORE ('"');
                    STORE ('?');
                    break;
 
                    STORE ('?');
                    break;
 
@@ -529,8 +544,9 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
          }
        }
 
          }
        }
 
-      if (! (backslash_escapes
-            && o->quote_these_too[c / INT_BITS] & (1 << (c % INT_BITS))))
+      if (! ((backslash_escapes || elide_outer_quotes)
+            && quote_these_too
+            && quote_these_too[c / INT_BITS] & (1 << (c % INT_BITS))))
        goto store_c;
 
     store_escape:
        goto store_c;
 
     store_escape:
@@ -542,7 +558,7 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
       STORE (c);
     }
 
       STORE (c);
     }
 
-  if (i == 0 && quoting_style == shell_always_quoting_style
+  if (len == 0 && quoting_style == shell_always_quoting_style
       && elide_outer_quotes)
     goto force_outer_quoting_style;
 
       && elide_outer_quotes)
     goto force_outer_quoting_style;
 
@@ -555,9 +571,11 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
   return len;
 
  force_outer_quoting_style:
   return len;
 
  force_outer_quoting_style:
+  /* Don't reuse quote_these_too, since the addition of outer quotes
+     sufficiently quotes the specified characters.  */
   return quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
                                   quoting_style,
   return quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
                                   quoting_style,
-                                  flags & ~QA_ELIDE_OUTER_QUOTES, o);
+                                  flags & ~QA_ELIDE_OUTER_QUOTES, NULL);
 }
 
 /* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
 }
 
 /* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
@@ -577,7 +595,7 @@ quotearg_buffer (char *buffer, size_t buffersize,
   struct quoting_options const *p = o ? o : &default_quoting_options;
   int e = errno;
   size_t r = quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
   struct quoting_options const *p = o ? o : &default_quoting_options;
   int e = errno;
   size_t r = quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
-                                      p->style, p->flags, p);
+                                      p->style, p->flags, p->quote_these_too);
   errno = e;
   return r;
 }
   errno = e;
   return r;
 }
@@ -605,9 +623,10 @@ quotearg_alloc_mem (char const *arg, size_t argsize, size_t *size,
   /* Elide embedded null bytes if we can't return a size.  */
   int flags = p->flags | (size ? 0 : QA_ELIDE_NULL_BYTES);
   size_t bufsize = quotearg_buffer_restyled (0, 0, arg, argsize, p->style,
   /* Elide embedded null bytes if we can't return a size.  */
   int flags = p->flags | (size ? 0 : QA_ELIDE_NULL_BYTES);
   size_t bufsize = quotearg_buffer_restyled (0, 0, arg, argsize, p->style,
-                                            flags, p) + 1;
+                                            flags, p->quote_these_too) + 1;
   char *buf = xcharalloc (bufsize);
   char *buf = xcharalloc (bufsize);
-  quotearg_buffer_restyled (buf, bufsize, arg, argsize, p->style, flags, p);
+  quotearg_buffer_restyled (buf, bufsize, arg, argsize, p->style, flags,
+                           p->quote_these_too);
   errno = e;
   if (size)
     *size = bufsize - 1;
   errno = e;
   if (size)
     *size = bufsize - 1;
@@ -695,7 +714,8 @@ quotearg_n_options (int n, char const *arg, size_t argsize,
     /* Elide embedded null bytes since we don't return a size.  */
     int flags = options->flags | QA_ELIDE_NULL_BYTES;
     size_t qsize = quotearg_buffer_restyled (val, size, arg, argsize,
     /* Elide embedded null bytes since we don't return a size.  */
     int flags = options->flags | QA_ELIDE_NULL_BYTES;
     size_t qsize = quotearg_buffer_restyled (val, size, arg, argsize,
-                                            options->style, flags, options);
+                                            options->style, flags,
+                                            options->quote_these_too);
 
     if (size <= qsize)
       {
 
     if (size <= qsize)
       {
@@ -704,7 +724,7 @@ quotearg_n_options (int n, char const *arg, size_t argsize,
          free (val);
        sv[n].val = val = xcharalloc (size);
        quotearg_buffer_restyled (val, size, arg, argsize, options->style,
          free (val);
        sv[n].val = val = xcharalloc (size);
        quotearg_buffer_restyled (val, size, arg, argsize, options->style,
-                                 flags, options);
+                                 flags, options->quote_these_too);
       }
 
     errno = e;
       }
 
     errno = e;
@@ -736,17 +756,6 @@ quotearg_mem (char const *arg, size_t argsize)
   return quotearg_n_mem (0, arg, argsize);
 }
 
   return quotearg_n_mem (0, arg, argsize);
 }
 
-/* Return quoting options for STYLE, with no extra quoting.  */
-static struct quoting_options
-quoting_options_from_style (enum quoting_style style)
-{
-  struct quoting_options o;
-  o.style = style;
-  o.flags = 0;
-  memset (o.quote_these_too, 0, sizeof o.quote_these_too);
-  return o;
-}
-
 char *
 quotearg_n_style (int n, enum quoting_style s, char const *arg)
 {
 char *
 quotearg_n_style (int n, enum quoting_style s, char const *arg)
 {
index 5d3ca47..9f81771 100644 (file)
 
 # include <stddef.h>
 
 
 # include <stddef.h>
 
-/* Basic quoting styles.  */
+/* Basic quoting styles.  For each style, an example is given on the
+   input strings "simple", "\0 \t\n'\"\033?""?/\\", and "a:b", using
+   quotearg_buffer, quotearg_mem, and quotearg_colon_mem with that
+   style and the default flags and quoted characters.  Note that the
+   examples are shown here as valid C strings rather than what
+   displays on a terminal (with "??/" as a trigraph for "\\").  */
 enum quoting_style
   {
     /* Output names as-is (ls --quoting-style=literal).  Can result in
        embedded null bytes if QA_ELIDE_NULL_BYTES is not in
 enum quoting_style
   {
     /* Output names as-is (ls --quoting-style=literal).  Can result in
        embedded null bytes if QA_ELIDE_NULL_BYTES is not in
-       effect.  */
+       effect.
+
+       quotearg_buffer:
+       "simple", "\0 \t\n'\"\033??/\\", "a:b"
+       quotearg:
+       "simple", " \t\n'\"\033??/\\", "a:b"
+       quotearg_colon:
+       "simple", " \t\n'\"\033??/\\", "a:b"
+    */
     literal_quoting_style,
 
     /* Quote names for the shell if they contain shell metacharacters
        or would cause ambiguous output (ls --quoting-style=shell).
        Can result in embedded null bytes if QA_ELIDE_NULL_BYTES is not
     literal_quoting_style,
 
     /* Quote names for the shell if they contain shell metacharacters
        or would cause ambiguous output (ls --quoting-style=shell).
        Can result in embedded null bytes if QA_ELIDE_NULL_BYTES is not
-       in effect.  */
+       in effect.
+
+       quotearg_buffer:
+       "simple", "'\0 \t\n'\\''\"\033??/\\'", "a:b"
+       quotearg:
+       "simple", "' \t\n'\\''\"\033??/\\'", "a:b"
+       quotearg_colon:
+       "simple", "' \t\n'\\''\"\033??/\\'", "'a:b'"
+    */
     shell_quoting_style,
 
     /* Quote names for the shell, even if they would normally not
        require quoting (ls --quoting-style=shell-always).  Can result
        in embedded null bytes if QA_ELIDE_NULL_BYTES is not in effect.
        Behaves like shell_quoting_style if QA_ELIDE_OUTER_QUOTES is in
     shell_quoting_style,
 
     /* Quote names for the shell, even if they would normally not
        require quoting (ls --quoting-style=shell-always).  Can result
        in embedded null bytes if QA_ELIDE_NULL_BYTES is not in effect.
        Behaves like shell_quoting_style if QA_ELIDE_OUTER_QUOTES is in
-       effect.  */
+       effect.
+
+       quotearg_buffer:
+       "'simple'", "'\0 \t\n'\\''\"\033??/\\'", "'a:b'"
+       quotearg:
+       "'simple'", "' \t\n'\\''\"\033??/\\'", "'a:b'"
+       quotearg_colon:
+       "'simple'", "' \t\n'\\''\"\033??/\\'", "'a:b'"
+    */
     shell_always_quoting_style,
 
     /* Quote names as for a C language string (ls --quoting-style=c).
        Behaves like c_maybe_quoting_style if QA_ELIDE_OUTER_QUOTES is
     shell_always_quoting_style,
 
     /* Quote names as for a C language string (ls --quoting-style=c).
        Behaves like c_maybe_quoting_style if QA_ELIDE_OUTER_QUOTES is
-       in effect.  */
+       in effect.
+
+       quotearg_buffer:
+       "\"simple\"", "\"\\0 \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a:b\""
+       quotearg:
+       "\"simple\"", "\"\\0 \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a:b\""
+       quotearg_colon:
+       "\"simple\"", "\"\\0 \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a\\:b\""
+    */
     c_quoting_style,
 
     /* Like c_quoting_style except omit the surrounding double-quote
     c_quoting_style,
 
     /* Like c_quoting_style except omit the surrounding double-quote
-       characters if no quoted characters are encountered.  */
+       characters if no quoted characters are encountered.
+
+       quotearg_buffer:
+       "simple", "\"\\0 \\t\\n'\\\"\\033?\"\"?/\\\\\"", "a:b"
+       quotearg:
+       "simple", "\"\\0 \\t\\n'\\\"\\033?\"\"?/\\\\\"", "a:b"
+       quotearg_colon:
+       "simple", "\"\\0 \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a:b\""
+    */
     c_maybe_quoting_style,
 
     /* Like c_quoting_style except always omit the surrounding
     c_maybe_quoting_style,
 
     /* Like c_quoting_style except always omit the surrounding
-       double-quote characters (ls --quoting-style=escape).  */
+       double-quote characters and don't worry about trigraphs (ls
+       --quoting-style=escape).
+
+       quotearg_buffer:
+       "simple", "\\0 \\t\\n'\"\\033??/\\\\", "a:b"
+       quotearg:
+       "simple", "\\0 \\t\\n'\"\\033??/\\\\", "a:b"
+       quotearg_colon:
+       "simple", "\\0 \\t\\n'\"\\033??/\\\\", "a\\:b"
+    */
     escape_quoting_style,
 
     /* Like clocale_quoting_style, but quote `like this' instead of
     escape_quoting_style,
 
     /* Like clocale_quoting_style, but quote `like this' instead of
-       "like this" in the default C locale (ls --quoting-style=locale).  */
+       "like this" in the default C locale (ls --quoting-style=locale).
+
+       LC_MESSAGES=C
+       quotearg_buffer:
+       "`simple'", "`\\0 \\t\\n\\'\"\\033??/\\\\'", "`a:b'"
+       quotearg:
+       "`simple'", "`\\0 \\t\\n\\'\"\\033??/\\\\'", "`a:b'"
+       quotearg_colon:
+       "`simple'", "`\\0 \\t\\n\\'\"\\033??/\\\\'", "`a\\:b'"
+
+       LC_MESSAGES=pt_PT.utf8
+       quotearg_buffer:
+       "\302\253simple\302\273",
+       "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a:b\302\273"
+       quotearg:
+       "\302\253simple\302\273",
+       "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a:b\302\273"
+       quotearg_colon:
+       "\302\253simple\302\273",
+       "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a\\:b\302\273"
+    */
     locale_quoting_style,
 
     /* Like c_quoting_style except use quotation marks appropriate for
     locale_quoting_style,
 
     /* Like c_quoting_style except use quotation marks appropriate for
-       the locale (ls --quoting-style=clocale).  */
+       the locale and don't worry about trigraphs (ls
+       --quoting-style=clocale).
+
+       LC_MESSAGES=C
+       quotearg_buffer:
+       "\"simple\"", "\"\\0 \\t\\n'\\\"\\033??/\\\\\"", "\"a:b\""
+       quotearg:
+       "\"simple\"", "\"\\0 \\t\\n'\\\"\\033??/\\\\\"", "\"a:b\""
+       quotearg_colon:
+       "\"simple\"", "\"\\0 \\t\\n'\\\"\\033??/\\\\\"", "\"a\\:b\""
+
+       LC_MESSAGES=pt_PT.utf8
+       quotearg_buffer:
+       "\302\253simple\302\273",
+       "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a:b\302\273"
+       quotearg:
+       "\302\253simple\302\273",
+       "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a:b\302\273"
+       quotearg_colon:
+       "\302\253simple\302\273",
+       "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a\\:b\302\273"
+    */
     clocale_quoting_style
   };
 
     clocale_quoting_style
   };
 
diff --git a/modules/quotearg-tests b/modules/quotearg-tests
new file mode 100644 (file)
index 0000000..724d540
--- /dev/null
@@ -0,0 +1,12 @@
+Files:
+tests/test-quotearg.c
+
+Depends-on:
+progname
+stdint
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-quotearg
+check_PROGRAMS += test-quotearg
diff --git a/tests/test-quotearg.c b/tests/test-quotearg.c
new file mode 100644 (file)
index 0000000..86ffd7c
--- /dev/null
@@ -0,0 +1,272 @@
+/* Test of quotearg family of functions.
+   Copyright (C) 2008 Free Software Foundation, 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 3, 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.  */
+
+/* Written by Eric Blake <ebb9@byu.net>, 2008.  */
+
+#include <config.h>
+
+#include "quotearg.h"
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+
+/* These quotes are borrowed from a pt_PT.utf8 translation.  */
+# define LQ "\302\253"
+# define RQ "\302\273"
+#endif
+
+#define ASSERT(expr) \
+  do                                                                        \
+    {                                                                       \
+      if (!(expr))                                                          \
+       {                                                                    \
+         fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
+         abort ();                                                          \
+       }                                                                    \
+    }                                                                       \
+  while (0)
+
+struct result_strings {
+  char const *str1; /* Translation of "".  */
+  char const *str2; /* Translation of "\0""1\0".  */
+  size_t len2; /* Length of str2.  */
+  char const *str3; /* Translation of "simple".  */
+  char const *str4; /* Translation of " \t\n'\"\033?""?/\\".  */
+  char const *str5; /* Translation of "a:b".  */
+};
+
+struct result_groups {
+  struct result_strings group1; /* Via quotearg_buffer.  */
+  struct result_strings group2; /* Via quotearg{,_mem}.  */
+  struct result_strings group3; /* Via quotearg_colon{,_mem}.  */
+};
+
+static struct result_strings inputs = {
+  "", "\0001\0", 3, "simple", " \t\n'\"\033?""?/\\", "a:b"
+};
+
+static struct result_groups results[] = {
+  /* literal_quoting_style */
+  { { "", "\0""1\0", 3, "simple", " \t\n'\"\033?""?/\\", "a:b" },
+    { "", "1", 1, "simple", " \t\n'\"\033?""?/\\", "a:b" },
+    { "", "1", 1, "simple", " \t\n'\"\033?""?/\\", "a:b" } },
+
+  /* shell_quoting_style */
+  { { "''", "\0""1\0", 3, "simple", "' \t\n'\\''\"\033?""?/\\'", "a:b" },
+    { "''", "1", 1, "simple", "' \t\n'\\''\"\033?""?/\\'", "a:b" },
+    { "''", "1", 1, "simple", "' \t\n'\\''\"\033?""?/\\'", "'a:b'" } },
+
+  /* shell_always_quoting_style */
+  { { "''", "'\0""1\0'", 5, "'simple'", "' \t\n'\\''\"\033?""?/\\'", "'a:b'" },
+    { "''", "'1'", 3, "'simple'", "' \t\n'\\''\"\033?""?/\\'", "'a:b'" },
+    { "''", "'1'", 3, "'simple'", "' \t\n'\\''\"\033?""?/\\'", "'a:b'" } },
+
+  /* c_quoting_style */
+  { { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"",
+      "\" \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a:b\"" },
+    { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"",
+      "\" \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a:b\"" },
+    { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"",
+      "\" \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a\\:b\"" } },
+
+  /* c_maybe_quoting_style */
+  { { "", "\"\\0001\\0\"", 9, "simple", "\" \\t\\n'\\\"\\033?\"\"?/\\\\\"",
+      "a:b" },
+    { "", "\"\\0001\\0\"", 9, "simple", "\" \\t\\n'\\\"\\033?\"\"?/\\\\\"",
+      "a:b" },
+    { "", "\"\\0001\\0\"", 9, "simple", "\" \\t\\n'\\\"\\033?\"\"?/\\\\\"",
+      "\"a:b\"" } },
+
+  /* escape_quoting_style */
+  { { "", "\\0001\\0", 7, "simple", " \\t\\n'\"\\033?""?/\\\\", "a:b" },
+    { "", "\\0001\\0", 7, "simple", " \\t\\n'\"\\033?""?/\\\\", "a:b" },
+    { "", "\\0001\\0", 7, "simple", " \\t\\n'\"\\033?""?/\\\\", "a\\:b" } },
+
+  /* locale_quoting_style */
+  { { "`'", "`\\0001\\0'", 9, "`simple'", "` \\t\\n\\'\"\\033?""?/\\\\'",
+      "`a:b'" },
+    { "`'", "`\\0001\\0'", 9, "`simple'", "` \\t\\n\\'\"\\033?""?/\\\\'",
+      "`a:b'" },
+    { "`'", "`\\0001\\0'", 9, "`simple'", "` \\t\\n\\'\"\\033?""?/\\\\'",
+      "`a\\:b'" } },
+
+  /* clocale_quoting_style */
+  { { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"",
+      "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a:b\"" },
+    { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"",
+      "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a:b\"" },
+    { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"",
+      "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a\\:b\"" } }
+};
+
+#if ENABLE_NLS
+static struct result_groups locale_results[] = {
+  /* locale_quoting_style */
+  { { LQ RQ, LQ "\\0001\\0" RQ, 11, LQ "simple" RQ,
+      LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a:b" RQ },
+    { LQ RQ, LQ "\\0001\\0" RQ, 11, LQ "simple" RQ,
+      LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a:b" RQ },
+    { LQ RQ, LQ "\\0001\\0" RQ, 11, LQ "simple" RQ,
+      LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a\\:b" RQ } },
+
+  /* clocale_quoting_style */
+  { { LQ RQ, LQ "\\0001\\0" RQ, 11, LQ "simple" RQ,
+      LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a:b" RQ },
+    { LQ RQ, LQ "\\0001\\0" RQ, 11, LQ "simple" RQ,
+      LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a:b" RQ },
+    { LQ RQ, LQ "\\0001\\0" RQ, 11, LQ "simple" RQ,
+      LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a\\:b" RQ } }
+};
+#endif /* ENABLE_NLS */
+
+static void
+compare (char const *a, size_t la, char const *b, size_t lb)
+{
+  ASSERT (la == lb);
+  ASSERT (memcmp (a, b, la) == 0);
+  ASSERT (b[lb] == '\0');
+}
+
+static void
+compare_strings (char *(func) (char const *, size_t *),
+                struct result_strings *results)
+{
+  size_t len;
+  char *p;
+
+  len = 0;
+  p = func (inputs.str1, &len);
+  compare (results->str1, strlen (results->str1), p, len);
+
+  len = inputs.len2;
+  p = func (inputs.str2, &len);
+  compare (results->str2, results->len2, p, len);
+
+  len = SIZE_MAX;
+  p = func (inputs.str3, &len);
+  compare (results->str3, strlen (results->str3), p, len);
+
+  len = strlen (inputs.str4);
+  p = func (inputs.str4, &len);
+  compare (results->str4, strlen (results->str4), p, len);
+
+  len = SIZE_MAX;
+  p = func (inputs.str5, &len);
+  compare (results->str5, strlen (results->str5), p, len);
+}
+
+static char *
+use_quotearg_buffer (const char *str, size_t *len)
+{
+  static char buf[100];
+  size_t size;
+  memset (buf, 0xa5, 100);
+  size = quotearg_buffer (buf, 100, str, *len, NULL);
+  *len = size;
+  ASSERT ((unsigned char) buf[size + 1] == 0xa5);
+  return buf;
+}
+
+static char *
+use_quotearg (const char *str, size_t *len)
+{
+  char *p = *len == SIZE_MAX ? quotearg (str) : quotearg_mem (str, *len);
+  *len = strlen (p);
+  return p;
+}
+
+static char *
+use_quotearg_colon (const char *str, size_t *len)
+{
+  char *p = (*len == SIZE_MAX ? quotearg_colon (str)
+            : quotearg_colon_mem (str, *len));
+  *len = strlen (p);
+  return p;
+}
+
+#if ENABLE_NLS
+/* True if the locale should be faked.  */
+static bool fake_locale;
+
+/* A replacement gettext that allows testing of locale quotes without
+   requiring a locale.  */
+char *
+gettext (char const *str)
+{
+  if (fake_locale)
+    {
+      static char lq[] = LQ;
+      static char rq[] = RQ;
+      if (strcmp (str, "`") == 0)
+       return lq;
+      if (strcmp (str, "'") == 0)
+       return rq;
+    }
+  return (char *) str;
+}
+
+char *
+dgettext (char const *d, char const *str)
+{
+  return gettext (str);
+}
+#endif /* ENABLE_NLS */
+
+int
+main (int argc, char **argv)
+{
+  int i;
+
+  /* This program is hard-wired to the C locale since it does not call
+     setlocale.  */
+  ASSERT (!isprint ('\033'));
+  for (i = literal_quoting_style; i <= clocale_quoting_style; i++)
+    {
+      set_quoting_style (NULL, i);
+      compare_strings (use_quotearg_buffer, &results[i].group1);
+      compare_strings (use_quotearg, &results[i].group2);
+      compare_strings (use_quotearg_colon, &results[i].group3);
+    }
+
+#if ENABLE_NLS
+  /* Rather than change locales, and require a .gmo file with
+     translations for "`" and "'" that match our expectations, we
+     merely override the gettext function to satisfy the link
+     dependencies of quotearg.c.  */
+  fake_locale = true;
+
+  set_quoting_style (NULL, locale_quoting_style);
+  compare_strings (use_quotearg_buffer, &locale_results[0].group1);
+  compare_strings (use_quotearg, &locale_results[0].group2);
+  compare_strings (use_quotearg_colon, &locale_results[0].group3);
+
+  set_quoting_style (NULL, clocale_quoting_style);
+  compare_strings (use_quotearg_buffer, &locale_results[1].group1);
+  compare_strings (use_quotearg, &locale_results[1].group2);
+  compare_strings (use_quotearg_colon, &locale_results[1].group3);
+#endif /* ENABLE_NLS */
+
+  quotearg_free ();
+  return 0;
+}