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