Coverage Report

Created: 2026-01-09 07:10

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
static int sq_dequote_to_argv_internal(char *arg,
175
               const char ***argv, int *nr, int *alloc,
176
               struct strvec *array)
177
0
{
178
0
  char *next = arg;
179
180
0
  if (!*arg)
181
0
    return 0;
182
0
  do {
183
0
    char *dequoted = sq_dequote_step(next, &next);
184
0
    if (!dequoted)
185
0
      return -1;
186
0
    if (next) {
187
0
      char c;
188
0
      if (!isspace(*next))
189
0
        return -1;
190
0
      do {
191
0
        c = *++next;
192
0
      } while (isspace(c));
193
0
    }
194
0
    if (argv) {
195
0
      ALLOC_GROW(*argv, *nr + 1, *alloc);
196
0
      (*argv)[(*nr)++] = dequoted;
197
0
    }
198
0
    if (array)
199
0
      strvec_push(array, dequoted);
200
0
  } while (next);
201
202
0
  return 0;
203
0
}
204
205
int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
206
0
{
207
0
  return sq_dequote_to_argv_internal(arg, argv, nr, alloc, NULL);
208
0
}
209
210
int sq_dequote_to_strvec(char *arg, struct strvec *array)
211
0
{
212
0
  return sq_dequote_to_argv_internal(arg, NULL, NULL, NULL, array);
213
0
}
214
215
/* 1 means: quote as octal
216
 * 0 means: quote as octal if (quote_path_fully)
217
 * -1 means: never quote
218
 * c: quote as "\\c"
219
 */
220
#define X8(x)   x, x, x, x, x, x, x, x
221
#define X16(x)  X8(x), X8(x)
222
static signed char const cq_lookup[256] = {
223
  /*           0    1    2    3    4    5    6    7 */
224
  /* 0x00 */   1,   1,   1,   1,   1,   1,   1, 'a',
225
  /* 0x08 */ 'b', 't', 'n', 'v', 'f', 'r',   1,   1,
226
  /* 0x10 */ X16(1),
227
  /* 0x20 */  -1,  -1, '"',  -1,  -1,  -1,  -1,  -1,
228
  /* 0x28 */ X16(-1), X16(-1), X16(-1),
229
  /* 0x58 */  -1,  -1,  -1,  -1,'\\',  -1,  -1,  -1,
230
  /* 0x60 */ X16(-1), X8(-1),
231
  /* 0x78 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,   1,
232
  /* 0x80 */ /* set to 0 */
233
};
234
235
static inline int cq_must_quote(char c)
236
0
{
237
0
  return cq_lookup[(unsigned char)c] + quote_path_fully > 0;
238
0
}
239
240
/* returns the longest prefix not needing a quote up to maxlen if positive.
241
   This stops at the first \0 because it's marked as a character needing an
242
   escape */
243
static size_t next_quote_pos(const char *s, ssize_t maxlen)
244
0
{
245
0
  size_t len;
246
0
  if (maxlen < 0) {
247
0
    for (len = 0; !cq_must_quote(s[len]); len++);
248
0
  } else {
249
0
    for (len = 0; len < maxlen && !cq_must_quote(s[len]); len++);
250
0
  }
251
0
  return len;
252
0
}
253
254
/*
255
 * C-style name quoting.
256
 *
257
 * (1) if sb and fp are both NULL, inspect the input name and counts the
258
 *     number of bytes that are needed to hold c_style quoted version of name,
259
 *     counting the double quotes around it but not terminating NUL, and
260
 *     returns it.
261
 *     However, if name does not need c_style quoting, it returns 0.
262
 *
263
 * (2) if sb or fp are not NULL, it emits the c_style quoted version
264
 *     of name, enclosed with double quotes if asked and needed only.
265
 *     Return value is the same as in (1).
266
 */
267
static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
268
            struct strbuf *sb, FILE *fp, unsigned flags)
269
0
{
270
0
#undef EMIT
271
0
#define EMIT(c)                                 \
272
0
  do {                                        \
273
0
    if (sb) strbuf_addch(sb, (c));          \
274
0
    if (fp) fputc((c), fp);                 \
275
0
    count++;                                \
276
0
  } while (0)
277
0
#define EMITBUF(s, l)                           \
278
0
  do {                                        \
279
0
    if (sb) strbuf_add(sb, (s), (l));       \
280
0
    if (fp) fwrite((s), (l), 1, fp);        \
281
0
    count += (l);                           \
282
0
  } while (0)
283
284
0
  int no_dq = !!(flags & CQUOTE_NODQ);
285
0
  size_t len, count = 0;
286
0
  const char *p = name;
287
288
0
  for (;;) {
289
0
    int ch;
290
291
0
    len = next_quote_pos(p, maxlen);
292
0
    if (len == maxlen || (maxlen < 0 && !p[len]))
293
0
      break;
294
295
0
    if (!no_dq && p == name)
296
0
      EMIT('"');
297
298
0
    EMITBUF(p, len);
299
0
    EMIT('\\');
300
0
    p += len;
301
0
    ch = (unsigned char)*p++;
302
0
    if (maxlen >= 0)
303
0
      maxlen -= len + 1;
304
0
    if (cq_lookup[ch] >= ' ') {
305
0
      EMIT(cq_lookup[ch]);
306
0
    } else {
307
0
      EMIT(((ch >> 6) & 03) + '0');
308
0
      EMIT(((ch >> 3) & 07) + '0');
309
0
      EMIT(((ch >> 0) & 07) + '0');
310
0
    }
311
0
  }
312
313
0
  EMITBUF(p, len);
314
0
  if (p == name)   /* no ending quote needed */
315
0
    return 0;
316
317
0
  if (!no_dq)
318
0
    EMIT('"');
319
0
  return count;
320
0
}
321
322
size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, unsigned flags)
323
0
{
324
0
  return quote_c_style_counted(name, -1, sb, fp, flags);
325
0
}
326
327
void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path,
328
           unsigned flags)
329
0
{
330
0
  int nodq = !!(flags & CQUOTE_NODQ);
331
0
  if (quote_c_style(prefix, NULL, NULL, 0) ||
332
0
      quote_c_style(path, NULL, NULL, 0)) {
333
0
    if (!nodq)
334
0
      strbuf_addch(sb, '"');
335
0
    quote_c_style(prefix, sb, NULL, CQUOTE_NODQ);
336
0
    quote_c_style(path, sb, NULL, CQUOTE_NODQ);
337
0
    if (!nodq)
338
0
      strbuf_addch(sb, '"');
339
0
  } else {
340
0
    strbuf_addstr(sb, prefix);
341
0
    strbuf_addstr(sb, path);
342
0
  }
343
0
}
344
345
void write_name_quoted(const char *name, FILE *fp, int terminator)
346
0
{
347
0
  if (terminator) {
348
0
    quote_c_style(name, NULL, fp, 0);
349
0
  } else {
350
0
    fputs(name, fp);
351
0
  }
352
0
  fputc(terminator, fp);
353
0
}
354
355
void write_name_quoted_relative(const char *name, const char *prefix,
356
        FILE *fp, int terminator)
357
0
{
358
0
  struct strbuf sb = STRBUF_INIT;
359
360
0
  name = relative_path(name, prefix, &sb);
361
0
  write_name_quoted(name, fp, terminator);
362
363
0
  strbuf_release(&sb);
364
0
}
365
366
/* quote path as relative to the given prefix */
367
char *quote_path(const char *in, const char *prefix, struct strbuf *out, unsigned flags)
368
0
{
369
0
  struct strbuf sb = STRBUF_INIT;
370
0
  const char *rel = relative_path(in, prefix, &sb);
371
0
  int force_dq = ((flags & QUOTE_PATH_QUOTE_SP) && strchr(rel, ' '));
372
373
0
  strbuf_reset(out);
374
375
  /*
376
   * If the caller wants us to enclose the output in a dq-pair
377
   * whether quote_c_style_counted() needs to, we do it ourselves
378
   * and tell quote_c_style_counted() not to.
379
   */
380
0
  if (force_dq)
381
0
    strbuf_addch(out, '"');
382
0
  quote_c_style_counted(rel, strlen(rel), out, NULL,
383
0
            force_dq ? CQUOTE_NODQ : 0);
384
0
  if (force_dq)
385
0
    strbuf_addch(out, '"');
386
0
  strbuf_release(&sb);
387
388
0
  return out->buf;
389
0
}
390
391
/*
392
 * C-style name unquoting.
393
 *
394
 * Quoted should point at the opening double quote.
395
 * + Returns 0 if it was able to unquote the string properly, and appends the
396
 *   result in the strbuf `sb'.
397
 * + Returns -1 in case of error, and doesn't touch the strbuf. Though note
398
 *   that this function will allocate memory in the strbuf, so calling
399
 *   strbuf_release is mandatory whichever result unquote_c_style returns.
400
 *
401
 * Updates endp pointer to point at one past the ending double quote if given.
402
 */
403
int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
404
0
{
405
0
  size_t oldlen = sb->len, len;
406
0
  int ch, ac;
407
408
0
  if (*quoted++ != '"')
409
0
    return -1;
410
411
0
  for (;;) {
412
0
    len = strcspn(quoted, "\"\\");
413
0
    strbuf_add(sb, quoted, len);
414
0
    quoted += len;
415
416
0
    switch (*quoted++) {
417
0
      case '"':
418
0
      if (endp)
419
0
        *endp = quoted;
420
0
      return 0;
421
0
      case '\\':
422
0
      break;
423
0
      default:
424
0
      goto error;
425
0
    }
426
427
0
    switch ((ch = *quoted++)) {
428
0
    case 'a': ch = '\a'; break;
429
0
    case 'b': ch = '\b'; break;
430
0
    case 'f': ch = '\f'; break;
431
0
    case 'n': ch = '\n'; break;
432
0
    case 'r': ch = '\r'; break;
433
0
    case 't': ch = '\t'; break;
434
0
    case 'v': ch = '\v'; break;
435
436
0
    case '\\': case '"':
437
0
      break; /* verbatim */
438
439
    /* octal values with first digit over 4 overflow */
440
0
    case '0': case '1': case '2': case '3':
441
0
          ac = ((ch - '0') << 6);
442
0
      if ((ch = *quoted++) < '0' || '7' < ch)
443
0
        goto error;
444
0
          ac |= ((ch - '0') << 3);
445
0
      if ((ch = *quoted++) < '0' || '7' < ch)
446
0
        goto error;
447
0
          ac |= (ch - '0');
448
0
          ch = ac;
449
0
          break;
450
0
        default:
451
0
      goto error;
452
0
      }
453
0
    strbuf_addch(sb, ch);
454
0
    }
455
456
0
  error:
457
0
  strbuf_setlen(sb, oldlen);
458
0
  return -1;
459
0
}
460
461
/* quoting as a string literal for other languages */
462
463
void perl_quote_buf(struct strbuf *sb, const char *src)
464
0
{
465
0
  const char sq = '\'';
466
0
  const char bq = '\\';
467
0
  char c;
468
469
0
  strbuf_addch(sb, sq);
470
0
  while ((c = *src++)) {
471
0
    if (c == sq || c == bq)
472
0
      strbuf_addch(sb, bq);
473
0
    strbuf_addch(sb, c);
474
0
  }
475
0
  strbuf_addch(sb, sq);
476
0
}
477
478
void perl_quote_buf_with_len(struct strbuf *sb, const char *src, size_t len)
479
0
{
480
0
  const char sq = '\'';
481
0
  const char bq = '\\';
482
0
  const char *c = src;
483
0
  const char *end = src + len;
484
485
0
  strbuf_addch(sb, sq);
486
0
  while (c != end) {
487
0
    if (*c == sq || *c == bq)
488
0
      strbuf_addch(sb, bq);
489
0
    strbuf_addch(sb, *c);
490
0
    c++;
491
0
  }
492
0
  strbuf_addch(sb, sq);
493
0
}
494
495
void python_quote_buf(struct strbuf *sb, const char *src)
496
0
{
497
0
  const char sq = '\'';
498
0
  const char bq = '\\';
499
0
  const char nl = '\n';
500
0
  char c;
501
502
0
  strbuf_addch(sb, sq);
503
0
  while ((c = *src++)) {
504
0
    if (c == nl) {
505
0
      strbuf_addch(sb, bq);
506
0
      strbuf_addch(sb, 'n');
507
0
      continue;
508
0
    }
509
0
    if (c == sq || c == bq)
510
0
      strbuf_addch(sb, bq);
511
0
    strbuf_addch(sb, c);
512
0
  }
513
0
  strbuf_addch(sb, sq);
514
0
}
515
516
void tcl_quote_buf(struct strbuf *sb, const char *src)
517
0
{
518
0
  char c;
519
520
0
  strbuf_addch(sb, '"');
521
0
  while ((c = *src++)) {
522
0
    switch (c) {
523
0
    case '[': case ']':
524
0
    case '{': case '}':
525
0
    case '$': case '\\': case '"':
526
0
      strbuf_addch(sb, '\\');
527
      /* fallthrough */
528
0
    default:
529
0
      strbuf_addch(sb, c);
530
0
      break;
531
0
    case '\f':
532
0
      strbuf_addstr(sb, "\\f");
533
0
      break;
534
0
    case '\r':
535
0
      strbuf_addstr(sb, "\\r");
536
0
      break;
537
0
    case '\n':
538
0
      strbuf_addstr(sb, "\\n");
539
0
      break;
540
0
    case '\t':
541
0
      strbuf_addstr(sb, "\\t");
542
0
      break;
543
0
    case '\v':
544
0
      strbuf_addstr(sb, "\\v");
545
0
      break;
546
0
    }
547
0
  }
548
0
  strbuf_addch(sb, '"');
549
0
}
550
551
void basic_regex_quote_buf(struct strbuf *sb, const char *src)
552
0
{
553
0
  char c;
554
555
0
  if (*src == '^') {
556
    /* only beginning '^' is special and needs quoting */
557
0
    strbuf_addch(sb, '\\');
558
0
    strbuf_addch(sb, *src++);
559
0
  }
560
0
  if (*src == '*')
561
    /* beginning '*' is not special, no quoting */
562
0
    strbuf_addch(sb, *src++);
563
564
0
  while ((c = *src++)) {
565
0
    switch (c) {
566
0
    case '[':
567
0
    case '.':
568
0
    case '\\':
569
0
    case '*':
570
0
      strbuf_addch(sb, '\\');
571
0
      strbuf_addch(sb, c);
572
0
      break;
573
574
0
    case '$':
575
      /* only the end '$' is special and needs quoting */
576
0
      if (*src == '\0')
577
0
        strbuf_addch(sb, '\\');
578
0
      strbuf_addch(sb, c);
579
0
      break;
580
581
0
    default:
582
0
      strbuf_addch(sb, c);
583
0
      break;
584
0
    }
585
0
  }
586
0
}