]> git.ipfire.org Git - thirdparty/git.git/blame - quote.c
Full rework of quote_c_style and write_name_quoted.
[thirdparty/git.git] / quote.c
CommitLineData
6fb737be
JH
1#include "cache.h"
2#include "quote.h"
3
4/* Help to copy the thing properly quoted for the shell safety.
77d604c3
PA
5 * any single quote is replaced with '\'', any exclamation point
6 * is replaced with '\!', and the whole thing is enclosed in a
6fb737be
JH
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'
77d604c3 13 * a!b ==> a'\!'b ==> 'a'\!'b'
6fb737be 14 */
4f6fbcdc 15#undef EMIT
8dcaefb5 16#define EMIT(x) do { if (++len < n) *bp++ = (x); } while(0)
6fb737be 17
35eb2d36
LT
18static inline int need_bs_quote(char c)
19{
20 return (c == '\'' || c == '!');
21}
22
16befb8b 23static size_t sq_quote_buf(char *dst, size_t n, const char *src)
77d604c3
PA
24{
25 char c;
26 char *bp = dst;
27 size_t len = 0;
6fb737be 28
77d604c3 29 EMIT('\'');
6fb737be 30 while ((c = *src++)) {
35eb2d36 31 if (need_bs_quote(c)) {
77d604c3
PA
32 EMIT('\'');
33 EMIT('\\');
34 EMIT(c);
35 EMIT('\'');
36 } else {
37 EMIT(c);
6fb737be
JH
38 }
39 }
77d604c3
PA
40 EMIT('\'');
41
42 if ( n )
43 *bp = 0;
44
45 return len;
46}
47
575ba9d6
ML
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
7cf67205
CC
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
86257aa3
CC
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
35eb2d36
LT
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
663af342
PH
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
4f6fbcdc
JH
198/*
199 * C-style name quoting.
200 *
663af342
PH
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.
4f6fbcdc 206 *
663af342
PH
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).
4f6fbcdc 210 */
663af342
PH
211static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
212 struct strbuf *sb, FILE *fp, int no_dq)
4f6fbcdc
JH
213{
214#undef EMIT
663af342
PH
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;
4f6fbcdc 230
663af342
PH
231 for (;;) {
232 int ch;
4f6fbcdc 233
663af342
PH
234 len = next_quote_pos(p, maxlen);
235 if (len == maxlen || !p[len])
50e7b067 236 break;
663af342
PH
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');
4f6fbcdc 251 }
4f6fbcdc 252 }
663af342
PH
253
254 EMITBUF(p, len);
255 if (p == name) /* no ending quote needed */
256 return 0;
257
4f6fbcdc
JH
258 if (!no_dq)
259 EMIT('"');
663af342
PH
260 return count;
261}
4f6fbcdc 262
663af342
PH
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);
4f6fbcdc
JH
266}
267
663af342 268void write_name_quoted(const char *name, FILE *fp, int terminator)
9ef2b3cb 269{
663af342
PH
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);
9ef2b3cb
JH
297}
298
4f6fbcdc
JH
299/*
300 * C-style name unquoting.
301 *
7fb1011e
PH
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.
4f6fbcdc 310 */
7fb1011e 311int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
4f6fbcdc 312{
7fb1011e
PH
313 size_t oldlen = sb->len, len;
314 int ch, ac;
4f6fbcdc
JH
315
316 if (*quoted++ != '"')
7fb1011e
PH
317 return -1;
318
319 for (;;) {
320 len = strcspn(quoted, "\"\\");
321 strbuf_add(sb, quoted, len);
322 quoted += len;
4f6fbcdc 323
7fb1011e
PH
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':
4f6fbcdc 349 ac = ((ch - '0') << 6);
7fb1011e
PH
350 if ((ch = *quoted++) < '0' || '7' < ch)
351 goto error;
4f6fbcdc 352 ac |= ((ch - '0') << 3);
7fb1011e
PH
353 if ((ch = *quoted++) < '0' || '7' < ch)
354 goto error;
4f6fbcdc
JH
355 ac |= (ch - '0');
356 ch = ac;
357 break;
358 default:
7fb1011e 359 goto error;
4f6fbcdc 360 }
7fb1011e 361 strbuf_addch(sb, ch);
4f6fbcdc
JH
362 }
363
7fb1011e
PH
364 error:
365 strbuf_setlen(sb, oldlen);
366 return -1;
4f6fbcdc
JH
367}
368
9f613ddd
JH
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}
5558e55c
SP
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}