filter-branch: use $SHELL_PATH instead of 'sh'
[gitweb.git] / quote.c
diff --git a/quote.c b/quote.c
index 7771c9c678d1b1508ef2d582eb218e57e3caeec1..d5cf9d8f94f37fe8ff9f964998c7f0525617e5bc 100644 (file)
--- a/quote.c
+++ b/quote.c
  *  a'b      ==> a'\''b    ==> 'a'\''b'
  *  a!b      ==> a'\!'b    ==> 'a'\!'b'
  */
-#undef EMIT
-#define EMIT(x) do { if (++len < n) *bp++ = (x); } while(0)
-
 static inline int need_bs_quote(char c)
 {
        return (c == '\'' || c == '!');
 }
 
-static size_t sq_quote_buf(char *dst, size_t n, const char *src)
+void sq_quote_buf(struct strbuf *dst, const char *src)
 {
-       char c;
-       char *bp = dst;
-       size_t len = 0;
-
-       EMIT('\'');
-       while ((c = *src++)) {
-               if (need_bs_quote(c)) {
-                       EMIT('\'');
-                       EMIT('\\');
-                       EMIT(c);
-                       EMIT('\'');
-               } else {
-                       EMIT(c);
+       char *to_free = NULL;
+
+       if (dst->buf == src)
+               to_free = strbuf_detach(dst, NULL);
+
+       strbuf_addch(dst, '\'');
+       while (*src) {
+               size_t len = strcspn(src, "'!");
+               strbuf_add(dst, src, len);
+               src += len;
+               while (need_bs_quote(*src)) {
+                       strbuf_addstr(dst, "'\\");
+                       strbuf_addch(dst, *src++);
+                       strbuf_addch(dst, '\'');
                }
        }
-       EMIT('\'');
-
-       if ( n )
-               *bp = 0;
-
-       return len;
+       strbuf_addch(dst, '\'');
+       free(to_free);
 }
 
 void sq_quote_print(FILE *stream, const char *src)
@@ -62,65 +56,18 @@ void sq_quote_print(FILE *stream, const char *src)
        fputc('\'', stream);
 }
 
-char *sq_quote_argv(const char** argv, int count)
+void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
 {
-       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);
+       strbuf_grow(dst, 255);
+       for (i = 0; argv[i]; ++i) {
+               strbuf_addch(dst, ' ');
+               sq_quote_buf(dst, argv[i]);
+               if (maxlen && dst->len > maxlen)
+                       die("Too many or long arguments");
        }
-
-       if (size <= oc) {
-               err = 1;
-               oc = size - 1;
-       }
-
-       *ptrp += oc;
-       **ptrp = '\0';
-       *sizep -= oc;
-       return err;
 }
 
 char *sq_dequote(char *arg)
@@ -157,83 +104,202 @@ char *sq_dequote(char *arg)
        }
 }
 
+/* 1 means: quote as octal
+ * 0 means: quote as octal if (quote_path_fully)
+ * -1 means: never quote
+ * c: quote as "\\c"
+ */
+#define X8(x)   x, x, x, x, x, x, x, x
+#define X16(x)  X8(x), X8(x)
+static signed char const sq_lookup[256] = {
+       /*           0    1    2    3    4    5    6    7 */
+       /* 0x00 */   1,   1,   1,   1,   1,   1,   1, 'a',
+       /* 0x08 */ 'b', 't', 'n', 'v', 'f', 'r',   1,   1,
+       /* 0x10 */ X16(1),
+       /* 0x20 */  -1,  -1, '"',  -1,  -1,  -1,  -1,  -1,
+       /* 0x28 */ X16(-1), X16(-1), X16(-1),
+       /* 0x58 */  -1,  -1,  -1,  -1,'\\',  -1,  -1,  -1,
+       /* 0x60 */ X16(-1), X8(-1),
+       /* 0x78 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,   1,
+       /* 0x80 */ /* set to 0 */
+};
+
+static inline int sq_must_quote(char c)
+{
+       return sq_lookup[(unsigned char)c] + quote_path_fully > 0;
+}
+
+/* returns the longest prefix not needing a quote up to maxlen if positive.
+   This stops at the first \0 because it's marked as a character needing an
+   escape */
+static size_t next_quote_pos(const char *s, ssize_t maxlen)
+{
+       size_t len;
+       if (maxlen < 0) {
+               for (len = 0; !sq_must_quote(s[len]); len++);
+       } else {
+               for (len = 0; len < maxlen && !sq_must_quote(s[len]); len++);
+       }
+       return len;
+}
+
 /*
  * C-style name quoting.
  *
- * Does one of three things:
- *
- * (1) if outbuf and outfp are both NULL, inspect the input name and
- *     counts the number of bytes that are needed to hold c_style
- *     quoted version of name, counting the double quotes around
- *     it but not terminating NUL, and returns it.  However, if name
- *     does not need c_style quoting, it returns 0.
+ * (1) if sb and fp are both NULL, inspect the input name and counts the
+ *     number of bytes that are needed to hold c_style quoted version of name,
+ *     counting the double quotes around it but not terminating NUL, and
+ *     returns it.
+ *     However, if name does not need c_style quoting, it returns 0.
  *
- * (2) if outbuf is not NULL, it must point at a buffer large enough
- *     to hold the c_style quoted version of name, enclosing double
- *     quotes, and terminating NUL.  Fills outbuf with c_style quoted
- *     version of name enclosed in double-quote pair.  Return value
- *     is undefined.
- *
- * (3) if outfp is not NULL, outputs c_style quoted version of name,
- *     but not enclosed in double-quote pair.  Return value is undefined.
+ * (2) if sb or fp are not NULL, it emits the c_style quoted version
+ *     of name, enclosed with double quotes if asked and needed only.
+ *     Return value is the same as in (1).
  */
-
-static int quote_c_style_counted(const char *name, int namelen,
-                                char *outbuf, FILE *outfp, int no_dq)
+static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
+                                    struct strbuf *sb, FILE *fp, int no_dq)
 {
 #undef EMIT
-#define EMIT(c) \
-       (outbuf ? (*outbuf++ = (c)) : outfp ? fputc(c, outfp) : (count++))
-
-#define EMITQ() EMIT('\\')
+#define EMIT(c)                                 \
+       do {                                        \
+               if (sb) strbuf_addch(sb, (c));          \
+               if (fp) fputc((c), fp);                 \
+               count++;                                \
+       } while (0)
+#define EMITBUF(s, l)                           \
+       do {                                        \
+               if (sb) strbuf_add(sb, (s), (l));       \
+               if (fp) fwrite((s), (l), 1, fp);        \
+               count += (l);                           \
+       } while (0)
+
+       size_t len, count = 0;
+       const char *p = name;
 
-       const char *sp;
-       unsigned char ch;
-       int count = 0, needquote = 0;
+       for (;;) {
+               int ch;
 
-       if (!no_dq)
-               EMIT('"');
-       for (sp = name; sp < name + namelen; sp++) {
-               ch = *sp;
-               if (!ch)
+               len = next_quote_pos(p, maxlen);
+               if (len == maxlen || !p[len])
                        break;
-               if ((ch < ' ') || (ch == '"') || (ch == '\\') ||
-                   (quote_path_fully && (ch >= 0177))) {
-                       needquote = 1;
-                       switch (ch) {
-                       case '\a': EMITQ(); ch = 'a'; break;
-                       case '\b': EMITQ(); ch = 'b'; break;
-                       case '\f': EMITQ(); ch = 'f'; break;
-                       case '\n': EMITQ(); ch = 'n'; break;
-                       case '\r': EMITQ(); ch = 'r'; break;
-                       case '\t': EMITQ(); ch = 't'; break;
-                       case '\v': EMITQ(); ch = 'v'; break;
-
-                       case '\\': /* fallthru */
-                       case '"': EMITQ(); break;
-                       default:
-                               /* octal */
-                               EMITQ();
-                               EMIT(((ch >> 6) & 03) + '0');
-                               EMIT(((ch >> 3) & 07) + '0');
-                               ch = (ch & 07) + '0';
-                               break;
-                       }
+
+               if (!no_dq && p == name)
+                       EMIT('"');
+
+               EMITBUF(p, len);
+               EMIT('\\');
+               p += len;
+               ch = (unsigned char)*p++;
+               if (sq_lookup[ch] >= ' ') {
+                       EMIT(sq_lookup[ch]);
+               } else {
+                       EMIT(((ch >> 6) & 03) + '0');
+                       EMIT(((ch >> 3) & 07) + '0');
+                       EMIT(((ch >> 0) & 07) + '0');
                }
-               EMIT(ch);
        }
+
+       EMITBUF(p, len);
+       if (p == name)   /* no ending quote needed */
+               return 0;
+
        if (!no_dq)
                EMIT('"');
-       if (outbuf)
-               *outbuf = 0;
+       return count;
+}
+
+size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, int nodq)
+{
+       return quote_c_style_counted(name, -1, sb, fp, nodq);
+}
 
-       return needquote ? count : 0;
+void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path, int nodq)
+{
+       if (quote_c_style(prefix, NULL, NULL, 0) ||
+           quote_c_style(path, NULL, NULL, 0)) {
+               if (!nodq)
+                       strbuf_addch(sb, '"');
+               quote_c_style(prefix, sb, NULL, 1);
+               quote_c_style(path, sb, NULL, 1);
+               if (!nodq)
+                       strbuf_addch(sb, '"');
+       } else {
+               strbuf_addstr(sb, prefix);
+               strbuf_addstr(sb, path);
+       }
 }
 
-int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
+void write_name_quoted(const char *name, FILE *fp, int terminator)
 {
-       int cnt = strlen(name);
-       return quote_c_style_counted(name, cnt, outbuf, outfp, no_dq);
+       if (terminator) {
+               quote_c_style(name, NULL, fp, 0);
+       } else {
+               fputs(name, fp);
+       }
+       fputc(terminator, fp);
+}
+
+extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
+                                 const char *name, FILE *fp, int terminator)
+{
+       int needquote = 0;
+
+       if (terminator) {
+               needquote = next_quote_pos(pfx, pfxlen) < pfxlen
+                       || name[next_quote_pos(name, -1)];
+       }
+       if (needquote) {
+               fputc('"', fp);
+               quote_c_style_counted(pfx, pfxlen, NULL, fp, 1);
+               quote_c_style(name, NULL, fp, 1);
+               fputc('"', fp);
+       } else {
+               fwrite(pfx, pfxlen, 1, fp);
+               fputs(name, fp);
+       }
+       fputc(terminator, fp);
+}
+
+/* quote path as relative to the given prefix */
+char *quote_path_relative(const char *in, int len,
+                         struct strbuf *out, const char *prefix)
+{
+       int needquote;
+
+       if (len < 0)
+               len = strlen(in);
+
+       /* "../" prefix itself does not need quoting, but "in" might. */
+       needquote = next_quote_pos(in, len) < len;
+       strbuf_setlen(out, 0);
+       strbuf_grow(out, len);
+
+       if (needquote)
+               strbuf_addch(out, '"');
+       if (prefix) {
+               int off = 0;
+               while (prefix[off] && off < len && prefix[off] == in[off])
+                       if (prefix[off] == '/') {
+                               prefix += off + 1;
+                               in += off + 1;
+                               len -= off + 1;
+                               off = 0;
+                       } else
+                               off++;
+
+               for (; *prefix; prefix++)
+                       if (*prefix == '/')
+                               strbuf_addstr(out, "../");
+       }
+
+       quote_c_style_counted (in, len, out, NULL, 1);
+
+       if (needquote)
+               strbuf_addch(out, '"');
+       if (!out->len)
+               strbuf_addstr(out, "./");
+
+       return out->buf;
 }
 
 /*
@@ -264,7 +330,7 @@ int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
                switch (*quoted++) {
                  case '"':
                        if (endp)
-                               *endp = quoted + 1;
+                               *endp = quoted;
                        return 0;
                  case '\\':
                        break;
@@ -306,37 +372,6 @@ int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
        return -1;
 }
 
-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_len)
-                       fprintf(out, "%.*s", prefix_len, prefix);
-               fputs(name, out);
-               return;
-       }
-
-       needquote = 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_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)