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 | } |