quote.con commit Full rework of quote_c_style and write_name_quoted. (663af34)
   1#include "cache.h"
   2#include "quote.h"
   3
   4/* Help to copy the thing properly quoted for the shell safety.
   5 * any single quote is replaced with '\'', any exclamation point
   6 * is replaced with '\!', and the whole thing is enclosed in a
   7 *
   8 * E.g.
   9 *  original     sq_quote     result
  10 *  name     ==> name      ==> 'name'
  11 *  a b      ==> a b       ==> 'a b'
  12 *  a'b      ==> a'\''b    ==> 'a'\''b'
  13 *  a!b      ==> a'\!'b    ==> 'a'\!'b'
  14 */
  15#undef EMIT
  16#define EMIT(x) do { if (++len < n) *bp++ = (x); } while(0)
  17
  18static inline int need_bs_quote(char c)
  19{
  20        return (c == '\'' || c == '!');
  21}
  22
  23static size_t sq_quote_buf(char *dst, size_t n, const char *src)
  24{
  25        char c;
  26        char *bp = dst;
  27        size_t len = 0;
  28
  29        EMIT('\'');
  30        while ((c = *src++)) {
  31                if (need_bs_quote(c)) {
  32                        EMIT('\'');
  33                        EMIT('\\');
  34                        EMIT(c);
  35                        EMIT('\'');
  36                } else {
  37                        EMIT(c);
  38                }
  39        }
  40        EMIT('\'');
  41
  42        if ( n )
  43                *bp = 0;
  44
  45        return len;
  46}
  47
  48void sq_quote_print(FILE *stream, const char *src)
  49{
  50        char c;
  51
  52        fputc('\'', stream);
  53        while ((c = *src++)) {
  54                if (need_bs_quote(c)) {
  55                        fputs("'\\", stream);
  56                        fputc(c, stream);
  57                        fputc('\'', stream);
  58                } else {
  59                        fputc(c, stream);
  60                }
  61        }
  62        fputc('\'', stream);
  63}
  64
  65char *sq_quote_argv(const char** argv, int count)
  66{
  67        char *buf, *to;
  68        int i;
  69        size_t len = 0;
  70
  71        /* Count argv if needed. */
  72        if (count < 0) {
  73                for (count = 0; argv[count]; count++)
  74                        ; /* just counting */
  75        }
  76
  77        /* Special case: no argv. */
  78        if (!count)
  79                return xcalloc(1,1);
  80
  81        /* Get destination buffer length. */
  82        for (i = 0; i < count; i++)
  83                len += sq_quote_buf(NULL, 0, argv[i]) + 1;
  84
  85        /* Alloc destination buffer. */
  86        to = buf = xmalloc(len + 1);
  87
  88        /* Copy into destination buffer. */
  89        for (i = 0; i < count; ++i) {
  90                *to++ = ' ';
  91                to += sq_quote_buf(to, len, argv[i]);
  92        }
  93
  94        return buf;
  95}
  96
  97/*
  98 * Append a string to a string buffer, with or without shell quoting.
  99 * Return true if the buffer overflowed.
 100 */
 101int add_to_string(char **ptrp, int *sizep, const char *str, int quote)
 102{
 103        char *p = *ptrp;
 104        int size = *sizep;
 105        int oc;
 106        int err = 0;
 107
 108        if (quote)
 109                oc = sq_quote_buf(p, size, str);
 110        else {
 111                oc = strlen(str);
 112                memcpy(p, str, (size <= oc) ? size - 1 : oc);
 113        }
 114
 115        if (size <= oc) {
 116                err = 1;
 117                oc = size - 1;
 118        }
 119
 120        *ptrp += oc;
 121        **ptrp = '\0';
 122        *sizep -= oc;
 123        return err;
 124}
 125
 126char *sq_dequote(char *arg)
 127{
 128        char *dst = arg;
 129        char *src = arg;
 130        char c;
 131
 132        if (*src != '\'')
 133                return NULL;
 134        for (;;) {
 135                c = *++src;
 136                if (!c)
 137                        return NULL;
 138                if (c != '\'') {
 139                        *dst++ = c;
 140                        continue;
 141                }
 142                /* We stepped out of sq */
 143                switch (*++src) {
 144                case '\0':
 145                        *dst = 0;
 146                        return arg;
 147                case '\\':
 148                        c = *++src;
 149                        if (need_bs_quote(c) && *++src == '\'') {
 150                                *dst++ = c;
 151                                continue;
 152                        }
 153                /* Fallthrough */
 154                default:
 155                        return NULL;
 156                }
 157        }
 158}
 159
 160/* 1 means: quote as octal
 161 * 0 means: quote as octal if (quote_path_fully)
 162 * -1 means: never quote
 163 * c: quote as "\\c"
 164 */
 165#define X8(x)   x, x, x, x, x, x, x, x
 166#define X16(x)  X8(x), X8(x)
 167static signed char const sq_lookup[256] = {
 168        /*           0    1    2    3    4    5    6    7 */
 169        /* 0x00 */   1,   1,   1,   1,   1,   1,   1, 'a',
 170        /* 0x08 */ 'b', 't', 'n', 'v', 'f', 'r',   1,   1,
 171        /* 0x10 */ X16(1),
 172        /* 0x20 */  -1,  -1, '"',  -1,  -1,  -1,  -1,  -1,
 173        /* 0x28 */ X16(-1), X16(-1), X16(-1),
 174        /* 0x58 */  -1,  -1,  -1,  -1,'\\',  -1,  -1,  -1,
 175        /* 0x60 */ X16(-1), X8(-1),
 176        /* 0x78 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,   1,
 177        /* 0x80 */ /* set to 0 */
 178};
 179
 180static inline int sq_must_quote(char c) {
 181        return sq_lookup[(unsigned char)c] + quote_path_fully > 0;
 182}
 183
 184/* returns the longest prefix not needing a quote up to maxlen if positive.
 185   This stops at the first \0 because it's marked as a character needing an
 186   escape */
 187static size_t next_quote_pos(const char *s, ssize_t maxlen)
 188{
 189        size_t len;
 190        if (maxlen < 0) {
 191                for (len = 0; !sq_must_quote(s[len]); len++);
 192        } else {
 193                for (len = 0; len < maxlen && !sq_must_quote(s[len]); len++);
 194        }
 195        return len;
 196}
 197
 198/*
 199 * C-style name quoting.
 200 *
 201 * (1) if sb and fp are both NULL, inspect the input name and counts the
 202 *     number of bytes that are needed to hold c_style quoted version of name,
 203 *     counting the double quotes around it but not terminating NUL, and
 204 *     returns it.
 205 *     However, if name does not need c_style quoting, it returns 0.
 206 *
 207 * (2) if sb or fp are not NULL, it emits the c_style quoted version
 208 *     of name, enclosed with double quotes if asked and needed only.
 209 *     Return value is the same as in (1).
 210 */
 211static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
 212                                    struct strbuf *sb, FILE *fp, int no_dq)
 213{
 214#undef EMIT
 215#define EMIT(c)                                 \
 216        do {                                        \
 217                if (sb) strbuf_addch(sb, (c));          \
 218                if (fp) fputc((c), fp);                 \
 219                count++;                                \
 220        } while (0)
 221#define EMITBUF(s, l)                           \
 222        do {                                        \
 223                if (sb) strbuf_add(sb, (s), (l));       \
 224                if (fp) fwrite((s), (l), 1, fp);        \
 225                count += (l);                           \
 226        } while (0)
 227
 228        size_t len, count = 0;
 229        const char *p = name;
 230
 231        for (;;) {
 232                int ch;
 233
 234                len = next_quote_pos(p, maxlen);
 235                if (len == maxlen || !p[len])
 236                        break;
 237
 238                if (!no_dq && p == name)
 239                        EMIT('"');
 240
 241                EMITBUF(p, len);
 242                EMIT('\\');
 243                p += len;
 244                ch = (unsigned char)*p++;
 245                if (sq_lookup[ch] >= ' ') {
 246                        EMIT(sq_lookup[ch]);
 247                } else {
 248                        EMIT(((ch >> 6) & 03) + '0');
 249                        EMIT(((ch >> 3) & 07) + '0');
 250                        EMIT(((ch >> 0) & 07) + '0');
 251                }
 252        }
 253
 254        EMITBUF(p, len);
 255        if (p == name)   /* no ending quote needed */
 256                return 0;
 257
 258        if (!no_dq)
 259                EMIT('"');
 260        return count;
 261}
 262
 263size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, int nodq)
 264{
 265        return quote_c_style_counted(name, -1, sb, fp, nodq);
 266}
 267
 268void write_name_quoted(const char *name, FILE *fp, int terminator)
 269{
 270        if (terminator) {
 271                quote_c_style(name, NULL, fp, 0);
 272        } else {
 273                fputs(name, fp);
 274        }
 275        fputc(terminator, fp);
 276}
 277
 278extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
 279                                 const char *name, FILE *fp, int terminator)
 280{
 281        int needquote = 0;
 282
 283        if (terminator) {
 284                needquote = next_quote_pos(pfx, pfxlen) < pfxlen
 285                        || name[next_quote_pos(name, -1)];
 286        }
 287        if (needquote) {
 288                fputc('"', fp);
 289                quote_c_style_counted(pfx, pfxlen, NULL, fp, 1);
 290                quote_c_style(name, NULL, fp, 1);
 291                fputc('"', fp);
 292        } else {
 293                fwrite(pfx, pfxlen, 1, fp);
 294                fputs(name, fp);
 295        }
 296        fputc(terminator, fp);
 297}
 298
 299/*
 300 * C-style name unquoting.
 301 *
 302 * Quoted should point at the opening double quote.
 303 * + Returns 0 if it was able to unquote the string properly, and appends the
 304 *   result in the strbuf `sb'.
 305 * + Returns -1 in case of error, and doesn't touch the strbuf. Though note
 306 *   that this function will allocate memory in the strbuf, so calling
 307 *   strbuf_release is mandatory whichever result unquote_c_style returns.
 308 *
 309 * Updates endp pointer to point at one past the ending double quote if given.
 310 */
 311int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
 312{
 313        size_t oldlen = sb->len, len;
 314        int ch, ac;
 315
 316        if (*quoted++ != '"')
 317                return -1;
 318
 319        for (;;) {
 320                len = strcspn(quoted, "\"\\");
 321                strbuf_add(sb, quoted, len);
 322                quoted += len;
 323
 324                switch (*quoted++) {
 325                  case '"':
 326                        if (endp)
 327                                *endp = quoted + 1;
 328                        return 0;
 329                  case '\\':
 330                        break;
 331                  default:
 332                        goto error;
 333                }
 334
 335                switch ((ch = *quoted++)) {
 336                case 'a': ch = '\a'; break;
 337                case 'b': ch = '\b'; break;
 338                case 'f': ch = '\f'; break;
 339                case 'n': ch = '\n'; break;
 340                case 'r': ch = '\r'; break;
 341                case 't': ch = '\t'; break;
 342                case 'v': ch = '\v'; break;
 343
 344                case '\\': case '"':
 345                        break; /* verbatim */
 346
 347                /* octal values with first digit over 4 overflow */
 348                case '0': case '1': case '2': case '3':
 349                                        ac = ((ch - '0') << 6);
 350                        if ((ch = *quoted++) < '0' || '7' < ch)
 351                                goto error;
 352                                        ac |= ((ch - '0') << 3);
 353                        if ((ch = *quoted++) < '0' || '7' < ch)
 354                                goto error;
 355                                        ac |= (ch - '0');
 356                                        ch = ac;
 357                                        break;
 358                                default:
 359                        goto error;
 360                        }
 361                strbuf_addch(sb, ch);
 362                }
 363
 364  error:
 365        strbuf_setlen(sb, oldlen);
 366        return -1;
 367}
 368
 369/* quoting as a string literal for other languages */
 370
 371void perl_quote_print(FILE *stream, const char *src)
 372{
 373        const char sq = '\'';
 374        const char bq = '\\';
 375        char c;
 376
 377        fputc(sq, stream);
 378        while ((c = *src++)) {
 379                if (c == sq || c == bq)
 380                        fputc(bq, stream);
 381                fputc(c, stream);
 382        }
 383        fputc(sq, stream);
 384}
 385
 386void python_quote_print(FILE *stream, const char *src)
 387{
 388        const char sq = '\'';
 389        const char bq = '\\';
 390        const char nl = '\n';
 391        char c;
 392
 393        fputc(sq, stream);
 394        while ((c = *src++)) {
 395                if (c == nl) {
 396                        fputc(bq, stream);
 397                        fputc('n', stream);
 398                        continue;
 399                }
 400                if (c == sq || c == bq)
 401                        fputc(bq, stream);
 402                fputc(c, stream);
 403        }
 404        fputc(sq, stream);
 405}
 406
 407void tcl_quote_print(FILE *stream, const char *src)
 408{
 409        char c;
 410
 411        fputc('"', stream);
 412        while ((c = *src++)) {
 413                switch (c) {
 414                case '[': case ']':
 415                case '{': case '}':
 416                case '$': case '\\': case '"':
 417                        fputc('\\', stream);
 418                default:
 419                        fputc(c, stream);
 420                        break;
 421                case '\f':
 422                        fputs("\\f", stream);
 423                        break;
 424                case '\r':
 425                        fputs("\\r", stream);
 426                        break;
 427                case '\n':
 428                        fputs("\\n", stream);
 429                        break;
 430                case '\t':
 431                        fputs("\\t", stream);
 432                        break;
 433                case '\v':
 434                        fputs("\\v", stream);
 435                        break;
 436                }
 437        }
 438        fputc('"', stream);
 439}