xsize_t: check whether we lose bits
[gitweb.git] / quote.c
diff --git a/quote.c b/quote.c
index 357f81a740aaed18632023b2f29137773e58eaf7..fb9e4ca253ea9bcadbcb55dcdd62be614c66758f 100644 (file)
--- a/quote.c
+++ b/quote.c
 #include "quote.h"
 
 /* Help to copy the thing properly quoted for the shell safety.
- * any single quote is replaced with '\'', and the caller is
- * expected to enclose the result within a single quote pair.
+ * any single quote is replaced with '\'', any exclamation point
+ * is replaced with '\!', and the whole thing is enclosed in a
  *
  * E.g.
  *  original     sq_quote     result
  *  name     ==> name      ==> 'name'
  *  a b      ==> a b       ==> 'a b'
  *  a'b      ==> a'\''b    ==> 'a'\''b'
+ *  a!b      ==> a'\!'b    ==> 'a'\!'b'
  */
-char *sq_quote(const char *src)
+#undef EMIT
+#define EMIT(x) do { if (++len < n) *bp++ = (x); } while(0)
+
+static inline int need_bs_quote(char c)
 {
-       static char *buf = NULL;
-       int cnt, c;
-       const char *cp;
-       char *bp;
+       return (c == '\'' || c == '!');
+}
 
-       /* count bytes needed to store the quoted string. */
-       for (cnt = 3, cp = src; *cp; cnt++, cp++)
-               if (*cp == '\'')
-                       cnt += 3;
+size_t sq_quote_buf(char *dst, size_t n, const char *src)
+{
+       char c;
+       char *bp = dst;
+       size_t len = 0;
 
-       buf = xmalloc(cnt);
-       bp = buf;
-       *bp++ = '\'';
+       EMIT('\'');
        while ((c = *src++)) {
-               if (c != '\'')
-                       *bp++ = c;
-               else {
-                       bp = strcpy(bp, "'\\''");
-                       bp += 4;
+               if (need_bs_quote(c)) {
+                       EMIT('\'');
+                       EMIT('\\');
+                       EMIT(c);
+                       EMIT('\'');
+               } else {
+                       EMIT(c);
                }
        }
-       *bp++ = '\'';
-       *bp = 0;
+       EMIT('\'');
+
+       if ( n )
+               *bp = 0;
+
+       return len;
+}
+
+void sq_quote_print(FILE *stream, const char *src)
+{
+       char c;
+
+       fputc('\'', stream);
+       while ((c = *src++)) {
+               if (need_bs_quote(c)) {
+                       fputs("'\\", stream);
+                       fputc(c, stream);
+                       fputc('\'', stream);
+               } else {
+                       fputc(c, stream);
+               }
+       }
+       fputc('\'', stream);
+}
+
+char *sq_quote(const char *src)
+{
+       char *buf;
+       size_t cnt;
+
+       cnt = sq_quote_buf(NULL, 0, src) + 1;
+       buf = xmalloc(cnt);
+       sq_quote_buf(buf, cnt, src);
+
+       return buf;
+}
+
+char *sq_quote_argv(const char** argv, int count)
+{
+       char *buf, *to;
+       int i;
+       size_t len = 0;
+
+       /* Count argv if needed. */
+       if (count < 0) {
+               for (count = 0; argv[count]; count++)
+                       ; /* just counting */
+       }
+
+       /* Special case: no argv. */
+       if (!count)
+               return xcalloc(1,1);
+
+       /* Get destination buffer length. */
+       for (i = 0; i < count; i++)
+               len += sq_quote_buf(NULL, 0, argv[i]) + 1;
+
+       /* Alloc destination buffer. */
+       to = buf = xmalloc(len + 1);
+
+       /* Copy into destination buffer. */
+       for (i = 0; i < count; ++i) {
+               *to++ = ' ';
+               to += sq_quote_buf(to, len, argv[i]);
+       }
+
        return buf;
 }
 
+/*
+ * Append a string to a string buffer, with or without shell quoting.
+ * Return true if the buffer overflowed.
+ */
+int add_to_string(char **ptrp, int *sizep, const char *str, int quote)
+{
+       char *p = *ptrp;
+       int size = *sizep;
+       int oc;
+       int err = 0;
+
+       if (quote)
+               oc = sq_quote_buf(p, size, str);
+       else {
+               oc = strlen(str);
+               memcpy(p, str, (size <= oc) ? size - 1 : oc);
+       }
+
+       if (size <= oc) {
+               err = 1;
+               oc = size - 1;
+       }
+
+       *ptrp += oc;
+       **ptrp = '\0';
+       *sizep -= oc;
+       return err;
+}
+
+char *sq_dequote(char *arg)
+{
+       char *dst = arg;
+       char *src = arg;
+       char c;
+
+       if (*src != '\'')
+               return NULL;
+       for (;;) {
+               c = *++src;
+               if (!c)
+                       return NULL;
+               if (c != '\'') {
+                       *dst++ = c;
+                       continue;
+               }
+               /* We stepped out of sq */
+               switch (*++src) {
+               case '\0':
+                       *dst = 0;
+                       return arg;
+               case '\\':
+                       c = *++src;
+                       if (need_bs_quote(c) && *++src == '\'') {
+                               *dst++ = c;
+                               continue;
+                       }
+               /* Fallthrough */
+               default:
+                       return NULL;
+               }
+       }
+}
+
 /*
  * C-style name quoting.
  *
@@ -60,7 +190,8 @@ char *sq_quote(const char *src)
  *     but not enclosed in double-quote pair.  Return value is undefined.
  */
 
-int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
+static int quote_c_style_counted(const char *name, int namelen,
+                                char *outbuf, FILE *outfp, int no_dq)
 {
 #undef EMIT
 #define EMIT(c) \
@@ -73,10 +204,12 @@ int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
 
        if (!no_dq)
                EMIT('"');
-       for (sp = name; (ch = *sp++); ) {
-
-               if ((ch <= ' ') || (ch == '"') ||
-                   (ch == '\\') || (ch == 0177)) {
+       for (sp = name; sp < name + namelen; sp++) {
+               ch = *sp;
+               if (!ch)
+                       break;
+               if ((ch < ' ') || (ch == '"') || (ch == '\\') ||
+                   (ch >= 0177)) {
                        needquote = 1;
                        switch (ch) {
                        case '\a': EMITQ(); ch = 'a'; break;
@@ -89,8 +222,6 @@ int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
 
                        case '\\': /* fallthru */
                        case '"': EMITQ(); break;
-                       case ' ':
-                               break;
                        default:
                                /* octal */
                                EMITQ();
@@ -110,6 +241,12 @@ int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
        return needquote ? count : 0;
 }
 
+int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
+{
+       int cnt = strlen(name);
+       return quote_c_style_counted(name, cnt, outbuf, outfp, no_dq);
+}
+
 /*
  * C-style name unquoting.
  *
@@ -147,7 +284,14 @@ char *unquote_c_style(const char *quoted, const char **endp)
                                case '\\': case '"':
                                        break; /* verbatim */
 
-                               case '0'...'7':
+                               case '0':
+                               case '1':
+                               case '2':
+                               case '3':
+                               case '4':
+                               case '5':
+                               case '6':
+                               case '7':
                                        /* octal */
                                        ac = ((ch - '0') << 6);
                                        if ((ch = *sp++) < '0' || '7' < ch)
@@ -175,31 +319,105 @@ char *unquote_c_style(const char *quoted, const char **endp)
        }
 }
 
-void write_name_quoted(const char *prefix, const char *name,
-                      int quote, FILE *out)
+void write_name_quoted(const char *prefix, int prefix_len,
+                      const char *name, int quote, FILE *out)
 {
        int needquote;
 
        if (!quote) {
        no_quote:
-               if (prefix && prefix[0])
-                       fputs(prefix, out);
+               if (prefix_len)
+                       fprintf(out, "%.*s", prefix_len, prefix);
                fputs(name, out);
                return;
        }
 
        needquote = 0;
-       if (prefix && prefix[0])
-               needquote = quote_c_style(prefix, NULL, NULL, 0);
+       if (prefix_len)
+               needquote = quote_c_style_counted(prefix, prefix_len,
+                                                 NULL, NULL, 0);
        if (!needquote)
                needquote = quote_c_style(name, NULL, NULL, 0);
        if (needquote) {
                fputc('"', out);
-               if (prefix && prefix[0])
-                       quote_c_style(prefix, NULL, out, 1);
+               if (prefix_len)
+                       quote_c_style_counted(prefix, prefix_len,
+                                             NULL, out, 1);
                quote_c_style(name, NULL, out, 1);
                fputc('"', out);
        }
        else
                goto no_quote;
 }
+
+/* quoting as a string literal for other languages */
+
+void perl_quote_print(FILE *stream, const char *src)
+{
+       const char sq = '\'';
+       const char bq = '\\';
+       char c;
+
+       fputc(sq, stream);
+       while ((c = *src++)) {
+               if (c == sq || c == bq)
+                       fputc(bq, stream);
+               fputc(c, stream);
+       }
+       fputc(sq, stream);
+}
+
+void python_quote_print(FILE *stream, const char *src)
+{
+       const char sq = '\'';
+       const char bq = '\\';
+       const char nl = '\n';
+       char c;
+
+       fputc(sq, stream);
+       while ((c = *src++)) {
+               if (c == nl) {
+                       fputc(bq, stream);
+                       fputc('n', stream);
+                       continue;
+               }
+               if (c == sq || c == bq)
+                       fputc(bq, stream);
+               fputc(c, stream);
+       }
+       fputc(sq, stream);
+}
+
+void tcl_quote_print(FILE *stream, const char *src)
+{
+       char c;
+
+       fputc('"', stream);
+       while ((c = *src++)) {
+               switch (c) {
+               case '[': case ']':
+               case '{': case '}':
+               case '$': case '\\': case '"':
+                       fputc('\\', stream);
+               default:
+                       fputc(c, stream);
+                       break;
+               case '\f':
+                       fputs("\\f", stream);
+                       break;
+               case '\r':
+                       fputs("\\r", stream);
+                       break;
+               case '\n':
+                       fputs("\\n", stream);
+                       break;
+               case '\t':
+                       fputs("\\t", stream);
+                       break;
+               case '\v':
+                       fputs("\\v", stream);
+                       break;
+               }
+       }
+       fputc('"', stream);
+}