New function quotearg_alloc. Treat { } = as special sometimes.
[gnulib.git] / lib / quotearg.c
index d5fbc9e..fe747fb 100644 (file)
@@ -1,5 +1,7 @@
 /* quotearg.c - quote arguments for output
-   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 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
 # include <config.h>
 #endif
 
-#include <sys/types.h>
-#include <quotearg.h>
-#include <xalloc.h>
-
-#include <ctype.h>
+#include "quotearg.h"
 
-#if ENABLE_NLS
-# include <libintl.h>
-# define _(text) gettext (text)
-#else
-# define _(text) text
-#endif
-#define N_(text) text
+#include "xalloc.h"
 
-#if HAVE_LIMITS_H
-# include <limits.h>
-#endif
-#ifndef CHAR_BIT
-# define CHAR_BIT 8
-#endif
-#ifndef UCHAR_MAX
-# define UCHAR_MAX ((unsigned char) -1)
-#endif
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
 
-#if HAVE_C_BACKSLASH_A
-# define ALERT_CHAR '\a'
-#else
-# define ALERT_CHAR '\7'
-#endif
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
 
-#if HAVE_STDLIB_H
-# include <stdlib.h>
-#endif
+#if HAVE_WCHAR_H
 
-#if HAVE_STRING_H
-# include <string.h>
-#endif
+/* BSD/OS 4.1 wchar.h requires FILE and struct tm to be declared.  */
+# include <stdio.h>
+# include <time.h>
 
-#if HAVE_WCHAR_H
 # include <wchar.h>
 #endif
 
-#if HAVE_MBRTOWC
-size_t mbrtowc ();
-# ifdef mbstate_t
-#  define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0)
-#  define mbsinit(ps) 1
-# endif
-#else
+#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
 # 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
-# define iswprint(wc) ISPRINT ((unsigned char) (wc))
 #endif
 
 #ifndef iswprint
@@ -89,17 +70,11 @@ size_t mbrtowc ();
 # endif
 #endif
 
-#define INT_BITS (sizeof (int) * CHAR_BIT)
-
-#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
-# define IN_CTYPE_DOMAIN(c) 1
-#else
-# define IN_CTYPE_DOMAIN(c) isascii(c)
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
 #endif
 
-/* Undefine to protect against the definition in wctype.h of solaris2.6.   */
-#undef ISPRINT
-#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c))
+#define INT_BITS (sizeof (int) * CHAR_BIT)
 
 struct quoting_options
 {
@@ -145,9 +120,10 @@ static struct quoting_options default_quoting_options;
 struct quoting_options *
 clone_quoting_options (struct quoting_options *o)
 {
-  struct quoting_options *p
-    = (struct quoting_options *) xmalloc (sizeof (struct quoting_options));
+  int e = errno;
+  struct quoting_options *p = xmalloc (sizeof *p);
   *p = *(o ? o : &default_quoting_options);
+  errno = e;
   return p;
 }
 
@@ -200,7 +176,7 @@ gettext_quote (char const *msgid, enum quoting_style s)
    size of the output, not counting the terminating null.
    If BUFFERSIZE is too small to store the output string, return the
    value that would have been returned had BUFFERSIZE been large enough.
-   If ARGSIZE is -1, use the string length of the argument for ARGSIZE.
+   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 instead of the quoting
@@ -279,7 +255,7 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
       break;
     }
 
-  for (i = 0;  ! (argsize == (size_t) -1 ? arg[i] == '\0' : i == argsize);  i++)
+  for (i = 0;  ! (argsize == SIZE_MAX ? arg[i] == '\0' : i == argsize);  i++)
     {
       unsigned char c;
       unsigned char esc;
@@ -293,6 +269,16 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
       c = arg[i];
       switch (c)
        {
+       case '\0':
+         if (backslash_escapes)
+           {
+             STORE ('\\');
+             STORE ('0');
+             STORE ('0');
+             c = '0';
+           }
+         break;
+
        case '?':
          switch (quoting_style)
            {
@@ -308,8 +294,8 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
                  case '<': case '=': case '>':
                    /* Escape the second '?' in what would otherwise be
                       a trigraph.  */
-                   i += 2;
                    c = arg[i + 2];
+                   i += 2;
                    STORE ('?');
                    STORE ('\\');
                    STORE ('?');
@@ -322,7 +308,7 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
            }
          break;
 
-       case ALERT_CHAR: esc = 'a'; goto c_escape;
+       case '\a': esc = 'a'; goto c_escape;
        case '\b': esc = 'b'; goto c_escape;
        case '\f': esc = 'f'; goto c_escape;
        case '\n': esc = 'n'; goto c_and_shell_escape;
@@ -342,6 +328,10 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
            }
          break;
 
+       case '{': case '}': /* sometimes special if isolated */
+         if (! (argsize == SIZE_MAX ? arg[1] == '\0' : argsize == 1))
+           break;
+         /* Fall through.  */
        case '#': case '~':
          if (i != 0)
            break;
@@ -350,7 +340,9 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
        case '!': /* special in bash */
        case '"': case '$': case '&':
        case '(': case ')': case '*': case ';':
-       case '<': case '>': case '[':
+       case '<':
+       case '=': /* sometimes special in 0th or (with "set -k") later args */
+       case '>': case '[':
        case '^': /* special in old /bin/sh, e.g. SunOS 4.1.4 */
        case '`': case '|':
          /* A shell special character.  In theory, '$' and '`' could
@@ -380,7 +372,7 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
 
        case '%': case '+': case ',': case '-': case '.': case '/':
        case '0': case '1': case '2': case '3': case '4': case '5':
-       case '6': case '7': case '8': case '9': case ':': case '=':
+       case '6': case '7': case '8': case '9': case ':':
        case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
        case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
        case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
@@ -390,7 +382,6 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
        case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
        case 'o': case 'p': case 'q': case 'r': case 's': case 't':
        case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
-       case '{': case '}':
          /* These characters don't cause problems, no matter what the
             quoting style is.  They cannot start multibyte sequences.  */
          break;
@@ -410,7 +401,7 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
            if (unibyte_locale)
              {
                m = 1;
-               printable = ISPRINT (c);
+               printable = isprint (c);
              }
            else
              {
@@ -419,7 +410,7 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
 
                m = 0;
                printable = 1;
-               if (argsize == (size_t) -1)
+               if (argsize == SIZE_MAX)
                  argsize = strlen (arg);
 
                do
@@ -443,6 +434,22 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
                      }
                    else
                      {
+                       /* Work around a bug with older shells that "see" a '\'
+                          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)
+                         {
+                           size_t j;
+                           for (j = 1; j < bytes; j++)
+                             switch (arg[i + m + j])
+                               {
+                               case '[': case '\\': case '^':
+                               case '`': case '|':
+                                 goto use_shell_always_quoting_style;
+                               }
+                         }
+                           
                        if (! iswprint (w))
                          printable = 0;
                        m += bytes;
@@ -488,6 +495,9 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
       STORE (c);
     }
 
+  if (i == 0 && quoting_style == shell_quoting_style)
+    goto use_shell_always_quoting_style;
+
   if (quote_string)
     for (; *quote_string; quote_string++)
       STORE (*quote_string);
@@ -508,31 +518,54 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize,
    size of the output, not counting the terminating null.
    If BUFFERSIZE is too small to store the output string, return the
    value that would have been returned had BUFFERSIZE been large enough.
-   If ARGSIZE is -1, use the string length of the argument for ARGSIZE.  */
+   If ARGSIZE is SIZE_MAX, use the string length of the argument for
+   ARGSIZE.  */
 size_t
 quotearg_buffer (char *buffer, size_t buffersize,
                 char const *arg, size_t argsize,
                 struct quoting_options const *o)
 {
   struct quoting_options const *p = o ? o : &default_quoting_options;
-  return quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
-                                  p->style, p);
+  int e = errno;
+  size_t r = quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
+                                      p->style, p);
+  errno = e;
+  return r;
+}
+
+/* Like quotearg_buffer (..., ARG, ARGSIZE, O), except return newly
+   allocated storage containing the quoted string.  */
+char *
+quotearg_alloc (char const *arg, size_t argsize,
+               struct quoting_options const *o)
+{
+  int e = errno;
+  size_t bufsize = quotearg_buffer (0, 0, arg, argsize, o) + 1;
+  char *buf = xmalloc (bufsize);
+  quotearg_buffer (buf, bufsize, arg, argsize, o);
+  errno = e;
+  return buf;
 }
 
-/* Use storage slot N to return a quoted version of the string ARG.
+/* Use storage slot N to return a quoted version of argument ARG.
+   ARG is of size ARGSIZE, but if that is SIZE_MAX, ARG is a
+   null-terminated string.
    OPTIONS specifies the quoting options.
    The returned value points to static storage that can be
    reused by the next call to this function with the same value of N.
    N must be nonnegative.  N is deliberately declared with type "int"
    to allow for future extensions (using negative values).  */
 static char *
-quotearg_n_options (int n, char const *arg,
+quotearg_n_options (int n, char const *arg, size_t argsize,
                    struct quoting_options const *options)
 {
+  int e = errno;
+
   /* Preallocate a slot 0 buffer, so that the caller can always quote
      one small component of a "memory exhausted" message in slot 0.  */
   static char slot0[256];
   static unsigned int nslots = 1;
+  unsigned int n0 = n;
   struct slotvec
     {
       size_t size;
@@ -541,42 +574,49 @@ quotearg_n_options (int n, char const *arg,
   static struct slotvec slotvec0 = {sizeof slot0, slot0};
   static struct slotvec *slotvec = &slotvec0;
 
-  if (nslots <= n)
+  if (n < 0)
+    abort ();
+
+  if (nslots <= n0)
     {
-      int n1 = n + 1;
-      size_t s = n1 * sizeof (struct slotvec);
-      if (! (0 < n1 && n1 == s / sizeof (struct slotvec)))
-       abort ();
+      unsigned int n1 = n0 + 1;
+
+      if (xalloc_oversized (n1, sizeof *slotvec))
+       xalloc_die ();
+
       if (slotvec == &slotvec0)
        {
-         slotvec = (struct slotvec *) xmalloc (sizeof (struct slotvec));
+         slotvec = xmalloc (sizeof *slotvec);
          *slotvec = slotvec0;
        }
-      slotvec = (struct slotvec *) xrealloc (slotvec, s);
-      memset (slotvec + nslots, 0, (n1 - nslots) * sizeof (struct slotvec));
-      nslots = n;
+      slotvec = xrealloc (slotvec, n1 * sizeof *slotvec);
+      memset (slotvec + nslots, 0, (n1 - nslots) * sizeof *slotvec);
+      nslots = n1;
     }
 
   {
     size_t size = slotvec[n].size;
     char *val = slotvec[n].val;
-    size_t qsize = quotearg_buffer (val, size, arg, (size_t) -1, options);
+    size_t qsize = quotearg_buffer (val, size, arg, argsize, options);
 
     if (size <= qsize)
       {
        slotvec[n].size = size = qsize + 1;
-       slotvec[n].val = val = xrealloc (val == slot0 ? 0 : val, size);
-       quotearg_buffer (val, size, arg, (size_t) -1, options);
+       if (val != slot0)
+         free (val);
+       slotvec[n].val = val = xmalloc (size);
+       quotearg_buffer (val, size, arg, argsize, options);
       }
 
+    errno = e;
     return val;
   }
 }
 
 char *
-quotearg_n (unsigned int n, char const *arg)
+quotearg_n (int n, char const *arg)
 {
-  return quotearg_n_options (n, arg, &default_quoting_options);
+  return quotearg_n_options (n, arg, SIZE_MAX, &default_quoting_options);
 }
 
 char *
@@ -585,13 +625,29 @@ quotearg (char const *arg)
   return quotearg_n (0, arg);
 }
 
-char *
-quotearg_n_style (unsigned int n, enum quoting_style s, char const *arg)
+/* 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 = s;
+  o.style = style;
   memset (o.quote_these_too, 0, sizeof o.quote_these_too);
-  return quotearg_n_options (n, arg, &o);
+  return o;
+}
+
+char *
+quotearg_n_style (int n, enum quoting_style s, char const *arg)
+{
+  struct quoting_options const o = quoting_options_from_style (s);
+  return quotearg_n_options (n, arg, SIZE_MAX, &o);
+}
+
+char *
+quotearg_n_style_mem (int n, enum quoting_style s,
+                     char const *arg, size_t argsize)
+{
+  struct quoting_options const o = quoting_options_from_style (s);
+  return quotearg_n_options (n, arg, argsize, &o);
 }
 
 char *
@@ -606,7 +662,7 @@ quotearg_char (char const *arg, char ch)
   struct quoting_options options;
   options = default_quoting_options;
   set_char_quoting (&options, ch, 1);
-  return quotearg_n_options (0, arg, &options);
+  return quotearg_n_options (0, arg, SIZE_MAX, &options);
 }
 
 char *