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.
index b6237b0..c2de86f 100644 (file)
@@ -163,6 +163,17 @@ set_quoting_flags (struct quoting_options *o, int i)
   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 *
@@ -175,8 +186,8 @@ gettext_quote (char const *msgid, enum quoting_style s)
 }
 
 /* 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
@@ -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,
-   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,
-                         struct quoting_options const *o)
+                         unsigned int *quote_these_too)
 {
   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 ('\\');
-             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)
@@ -336,7 +350,8 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
                    c = arg[i + 2];
                    i += 2;
                    STORE ('?');
-                   STORE ('\\');
+                   STORE ('"');
+                   STORE ('"');
                    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:
@@ -542,7 +558,7 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
       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;
 
@@ -555,9 +571,11 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
   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,
-                                  flags & ~QA_ELIDE_OUTER_QUOTES, o);
+                                  flags & ~QA_ELIDE_OUTER_QUOTES, NULL);
 }
 
 /* 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,
-                                      p->style, p->flags, p);
+                                      p->style, p->flags, p->quote_these_too);
   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,
-                                            flags, p) + 1;
+                                            flags, p->quote_these_too) + 1;
   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;
@@ -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,
-                                            options->style, flags, options);
+                                            options->style, flags,
+                                            options->quote_these_too);
 
     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,
-                                 flags, options);
+                                 flags, options->quote_these_too);
       }
 
     errno = e;
@@ -736,17 +756,6 @@ quotearg_mem (char const *arg, size_t 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)
 {
index 5d3ca47..9f81771 100644 (file)
 
 # 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
-       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
-       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
-       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
-       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
-       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
-       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
-       "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
-       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
   };
 
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;
+}