utimens: cache whether utimensat syscall works
[pspp] / lib / quotearg.c
index f9628e5fa743e43f5c2d69cc03c69337c7afae5f..8380e9de6c038e604e50440d5ac49415a94c7aa2 100644 (file)
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
-#if !HAVE_MBRTOWC
-/* Disable multibyte processing entirely.  Since MB_CUR_MAX is 1, the
-   other macros are defined only for documentation and to satisfy C
-   syntax.  */
-# undef MB_CUR_MAX
-# define MB_CUR_MAX 1
-# undef mbstate_t
-# define mbstate_t int
-# define mbrtowc(pwc, s, n, ps) ((*(pwc) = *(s)) != 0)
-# define iswprint(wc) isprint ((unsigned char) (wc))
-# undef HAVE_MBSINIT
-#endif
-
-#if !defined mbsinit && !HAVE_MBSINIT
-# define mbsinit(ps) 1
-#endif
-
 #ifndef SIZE_MAX
 # define SIZE_MAX ((size_t) -1)
 #endif
@@ -65,14 +48,18 @@ struct quoting_options
   /* Basic quoting style.  */
   enum quoting_style style;
 
-  /* Additional flags.  Behavior is altered according to these bits:
-     0x01: Elide null bytes rather than embed them unquoted.
-   */
+  /* Additional flags.  Bitwise combination of enum quoting_flags.  */
   int flags;
 
   /* Quote the characters indicated by this bit vector even if the
      quoting style would not normally require them to be quoted.  */
   unsigned int quote_these_too[(UCHAR_MAX / INT_BITS) + 1];
+
+  /* The left quote for custom_quoting_style.  */
+  char const *left_quote;
+
+  /* The right quote for custom_quoting_style.  */
+  char const *right_quote;
 };
 
 /* Names of quoting styles.  */
@@ -82,6 +69,7 @@ char const *const quoting_style_args[] =
   "shell",
   "shell-always",
   "c",
+  "c-maybe",
   "escape",
   "locale",
   "clocale",
@@ -95,6 +83,7 @@ enum quoting_style const quoting_style_vals[] =
   shell_quoting_style,
   shell_always_quoting_style,
   c_quoting_style,
+  c_maybe_quoting_style,
   escape_quoting_style,
   locale_quoting_style,
   clocale_quoting_style
@@ -149,10 +138,9 @@ set_char_quoting (struct quoting_options *o, char c, int i)
 }
 
 /* In O (or in the default if O is null),
-   set the value of the quoting options flag to I.
-   Return the old value.  Currently, the only values defined for I are
-   0 (the default) and 1 (which means to elide null bytes from styles
-   that would otherwise output them unquoted).  */
+   set the value of the quoting options flag to I, which can be a
+   bitwise combination of enum quoting_flags, or 0 for default
+   behavior.  Return the old value.  */
 int
 set_quoting_flags (struct quoting_options *o, int i)
 {
@@ -164,6 +152,30 @@ set_quoting_flags (struct quoting_options *o, int i)
   return r;
 }
 
+void
+set_custom_quoting (struct quoting_options *o,
+                    char const *left_quote, char const *right_quote)
+{
+  if (!o)
+    o = &default_quoting_options;
+  o->style = custom_quoting_style;
+  if (!left_quote || !right_quote)
+    abort ();
+  o->left_quote = left_quote;
+  o->right_quote = right_quote;
+}
+
+/* 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 *
@@ -176,8 +188,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
@@ -185,14 +197,16 @@ 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 const *quote_these_too,
+                         char const *left_quote,
+                         char const *right_quote)
 {
   size_t i;
   size_t len = 0;
@@ -200,6 +214,7 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
   size_t quote_string_len = 0;
   bool backslash_escapes = false;
   bool unibyte_locale = MB_CUR_MAX == 1;
+  bool elide_outer_quotes = (flags & QA_ELIDE_OUTER_QUOTES) != 0;
 
 #define STORE(c) \
     do \
@@ -212,8 +227,13 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
 
   switch (quoting_style)
     {
+    case c_maybe_quoting_style:
+      quoting_style = c_quoting_style;
+      elide_outer_quotes = true;
+      /* Fall through.  */
     case c_quoting_style:
-      STORE ('"');
+      if (!elide_outer_quotes)
+       STORE ('"');
       backslash_escapes = true;
       quote_string = "\"";
       quote_string_len = 1;
@@ -221,61 +241,80 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
 
     case escape_quoting_style:
       backslash_escapes = true;
+      elide_outer_quotes = false;
       break;
 
     case locale_quoting_style:
     case clocale_quoting_style:
+    case custom_quoting_style:
       {
-       /* TRANSLATORS:
-          Get translations for open and closing quotation marks.
-
-          The message catalog should translate "`" to a left
-          quotation mark suitable for the locale, and similarly for
-          "'".  If the catalog has no translation,
-          locale_quoting_style quotes `like this', and
-          clocale_quoting_style quotes "like this".
-
-          For example, an American English Unicode locale should
-          translate "`" to U+201C (LEFT DOUBLE QUOTATION MARK), and
-          should translate "'" to U+201D (RIGHT DOUBLE QUOTATION
-          MARK).  A British English Unicode locale should instead
-          translate these to U+2018 (LEFT SINGLE QUOTATION MARK) and
-          U+2019 (RIGHT SINGLE QUOTATION MARK), respectively.
-
-          If you don't know what to put here, please see
-          <http://en.wikipedia.org/wiki/Quotation_mark#Glyphs>
-          and use glyphs suitable for your language.  */
-
-       char const *left = gettext_quote (N_("`"), quoting_style);
-       char const *right = gettext_quote (N_("'"), quoting_style);
-       for (quote_string = left; *quote_string; quote_string++)
-         STORE (*quote_string);
+       if (quoting_style != custom_quoting_style)
+         {
+           /* TRANSLATORS:
+              Get translations for open and closing quotation marks.
+
+              The message catalog should translate "`" to a left
+              quotation mark suitable for the locale, and similarly for
+              "'".  If the catalog has no translation,
+              locale_quoting_style quotes `like this', and
+              clocale_quoting_style quotes "like this".
+
+              For example, an American English Unicode locale should
+              translate "`" to U+201C (LEFT DOUBLE QUOTATION MARK), and
+              should translate "'" to U+201D (RIGHT DOUBLE QUOTATION
+              MARK).  A British English Unicode locale should instead
+              translate these to U+2018 (LEFT SINGLE QUOTATION MARK)
+              and U+2019 (RIGHT SINGLE QUOTATION MARK), respectively.
+
+              If you don't know what to put here, please see
+              <http://en.wikipedia.org/wiki/Quotation_mark#Glyphs>
+              and use glyphs suitable for your language.  */
+           left_quote = gettext_quote (N_("`"), quoting_style);
+           right_quote = gettext_quote (N_("'"), quoting_style);
+         }
+       if (!elide_outer_quotes)
+         for (quote_string = left_quote; *quote_string; quote_string++)
+           STORE (*quote_string);
        backslash_escapes = true;
-       quote_string = right;
+       quote_string = right_quote;
        quote_string_len = strlen (quote_string);
       }
       break;
 
+    case shell_quoting_style:
+      quoting_style = shell_always_quoting_style;
+      elide_outer_quotes = true;
+      /* Fall through.  */
     case shell_always_quoting_style:
-      STORE ('\'');
+      if (!elide_outer_quotes)
+       STORE ('\'');
       quote_string = "'";
       quote_string_len = 1;
       break;
 
-    default:
+    case literal_quoting_style:
+      elide_outer_quotes = false;
       break;
+
+    default:
+      abort ();
     }
 
   for (i = 0;  ! (argsize == SIZE_MAX ? arg[i] == '\0' : i == argsize);  i++)
     {
       unsigned char c;
       unsigned char esc;
+      bool is_right_quote = false;
 
       if (backslash_escapes
          && quote_string_len
          && i + quote_string_len <= argsize
          && memcmp (arg + i, quote_string, quote_string_len) == 0)
-       STORE ('\\');
+       {
+         if (elide_outer_quotes)
+           goto force_outer_quoting_style;
+         is_right_quote = true;
+       }
 
       c = arg[i];
       switch (c)
@@ -283,23 +322,40 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
        case '\0':
          if (backslash_escapes)
            {
+             if (elide_outer_quotes)
+               goto force_outer_quoting_style;
              STORE ('\\');
-             STORE ('0');
-             STORE ('0');
+             /* If quote_string were to begin with digits, we'd need to
+                test for the end of the arg as well.  However, it's
+                hard to imagine any locale that would use digits in
+                quotes, and set_custom_quoting is documented not to
+                accept them.  */
+             if (i + 1 < argsize && '0' <= arg[i + 1] && arg[i + 1] <= '9')
+               {
+                 STORE ('0');
+                 STORE ('0');
+               }
              c = '0';
+             /* We don't have to worry that this last '0' will be
+                backslash-escaped because, again, quote_string should
+                not start with it and because quote_these_too is
+                documented as not accepting it.  */
            }
-         else if (flags & 0x1)
+         else if (flags & QA_ELIDE_NULL_BYTES)
            continue;
          break;
 
        case '?':
          switch (quoting_style)
            {
-           case shell_quoting_style:
-             goto use_shell_always_quoting_style;
+           case shell_always_quoting_style:
+             if (elide_outer_quotes)
+               goto force_outer_quoting_style;
+             break;
 
            case c_quoting_style:
-             if (i + 2 < argsize && arg[i + 1] == '?')
+             if ((flags & QA_SPLIT_TRIGRAPHS)
+                 && i + 2 < argsize && arg[i + 1] == '?')
                switch (arg[i + 2])
                  {
                  case '!': case '\'':
@@ -307,10 +363,13 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
                  case '<': case '=': case '>':
                    /* Escape the second '?' in what would otherwise be
                       a trigraph.  */
+                   if (elide_outer_quotes)
+                     goto force_outer_quoting_style;
                    c = arg[i + 2];
                    i += 2;
                    STORE ('?');
-                   STORE ('\\');
+                   STORE ('"');
+                   STORE ('"');
                    STORE ('?');
                    break;
 
@@ -331,11 +390,17 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
        case '\r': esc = 'r'; goto c_and_shell_escape;
        case '\t': esc = 't'; goto c_and_shell_escape;
        case '\v': esc = 'v'; goto c_escape;
-       case '\\': esc = c; goto c_and_shell_escape;
+       case '\\': esc = c;
+         /* No need to escape the escape if we are trying to elide
+            outer quotes and nothing else is problematic.  */
+         if (backslash_escapes && elide_outer_quotes && quote_string_len)
+           goto store_c;
 
        c_and_shell_escape:
-         if (quoting_style == shell_quoting_style)
-           goto use_shell_always_quoting_style;
+         if (quoting_style == shell_always_quoting_style
+             && elide_outer_quotes)
+           goto force_outer_quoting_style;
+         /* Fall through.  */
        c_escape:
          if (backslash_escapes)
            {
@@ -365,24 +430,19 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
             be the first bytes of multibyte characters, which means
             we should check them with mbrtowc, but in practice this
             doesn't happen so it's not worth worrying about.  */
-         if (quoting_style == shell_quoting_style)
-           goto use_shell_always_quoting_style;
+         if (quoting_style == shell_always_quoting_style
+             && elide_outer_quotes)
+           goto force_outer_quoting_style;
          break;
 
        case '\'':
-         switch (quoting_style)
+         if (quoting_style == shell_always_quoting_style)
            {
-           case shell_quoting_style:
-             goto use_shell_always_quoting_style;
-
-           case shell_always_quoting_style:
+             if (elide_outer_quotes)
+               goto force_outer_quoting_style;
              STORE ('\'');
              STORE ('\\');
              STORE ('\'');
-             break;
-
-           default:
-             break;
            }
          break;
 
@@ -399,7 +459,15 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
        case 'o': case 'p': case 'q': case 'r': case 's': case 't':
        case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
          /* These characters don't cause problems, no matter what the
-            quoting style is.  They cannot start multibyte sequences.  */
+            quoting style is.  They cannot start multibyte sequences.
+            A digit or a special letter would cause trouble if it
+            appeared at the beginning of quote_string because we'd then
+            escape by prepending a backslash.  However, it's hard to
+            imagine any locale that would use digits or letters as
+            quotes, and set_custom_quoting is documented not to accept
+            them.  Also, a digit or a special letter would cause
+            trouble if it appeared in quote_these_too, but that's also
+            documented as not accepting them.  */
          break;
 
        default:
@@ -454,7 +522,8 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
                           that is really the 2nd byte of a multibyte character.
                           In practice the problem is limited to ASCII
                           chars >= '@' that are shell special chars.  */
-                       if ('[' == 0x5b && quoting_style == shell_quoting_style)
+                       if ('[' == 0x5b && elide_outer_quotes
+                           && quoting_style == shell_always_quoting_style)
                          {
                            size_t j;
                            for (j = 1; j < bytes; j++)
@@ -462,7 +531,7 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
                                {
                                case '[': case '\\': case '^':
                                case '`': case '|':
-                                 goto use_shell_always_quoting_style;
+                                 goto force_outer_quoting_style;
 
                                default:
                                  break;
@@ -487,11 +556,18 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
                  {
                    if (backslash_escapes && ! printable)
                      {
+                       if (elide_outer_quotes)
+                         goto force_outer_quoting_style;
                        STORE ('\\');
                        STORE ('0' + (c >> 6));
                        STORE ('0' + ((c >> 3) & 7));
                        c = '0' + (c & 7);
                      }
+                   else if (is_right_quote)
+                     {
+                       STORE ('\\');
+                       is_right_quote = false;
+                     }
                    if (ilim <= i + 1)
                      break;
                    STORE (c);
@@ -503,21 +579,26 @@ 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)))
+         && !is_right_quote)
        goto store_c;
 
     store_escape:
+      if (elide_outer_quotes)
+       goto force_outer_quoting_style;
       STORE ('\\');
 
     store_c:
       STORE (c);
     }
 
-  if (i == 0 && quoting_style == shell_quoting_style)
-    goto use_shell_always_quoting_style;
+  if (len == 0 && quoting_style == shell_always_quoting_style
+      && elide_outer_quotes)
+    goto force_outer_quoting_style;
 
-  if (quote_string)
+  if (quote_string && !elide_outer_quotes)
     for (; *quote_string; quote_string++)
       STORE (*quote_string);
 
@@ -525,9 +606,13 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
     buffer[len] = '\0';
   return len;
 
- use_shell_always_quoting_style:
+ 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,
-                                  shell_always_quoting_style, flags, o);
+                                  quoting_style,
+                                  flags & ~QA_ELIDE_OUTER_QUOTES, NULL,
+                                  left_quote, right_quote);
 }
 
 /* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
@@ -547,7 +632,8 @@ 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,
+                                      p->left_quote, p->right_quote);
   errno = e;
   return r;
 }
@@ -573,11 +659,15 @@ quotearg_alloc_mem (char const *arg, size_t argsize, size_t *size,
   struct quoting_options const *p = o ? o : &default_quoting_options;
   int e = errno;
   /* Elide embedded null bytes if we can't return a size.  */
-  int flags = p->flags | (size ? 0 : 0x1);
+  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,
+                                            p->left_quote,
+                                            p->right_quote) + 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,
+                           p->left_quote, p->right_quote);
   errno = e;
   if (size)
     *size = bufsize - 1;
@@ -663,9 +753,12 @@ quotearg_n_options (int n, char const *arg, size_t argsize,
     size_t size = sv[n].size;
     char *val = sv[n].val;
     /* 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,
-                                            options->flags | 0x1, options);
+                                            options->style, flags,
+                                            options->quote_these_too,
+                                            options->left_quote,
+                                            options->right_quote);
 
     if (size <= qsize)
       {
@@ -674,7 +767,9 @@ 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,
-                                 options->flags | 0x1, options);
+                                 flags, options->quote_these_too,
+                                 options->left_quote,
+                                 options->right_quote);
       }
 
     errno = e;
@@ -706,17 +801,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)
 {
@@ -770,3 +854,36 @@ quotearg_colon_mem (char const *arg, size_t argsize)
 {
   return quotearg_char_mem (arg, argsize, ':');
 }
+
+char *
+quotearg_n_custom (int n, char const *left_quote,
+                  char const *right_quote, char const *arg)
+{
+  return quotearg_n_custom_mem (n, left_quote, right_quote, arg,
+                               SIZE_MAX);
+}
+
+char *
+quotearg_n_custom_mem (int n, char const *left_quote,
+                      char const *right_quote,
+                      char const *arg, size_t argsize)
+{
+  struct quoting_options o = default_quoting_options;
+  set_custom_quoting (&o, left_quote, right_quote);
+  return quotearg_n_options (n, arg, argsize, &o);
+}
+
+char *
+quotearg_custom (char const *left_quote, char const *right_quote,
+                char const *arg)
+{
+  return quotearg_n_custom (0, left_quote, right_quote, arg);
+}
+
+char *
+quotearg_custom_mem (char const *left_quote, char const *right_quote,
+                    char const *arg, size_t argsize)
+{
+  return quotearg_n_custom_mem (0, left_quote, right_quote, arg,
+                               argsize);
+}