Coverage Report

Created: 2026-09-28 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/quote.c
Line
Count
Source
1
#define DISABLE_SIGN_COMPARE_WARNINGS
2
3
#include "git-compat-util.h"
4
#include "path.h"
5
#include "quote.h"
6
#include "strbuf.h"
7
#include "strvec.h"
8
9
int quote_path_fully = 1;
10
11
static inline int need_bs_quote(char c)
12
0
{
13
0
  return (c == '\'' || c == '!');
14
0
}
15
16
/* Help to copy the thing properly quoted for the shell safety.
17
 * any single quote is replaced with '\'', any exclamation point
18
 * is replaced with '\!', and the whole thing is enclosed in a
19
 * single quote pair.
20
 *
21
 * E.g.
22
 *  original     sq_quote     result
23
 *  name     ==> name      ==> 'name'
24
 *  a b      ==> a b       ==> 'a b'
25
 *  a'b      ==> a'\''b    ==> 'a'\''b'
26
 *  a!b      ==> a'\!'b    ==> 'a'\!'b'
27
 */
28
void sq_quote_buf(struct strbuf *dst, const char *src)
29
0
{
30
0
  char *to_free = NULL;
31
32
0
  if (dst->buf == src)
33
0
    to_free = strbuf_detach(dst, NULL);
34
35
0
  strbuf_addch(dst, '\'');
36
0
  while (*src) {
37
0
    size_t len = strcspn(src, "'!");
38
0
    strbuf_add(dst, src, len);
39
0
    src += len;
40
0
    while (need_bs_quote(*src)) {
41
0
      strbuf_addstr(dst, "'\\");
42
0
      strbuf_addch(dst, *src++);
43
0
      strbuf_addch(dst, '\'');
44
0
    }
45
0
  }
46
0
  strbuf_addch(dst, '\'');
47
0
  free(to_free);
48
0
}
49
50
void sq_quote_buf_pretty(struct strbuf *dst, const char *src)
51
0
{
52
0
  static const char ok_punct[] = "+,-./:=@_^";
53
0
  const char *p;
54
55
  /* Avoid losing a zero-length string by adding '' */
56
0
  if (!*src) {
57
0
    strbuf_addstr(dst, "''");
58
0
    return;
59
0
  }
60
61
0
  for (p = src; *p; p++) {
62
0
    if (!isalnum(*p) && !strchr(ok_punct, *p)) {
63
0
      sq_quote_buf(dst, src);
64
0
      return;
65
0
    }
66
0
  }
67
68
  /* if we get here, we did not need quoting */
69
0
  strbuf_addstr(dst, src);
70
0
}
71
72
void sq_quotef(struct strbuf *dst, const char *fmt, ...)
73
0
{
74
0
  struct strbuf src = STRBUF_INIT;
75
76
0
  va_list ap;
77
0
  va_start(ap, fmt);
78
0
  strbuf_vaddf(&src, fmt, ap);
79
0
  va_end(ap);
80
81
0
  sq_quote_buf(dst, src.buf);
82
0
  strbuf_release(&src);
83
0
}
84
85
void sq_quote_argv(struct strbuf *dst, const char **argv)
86
0
{
87
0
  int i;
88
89
  /* Copy into destination buffer. */
90
0
  strbuf_grow(dst, 255);
91
0
  for (i = 0; argv[i]; ++i) {
92
0
    strbuf_addch(dst, ' ');
93
0
    sq_quote_buf(dst, argv[i]);
94
0
  }
95
0
}
96
97
/*
98
 * Legacy function to append each argv value, quoted as necessasry,
99
 * with whitespace before each value.  This results in a leading
100
 * space in the result.
101
 */
102
void sq_quote_argv_pretty(struct strbuf *dst, const char **argv)
103
0
{
104
0
  if (argv[0])
105
0
    strbuf_addch(dst, ' ');
106
0
  sq_append_quote_argv_pretty(dst, argv);
107
0
}
108
109
/*
110
 * Append each argv value, quoted as necessary, with whitespace between them.
111
 */
112
void sq_append_quote_argv_pretty(struct strbuf *dst, const char **argv)
113
0
{
114
0
  int i;
115
116
0
  for (i = 0; argv[i]; i++) {
117
0
    if (i > 0)
118
0
      strbuf_addch(dst, ' ');
119
0
    sq_quote_buf_pretty(dst, argv[i]);
120
0
  }
121
0
}
122
123
char *sq_dequote_step(char *arg, char **next)
124
0
{
125
0
  char *dst = arg;
126
0
  char *src = arg;
127
0
  char c;
128
129
0
  if (*src != '\'')
130
0
    return NULL;
131
0
  for (;;) {
132
0
    c = *++src;
133
0
    if (!c)
134
0
      return NULL;
135
0
    if (c != '\'') {
136
0
      *dst++ = c;
137
0
      continue;
138
0
    }
139
    /* We stepped out of sq */
140
0
    switch (*++src) {
141
0
    case '\0':
142
0
      *dst = 0;
143
0
      if (next)
144
0
        *next = NULL;
145
0
      return arg;
146
0
    case '\\':
147
      /*
148
       * Allow backslashed characters outside of
149
       * single-quotes only if they need escaping,
150
       * and only if we resume the single-quoted part
151
       * afterward.
152
       */
153
0
      if (need_bs_quote(src[1]) && src[2] == '\'') {
154
0
        *dst++ = src[1];
155
0
        src += 2;
156
0
        continue;
157
0
      }
158
    /* Fallthrough */
159
0
    default:
160
0
      if (!next)
161
0
        return NULL;
162
0
      *dst = 0;
163
0
      *next = src;
164
0
      return arg;
165
0
    }
166
0
  }
167
0
}
168
169
char *sq_dequote(char *arg)
170
0
{
171
0
  return sq_dequote_step(arg, NULL);
172
0
}
173
174
int sq_dequote_to_strvec(char *arg, struct strvec *array)
175
0
{
176
0
  char *next = arg;
177
178
0
  if (!*arg)
179
0
    return 0;
180
0
  do {
181
0
    char *dequoted = sq_dequote_step(next, &next);
182
0
    if (!dequoted)
183
0
      return -1;
184
0
    if (next) {
185
0
      char c;
186
0
      if (!isspace(*next))
187
0
        return -1;
188
0
      do {
189
0
        c = *++next;
190
0
      } while (isspace(c));
191
0
    }
192
0
    strvec_push(array, dequoted);
193
0
  } while (next);
194
195
0
  return 0;
196
0
}
197
198
/* 1 means: quote as octal
199
 * 0 means: quote as octal if (quote_path_fully)
200
 * -1 means: never quote
201
 * c: quote as "\\c"
202
 */
203
#define X8(x)   x, x, x, x, x, x, x, x
204
#define X16(x)  X8(x), X8(x)
205
static signed char const cq_lookup[256] = {
206
  /*           0    1    2    3    4    5    6    7 */
207
  /* 0x00 */   1,   1,   1,   1,   1,   1,   1, 'a',
208
  /* 0x08 */ 'b', 't', 'n', 'v', 'f', 'r',   1,   1,
209
  /* 0x10 */ X16(1),
210
  /* 0x20 */  -1,  -1, '"',  -1,  -1,  -1,  -1,  -1,
211
  /* 0x28 */ X16(-1), X16(-1), X16(-1),
212
  /* 0x58 */  -1,  -1,  -1,  -1,'\\',  -1,  -1,  -1,
213
  /* 0x60 */ X16(-1), X8(-1),
214
  /* 0x78 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,   1,
215
  /* 0x80 */ /* set to 0 */
216
};
217
218
static inline int cq_must_quote(char c)
219
0
{
220
0
  return cq_lookup[(unsigned char)c] + quote_path_fully > 0;
221
0
}
222
223
/* returns the longest prefix not needing a quote up to maxlen if positive.
224
   This stops at the first \0 because it's marked as a character needing an
225
   escape */
226
static size_t next_quote_pos(const char *s, ssize_t maxlen)
227
0
{
228
0
  size_t len;
229
0
  if (maxlen < 0) {
230
0
    for (len = 0; !cq_must_quote(s[len]); len++);
231
0
  } else {
232
0
    for (len = 0; len < maxlen && !cq_must_quote(s[len]); len++);
233
0
  }
234
0
  return len;
235
0
}
236
237
/*
238
 * C-style name quoting.
239
 *
240
 * (1) if sb and fp are both NULL, inspect the input name and counts the
241
 *     number of bytes that are needed to hold c_style quoted version of name,
242
 *     counting the double quotes around it but not terminating NUL, and
243
 *     returns it.
244
 *     However, if name does not need c_style quoting, it returns 0.
245
 *
246
 * (2) if sb or fp are not NULL, it emits the c_style quoted version
247
 *     of name, enclosed with double quotes if asked and needed only.
248
 *     Return value is the same as in (1).
249
 */
250
static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
251
            struct strbuf *sb, FILE *fp, unsigned flags)
252
0
{
253
0
#undef EMIT
254
0
#define EMIT(c)                                 \
255
0
  do {                                        \
256
0
    if (sb) strbuf_addch(sb, (c));          \
257
0
    if (fp) fputc((c), fp);                 \
258
0
    count++;                                \
259
0
  } while (0)
260
0
#define EMITBUF(s, l)                           \
261
0
  do {                                        \
262
0
    if (sb) strbuf_add(sb, (s), (l));       \
263
0
    if (fp) fwrite((s), (l), 1, fp);        \
264
0
    count += (l);                           \
265
0
  } while (0)
266
267
0
  int no_dq = !!(flags & CQUOTE_NODQ);
268
0
  size_t len, count = 0;
269
0
  const char *p = name;
270
271
0
  for (;;) {
272
0
    int ch;
273
274
0
    len = next_quote_pos(p, maxlen);
275
0
    if (len == maxlen || (maxlen < 0 && !p[len]))
276
0
      break;
277
278
0
    if (!no_dq && p == name)
279
0
      EMIT('"');
280
281
0
    EMITBUF(p, len);
282
0
    EMIT('\\');
283
0
    p += len;
284
0
    ch = (unsigned char)*p++;
285
0
    if (maxlen >= 0)
286
0
      maxlen -= len + 1;
287
0
    if (cq_lookup[ch] >= ' ') {
288
0
      EMIT(cq_lookup[ch]);
289
0
    } else {
290
0
      EMIT(((ch >> 6) & 03) + '0');
291
0
      EMIT(((ch >> 3) & 07) + '0');
292
0
      EMIT(((ch >> 0) & 07) + '0');
293
0
    }
294
0
  }
295
296
0
  EMITBUF(p, len);
297
0
  if (p == name)   /* no ending quote needed */
298
0
    return 0;
299
300
0
  if (!no_dq)
301
0
    EMIT('"');
302
0
  return count;
303
0
}
304
305
size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, unsigned flags)
306
0
{
307
0
  return quote_c_style_counted(name, -1, sb, fp, flags);
308
0
}
309
310
void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path,
311
           unsigned flags)
312
0
{
313
0
  int nodq = !!(flags & CQUOTE_NODQ);
314
0
  if (quote_c_style(prefix, NULL, NULL, 0) ||
315
0
      quote_c_style(path, NULL, NULL, 0)) {
316
0
    if (!nodq)
317
0
      strbuf_addch(sb, '"');
318
0
    quote_c_style(prefix, sb, NULL, CQUOTE_NODQ);
319
0
    quote_c_style(path, sb, NULL, CQUOTE_NODQ);
320
0
    if (!nodq)
321
0
      strbuf_addch(sb, '"');
322
0
  } else {
323
0
    strbuf_addstr(sb, prefix);
324
0
    strbuf_addstr(sb, path);
325
0
  }
326
0
}
327
328
void write_name_quoted(const char *name, FILE *fp, int terminator)
329
0
{
330
0
  if (terminator) {
331
0
    quote_c_style(name, NULL, fp, 0);
332
0
  } else {
333
0
    fputs(name, fp);
334
0
  }
335
0
  fputc(terminator, fp);
336
0
}
337
338
void write_name_quoted_relative(const char *name, const char *prefix,
339
        FILE *fp, int terminator)
340
0
{
341
0
  struct strbuf sb = STRBUF_INIT;
342
343
0
  name = relative_path(name, prefix, &sb);
344
0
  write_name_quoted(name, fp, terminator);
345
346
0
  strbuf_release(&sb);
347
0
}
348
349
/* quote path as relative to the given prefix */
350
char *quote_path(const char *in, const char *prefix, struct strbuf *out, unsigned flags)
351
0
{
352
0
  struct strbuf sb = STRBUF_INIT;
353
0
  const char *rel = relative_path(in, prefix, &sb);
354
0
  int force_dq = ((flags & QUOTE_PATH_QUOTE_SP) && strchr(rel, ' '));
355
356
0
  strbuf_reset(out);
357
358
  /*
359
   * If the caller wants us to enclose the output in a dq-pair
360
   * whether quote_c_style_counted() needs to, we do it ourselves
361
   * and tell quote_c_style_counted() not to.
362
   */
363
0
  if (force_dq)
364
0
    strbuf_addch(out, '"');
365
0
  quote_c_style_counted(rel, strlen(rel), out, NULL,
366
0
            force_dq ? CQUOTE_NODQ : 0);
367
0
  if (force_dq)
368
0
    strbuf_addch(out, '"');
369
0
  strbuf_release(&sb);
370
371
0
  return out->buf;
372
0
}
373
374
/*
375
 * C-style name unquoting.
376
 *
377
 * Quoted should point at the opening double quote.
378
 * + Returns 0 if it was able to unquote the string properly, and appends the
379
 *   result in the strbuf `sb'.
380
 * + Returns -1 in case of error, and doesn't touch the strbuf. Though note
381
 *   that this function will allocate memory in the strbuf, so calling
382
 *   strbuf_release is mandatory whichever result unquote_c_style returns.
383
 *
384
 * Updates endp pointer to point at one past the ending double quote if given.
385
 */
386
int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
387
366
{
388
366
  size_t oldlen = sb->len, len;
389
366
  int ch, ac;
390
391
366
  if (*quoted++ != '"')
392
0
    return -1;
393
394
4.05k
  for (;;) {
395
4.05k
    len = strcspn(quoted, "\"\\");
396
4.05k
    strbuf_add(sb, quoted, len);
397
4.05k
    quoted += len;
398
399
4.05k
    switch (*quoted++) {
400
11
      case '"':
401
11
      if (endp)
402
11
        *endp = quoted;
403
11
      return 0;
404
3.76k
      case '\\':
405
3.76k
      break;
406
279
      default:
407
279
      goto error;
408
4.05k
    }
409
410
3.76k
    switch ((ch = *quoted++)) {
411
338
    case 'a': ch = '\a'; break;
412
344
    case 'b': ch = '\b'; break;
413
217
    case 'f': ch = '\f'; break;
414
250
    case 'n': ch = '\n'; break;
415
430
    case 'r': ch = '\r'; break;
416
232
    case 't': ch = '\t'; break;
417
272
    case 'v': ch = '\v'; break;
418
419
631
    case '\\': case '"':
420
631
      break; /* verbatim */
421
422
    /* octal values with first digit over 4 overflow */
423
1.04k
    case '0': case '1': case '2': case '3':
424
1.04k
          ac = ((ch - '0') << 6);
425
1.04k
      if ((ch = *quoted++) < '0' || '7' < ch)
426
45
        goto error;
427
995
          ac |= ((ch - '0') << 3);
428
995
      if ((ch = *quoted++) < '0' || '7' < ch)
429
21
        goto error;
430
974
          ac |= (ch - '0');
431
974
          ch = ac;
432
974
          break;
433
10
        default:
434
10
      goto error;
435
3.76k
      }
436
3.68k
    strbuf_addch(sb, ch);
437
3.68k
    }
438
439
355
  error:
440
355
  strbuf_setlen(sb, oldlen);
441
355
  return -1;
442
366
}
443
444
/* quoting as a string literal for other languages */
445
446
void perl_quote_buf(struct strbuf *sb, const char *src)
447
0
{
448
0
  const char sq = '\'';
449
0
  const char bq = '\\';
450
0
  char c;
451
452
0
  strbuf_addch(sb, sq);
453
0
  while ((c = *src++)) {
454
0
    if (c == sq || c == bq)
455
0
      strbuf_addch(sb, bq);
456
0
    strbuf_addch(sb, c);
457
0
  }
458
0
  strbuf_addch(sb, sq);
459
0
}
460
461
void perl_quote_buf_with_len(struct strbuf *sb, const char *src, size_t len)
462
0
{
463
0
  const char sq = '\'';
464
0
  const char bq = '\\';
465
0
  const char *c = src;
466
0
  const char *end = src + len;
467
468
0
  strbuf_addch(sb, sq);
469
0
  while (c != end) {
470
0
    if (*c == sq || *c == bq)
471
0
      strbuf_addch(sb, bq);
472
0
    strbuf_addch(sb, *c);
473
0
    c++;
474
0
  }
475
0
  strbuf_addch(sb, sq);
476
0
}
477
478
void python_quote_buf(struct strbuf *sb, const char *src)
479
0
{
480
0
  const char sq = '\'';
481
0
  const char bq = '\\';
482
0
  const char nl = '\n';
483
0
  char c;
484
485
0
  strbuf_addch(sb, sq);
486
0
  while ((c = *src++)) {
487
0
    if (c == nl) {
488
0
      strbuf_addch(sb, bq);
489
0
      strbuf_addch(sb, 'n');
490
0
      continue;
491
0
    }
492
0
    if (c == sq || c == bq)
493
0
      strbuf_addch(sb, bq);
494
0
    strbuf_addch(sb, c);
495
0
  }
496
0
  strbuf_addch(sb, sq);
497
0
}
498
499
void tcl_quote_buf(struct strbuf *sb, const char *src)
500
0
{
501
0
  char c;
502
503
0
  strbuf_addch(sb, '"');
504
0
  while ((c = *src++)) {
505
0
    switch (c) {
506
0
    case '[': case ']':
507
0
    case '{': case '}':
508
0
    case '$': case '\\': case '"':
509
0
      strbuf_addch(sb, '\\');
510
      /* fallthrough */
511
0
    default:
512
0
      strbuf_addch(sb, c);
513
0
      break;
514
0
    case '\f':
515
0
      strbuf_addstr(sb, "\\f");
516
0
      break;
517
0
    case '\r':
518
0
      strbuf_addstr(sb, "\\r");
519
0
      break;
520
0
    case '\n':
521
0
      strbuf_addstr(sb, "\\n");
522
0
      break;
523
0
    case '\t':
524
0
      strbuf_addstr(sb, "\\t");
525
0
      break;
526
0
    case '\v':
527
0
      strbuf_addstr(sb, "\\v");
528
0
      break;
529
0
    }
530
0
  }
531
0
  strbuf_addch(sb, '"');
532
0
}
533
534
void basic_regex_quote_buf(struct strbuf *sb, const char *src)
535
0
{
536
0
  char c;
537
538
0
  if (*src == '^') {
539
    /* only beginning '^' is special and needs quoting */
540
0
    strbuf_addch(sb, '\\');
541
0
    strbuf_addch(sb, *src++);
542
0
  }
543
0
  if (*src == '*')
544
    /* beginning '*' is not special, no quoting */
545
0
    strbuf_addch(sb, *src++);
546
547
0
  while ((c = *src++)) {
548
0
    switch (c) {
549
0
    case '[':
550
0
    case '.':
551
0
    case '\\':
552
0
    case '*':
553
0
      strbuf_addch(sb, '\\');
554
0
      strbuf_addch(sb, c);
555
0
      break;
556
557
0
    case '$':
558
      /* only the end '$' is special and needs quoting */
559
0
      if (*src == '\0')
560
0
        strbuf_addch(sb, '\\');
561
0
      strbuf_addch(sb, c);
562
0
      break;
563
564
0
    default:
565
0
      strbuf_addch(sb, c);
566
0
      break;
567
0
    }
568
0
  }
569
0
}