Imported Upstream version 2.29.0
[platform/upstream/git.git] / quote.c
diff --git a/quote.c b/quote.c
index 911229f..69f4ca4 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -1,12 +1,18 @@
 #include "cache.h"
 #include "quote.h"
-#include "argv-array.h"
+#include "strvec.h"
 
 int quote_path_fully = 1;
 
+static inline int need_bs_quote(char c)
+{
+       return (c == '\'' || c == '!');
+}
+
 /* Help to copy the thing properly quoted for the shell safety.
  * any single quote is replaced with '\'', any exclamation point
  * is replaced with '\!', and the whole thing is enclosed in a
+ * single quote pair.
  *
  * E.g.
  *  original     sq_quote     result
@@ -15,11 +21,6 @@ int quote_path_fully = 1;
  *  a'b      ==> a'\''b    ==> 'a'\''b'
  *  a!b      ==> a'\!'b    ==> 'a'\!'b'
  */
-static inline int need_bs_quote(char c)
-{
-       return (c == '\'' || c == '!');
-}
-
 void sq_quote_buf(struct strbuf *dst, const char *src)
 {
        char *to_free = NULL;
@@ -42,24 +43,42 @@ void sq_quote_buf(struct strbuf *dst, const char *src)
        free(to_free);
 }
 
-void sq_quote_print(FILE *stream, const char *src)
+void sq_quote_buf_pretty(struct strbuf *dst, const char *src)
 {
-       char c;
+       static const char ok_punct[] = "+,-./:=@_^";
+       const char *p;
 
-       fputc('\'', stream);
-       while ((c = *src++)) {
-               if (need_bs_quote(c)) {
-                       fputs("'\\", stream);
-                       fputc(c, stream);
-                       fputc('\'', stream);
-               } else {
-                       fputc(c, stream);
+       /* Avoid losing a zero-length string by adding '' */
+       if (!*src) {
+               strbuf_addstr(dst, "''");
+               return;
+       }
+
+       for (p = src; *p; p++) {
+               if (!isalnum(*p) && !strchr(ok_punct, *p)) {
+                       sq_quote_buf(dst, src);
+                       return;
                }
        }
-       fputc('\'', stream);
+
+       /* if we get here, we did not need quoting */
+       strbuf_addstr(dst, src);
+}
+
+void sq_quotef(struct strbuf *dst, const char *fmt, ...)
+{
+       struct strbuf src = STRBUF_INIT;
+
+       va_list ap;
+       va_start(ap, fmt);
+       strbuf_vaddf(&src, fmt, ap);
+       va_end(ap);
+
+       sq_quote_buf(dst, src.buf);
+       strbuf_release(&src);
 }
 
-void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
+void sq_quote_argv(struct strbuf *dst, const char **argv)
 {
        int i;
 
@@ -68,8 +87,32 @@ void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
        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");
+       }
+}
+
+/*
+ * Legacy function to append each argv value, quoted as necessasry,
+ * with whitespace before each value.  This results in a leading
+ * space in the result.
+ */
+void sq_quote_argv_pretty(struct strbuf *dst, const char **argv)
+{
+       if (argv[0])
+               strbuf_addch(dst, ' ');
+       sq_append_quote_argv_pretty(dst, argv);
+}
+
+/*
+ * Append each argv value, quoted as necessary, with whitespace between them.
+ */
+void sq_append_quote_argv_pretty(struct strbuf *dst, const char **argv)
+{
+       int i;
+
+       for (i = 0; argv[i]; i++) {
+               if (i > 0)
+                       strbuf_addch(dst, ' ');
+               sq_quote_buf_pretty(dst, argv[i]);
        }
 }
 
@@ -97,9 +140,15 @@ static char *sq_dequote_step(char *arg, char **next)
                                *next = NULL;
                        return arg;
                case '\\':
-                       c = *++src;
-                       if (need_bs_quote(c) && *++src == '\'') {
-                               *dst++ = c;
+                       /*
+                        * Allow backslashed characters outside of
+                        * single-quotes only if they need escaping,
+                        * and only if we resume the single-quoted part
+                        * afterward.
+                        */
+                       if (need_bs_quote(src[1]) && src[2] == '\'') {
+                               *dst++ = src[1];
+                               src += 2;
                                continue;
                        }
                /* Fallthrough */
@@ -123,7 +172,7 @@ char *sq_dequote(char *arg)
 
 static int sq_dequote_to_argv_internal(char *arg,
                                       const char ***argv, int *nr, int *alloc,
-                                      struct argv_array *array)
+                                      struct strvec *array)
 {
        char *next = arg;
 
@@ -138,7 +187,7 @@ static int sq_dequote_to_argv_internal(char *arg,
                        (*argv)[(*nr)++] = dequoted;
                }
                if (array)
-                       argv_array_push(array, dequoted);
+                       strvec_push(array, dequoted);
        } while (next);
 
        return 0;
@@ -149,7 +198,7 @@ int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
        return sq_dequote_to_argv_internal(arg, argv, nr, alloc, NULL);
 }
 
-int sq_dequote_to_argv_array(char *arg, struct argv_array *array)
+int sq_dequote_to_strvec(char *arg, struct strvec *array)
 {
        return sq_dequote_to_argv_internal(arg, NULL, NULL, NULL, array);
 }
@@ -161,7 +210,7 @@ int sq_dequote_to_argv_array(char *arg, struct argv_array *array)
  */
 #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] = {
+static signed char const cq_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,
@@ -174,9 +223,9 @@ static signed char const sq_lookup[256] = {
        /* 0x80 */ /* set to 0 */
 };
 
-static inline int sq_must_quote(char c)
+static inline int cq_must_quote(char c)
 {
-       return sq_lookup[(unsigned char)c] + quote_path_fully > 0;
+       return cq_lookup[(unsigned char)c] + quote_path_fully > 0;
 }
 
 /* returns the longest prefix not needing a quote up to maxlen if positive.
@@ -186,9 +235,9 @@ 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++);
+               for (len = 0; !cq_must_quote(s[len]); len++);
        } else {
-               for (len = 0; len < maxlen && !sq_must_quote(s[len]); len++);
+               for (len = 0; len < maxlen && !cq_must_quote(s[len]); len++);
        }
        return len;
 }
@@ -207,7 +256,7 @@ static size_t next_quote_pos(const char *s, ssize_t maxlen)
  *     Return value is the same as in (1).
  */
 static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
-                                    struct strbuf *sb, FILE *fp, int no_dq)
+                                   struct strbuf *sb, FILE *fp, unsigned flags)
 {
 #undef EMIT
 #define EMIT(c)                                 \
@@ -223,6 +272,7 @@ static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
                count += (l);                           \
        } while (0)
 
+       int no_dq = !!(flags & CQUOTE_NODQ);
        size_t len, count = 0;
        const char *p = name;
 
@@ -242,8 +292,8 @@ static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
                ch = (unsigned char)*p++;
                if (maxlen >= 0)
                        maxlen -= len + 1;
-               if (sq_lookup[ch] >= ' ') {
-                       EMIT(sq_lookup[ch]);
+               if (cq_lookup[ch] >= ' ') {
+                       EMIT(cq_lookup[ch]);
                } else {
                        EMIT(((ch >> 6) & 03) + '0');
                        EMIT(((ch >> 3) & 07) + '0');
@@ -260,19 +310,21 @@ static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
        return count;
 }
 
-size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, int nodq)
+size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, unsigned flags)
 {
-       return quote_c_style_counted(name, -1, sb, fp, nodq);
+       return quote_c_style_counted(name, -1, sb, fp, flags);
 }
 
-void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path, int nodq)
+void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path,
+                      unsigned flags)
 {
+       int nodq = !!(flags & CQUOTE_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);
+               quote_c_style(prefix, sb, NULL, CQUOTE_NODQ);
+               quote_c_style(path, sb, NULL, CQUOTE_NODQ);
                if (!nodq)
                        strbuf_addch(sb, '"');
        } else {
@@ -291,102 +343,38 @@ void write_name_quoted(const char *name, FILE *fp, int terminator)
        fputc(terminator, fp);
 }
 
-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);
-}
-
-static const char *path_relative(const char *in, int len,
-                                struct strbuf *sb, const char *prefix,
-                                int prefix_len);
-
-void write_name_quoted_relative(const char *name, size_t len,
-                               const char *prefix, size_t prefix_len,
+void write_name_quoted_relative(const char *name, const char *prefix,
                                FILE *fp, int terminator)
 {
        struct strbuf sb = STRBUF_INIT;
 
-       name = path_relative(name, len, &sb, prefix, prefix_len);
+       name = relative_path(name, prefix, &sb);
        write_name_quoted(name, fp, terminator);
 
        strbuf_release(&sb);
 }
 
-/*
- * Give path as relative to prefix.
- *
- * The strbuf may or may not be used, so do not assume it contains the
- * returned path.
- */
-static const char *path_relative(const char *in, int len,
-                                struct strbuf *sb, const char *prefix,
-                                int prefix_len)
-{
-       int off, i;
-
-       if (len < 0)
-               len = strlen(in);
-       if (prefix_len < 0) {
-               if (prefix)
-                       prefix_len = strlen(prefix);
-               else
-                       prefix_len = 0;
-       }
-
-       off = 0;
-       i = 0;
-       while (i < prefix_len && i < len && prefix[i] == in[i]) {
-               if (prefix[i] == '/')
-                       off = i + 1;
-               i++;
-       }
-       in += off;
-       len -= off;
-
-       if (i >= prefix_len)
-               return in;
-
-       strbuf_reset(sb);
-       strbuf_grow(sb, len);
-
-       while (i < prefix_len) {
-               if (prefix[i] == '/')
-                       strbuf_addstr(sb, "../");
-               i++;
-       }
-       strbuf_add(sb, in, len);
-
-       return sb->buf;
-}
-
 /* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, int len,
-                         struct strbuf *out, const char *prefix)
+char *quote_path(const char *in, const char *prefix, struct strbuf *out, unsigned flags)
 {
        struct strbuf sb = STRBUF_INIT;
-       const char *rel = path_relative(in, len, &sb, prefix, -1);
+       const char *rel = relative_path(in, prefix, &sb);
+       int force_dq = ((flags & QUOTE_PATH_QUOTE_SP) && strchr(rel, ' '));
+
        strbuf_reset(out);
-       quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
-       strbuf_release(&sb);
 
-       if (!out->len)
-               strbuf_addstr(out, "./");
+       /*
+        * If the caller wants us to enclose the output in a dq-pair
+        * whether quote_c_style_counted() needs to, we do it ourselves
+        * and tell quote_c_style_counted() not to.
+        */
+       if (force_dq)
+               strbuf_addch(out, '"');
+       quote_c_style_counted(rel, strlen(rel), out, NULL,
+                             force_dq ? CQUOTE_NODQ : 0);
+       if (force_dq)
+               strbuf_addch(out, '"');
+       strbuf_release(&sb);
 
        return out->buf;
 }
@@ -463,72 +451,110 @@ int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
 
 /* quoting as a string literal for other languages */
 
-void perl_quote_print(FILE *stream, const char *src)
+void perl_quote_buf(struct strbuf *sb, const char *src)
 {
        const char sq = '\'';
        const char bq = '\\';
        char c;
 
-       fputc(sq, stream);
+       strbuf_addch(sb, sq);
        while ((c = *src++)) {
                if (c == sq || c == bq)
-                       fputc(bq, stream);
-               fputc(c, stream);
+                       strbuf_addch(sb, bq);
+               strbuf_addch(sb, c);
        }
-       fputc(sq, stream);
+       strbuf_addch(sb, sq);
 }
 
-void python_quote_print(FILE *stream, const char *src)
+void python_quote_buf(struct strbuf *sb, const char *src)
 {
        const char sq = '\'';
        const char bq = '\\';
        const char nl = '\n';
        char c;
 
-       fputc(sq, stream);
+       strbuf_addch(sb, sq);
        while ((c = *src++)) {
                if (c == nl) {
-                       fputc(bq, stream);
-                       fputc('n', stream);
+                       strbuf_addch(sb, bq);
+                       strbuf_addch(sb, 'n');
                        continue;
                }
                if (c == sq || c == bq)
-                       fputc(bq, stream);
-               fputc(c, stream);
+                       strbuf_addch(sb, bq);
+               strbuf_addch(sb, c);
        }
-       fputc(sq, stream);
+       strbuf_addch(sb, sq);
 }
 
-void tcl_quote_print(FILE *stream, const char *src)
+void tcl_quote_buf(struct strbuf *sb, const char *src)
 {
        char c;
 
-       fputc('"', stream);
+       strbuf_addch(sb, '"');
        while ((c = *src++)) {
                switch (c) {
                case '[': case ']':
                case '{': case '}':
                case '$': case '\\': case '"':
-                       fputc('\\', stream);
+                       strbuf_addch(sb, '\\');
+                       /* fallthrough */
                default:
-                       fputc(c, stream);
+                       strbuf_addch(sb, c);
                        break;
                case '\f':
-                       fputs("\\f", stream);
+                       strbuf_addstr(sb, "\\f");
                        break;
                case '\r':
-                       fputs("\\r", stream);
+                       strbuf_addstr(sb, "\\r");
                        break;
                case '\n':
-                       fputs("\\n", stream);
+                       strbuf_addstr(sb, "\\n");
                        break;
                case '\t':
-                       fputs("\\t", stream);
+                       strbuf_addstr(sb, "\\t");
                        break;
                case '\v':
-                       fputs("\\v", stream);
+                       strbuf_addstr(sb, "\\v");
+                       break;
+               }
+       }
+       strbuf_addch(sb, '"');
+}
+
+void basic_regex_quote_buf(struct strbuf *sb, const char *src)
+{
+       char c;
+
+       if (*src == '^') {
+               /* only beginning '^' is special and needs quoting */
+               strbuf_addch(sb, '\\');
+               strbuf_addch(sb, *src++);
+       }
+       if (*src == '*')
+               /* beginning '*' is not special, no quoting */
+               strbuf_addch(sb, *src++);
+
+       while ((c = *src++)) {
+               switch (c) {
+               case '[':
+               case '.':
+               case '\\':
+               case '*':
+                       strbuf_addch(sb, '\\');
+                       strbuf_addch(sb, c);
+                       break;
+
+               case '$':
+                       /* only the end '$' is special and needs quoting */
+                       if (*src == '\0')
+                               strbuf_addch(sb, '\\');
+                       strbuf_addch(sb, c);
+                       break;
+
+               default:
+                       strbuf_addch(sb, c);
                        break;
                }
        }
-       fputc('"', stream);
 }