Full rework of quote_c_style and write_name_quoted.
* quote_c_style works on a strbuf instead of a wild buffer. * quote_c_style is now clever enough to not add double quotes if not needed. * write_name_quoted inherits those advantages, but also take a different set of arguments. Now instead of asking for quotes or not, you pass a "terminator". If it's \0 then we assume you don't want to escape, else C escaping is performed. In any case, the terminator is also appended to the stream. It also no longer takes the prefix/prefix_len arguments, as it's seldomly used, and makes some optimizations harder. * write_name_quotedpfx is created to work like write_name_quoted and take the prefix/prefix_len arguments. Thanks to those API changes, diff.c has somehow lost weight, thanks to the removal of functions that were wrappers around the old write_name_quoted trying to give it a semantics like the new one, but performing a lot of allocations for this goal. Now we always write directly to the stream, no intermediate allocation is performed. As a side effect of the refactor in builtin-apply.c, the length of the bar graphs in diffstats are not affected anymore by the fact that the path was clipped. Signed-off-by: Pierre Habouzit <madcoder@debian.org>
This commit is contained in:

committed by
Junio C Hamano

parent
7fb1011e61
commit
663af3422a
207
quote.c
207
quote.c
@ -157,83 +157,143 @@ char *sq_dequote(char *arg)
|
||||
}
|
||||
}
|
||||
|
||||
/* 1 means: quote as octal
|
||||
* 0 means: quote as octal if (quote_path_fully)
|
||||
* -1 means: never quote
|
||||
* c: quote as "\\c"
|
||||
*/
|
||||
#define X8(x) x, x, x, x, x, x, x, x
|
||||
#define X16(x) X8(x), X8(x)
|
||||
static signed char const sq_lookup[256] = {
|
||||
/* 0 1 2 3 4 5 6 7 */
|
||||
/* 0x00 */ 1, 1, 1, 1, 1, 1, 1, 'a',
|
||||
/* 0x08 */ 'b', 't', 'n', 'v', 'f', 'r', 1, 1,
|
||||
/* 0x10 */ X16(1),
|
||||
/* 0x20 */ -1, -1, '"', -1, -1, -1, -1, -1,
|
||||
/* 0x28 */ X16(-1), X16(-1), X16(-1),
|
||||
/* 0x58 */ -1, -1, -1, -1,'\\', -1, -1, -1,
|
||||
/* 0x60 */ X16(-1), X8(-1),
|
||||
/* 0x78 */ -1, -1, -1, -1, -1, -1, -1, 1,
|
||||
/* 0x80 */ /* set to 0 */
|
||||
};
|
||||
|
||||
static inline int sq_must_quote(char c) {
|
||||
return sq_lookup[(unsigned char)c] + quote_path_fully > 0;
|
||||
}
|
||||
|
||||
/* returns the longest prefix not needing a quote up to maxlen if positive.
|
||||
This stops at the first \0 because it's marked as a character needing an
|
||||
escape */
|
||||
static size_t next_quote_pos(const char *s, ssize_t maxlen)
|
||||
{
|
||||
size_t len;
|
||||
if (maxlen < 0) {
|
||||
for (len = 0; !sq_must_quote(s[len]); len++);
|
||||
} else {
|
||||
for (len = 0; len < maxlen && !sq_must_quote(s[len]); len++);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* C-style name quoting.
|
||||
*
|
||||
* Does one of three things:
|
||||
* (1) if sb and fp are both NULL, inspect the input name and counts the
|
||||
* number of bytes that are needed to hold c_style quoted version of name,
|
||||
* counting the double quotes around it but not terminating NUL, and
|
||||
* returns it.
|
||||
* However, if name does not need c_style quoting, it returns 0.
|
||||
*
|
||||
* (1) if outbuf and outfp are both NULL, inspect the input name and
|
||||
* counts the number of bytes that are needed to hold c_style
|
||||
* quoted version of name, counting the double quotes around
|
||||
* it but not terminating NUL, and returns it. However, if name
|
||||
* does not need c_style quoting, it returns 0.
|
||||
*
|
||||
* (2) if outbuf is not NULL, it must point at a buffer large enough
|
||||
* to hold the c_style quoted version of name, enclosing double
|
||||
* quotes, and terminating NUL. Fills outbuf with c_style quoted
|
||||
* version of name enclosed in double-quote pair. Return value
|
||||
* is undefined.
|
||||
*
|
||||
* (3) if outfp is not NULL, outputs c_style quoted version of name,
|
||||
* but not enclosed in double-quote pair. Return value is undefined.
|
||||
* (2) if sb or fp are not NULL, it emits the c_style quoted version
|
||||
* of name, enclosed with double quotes if asked and needed only.
|
||||
* Return value is the same as in (1).
|
||||
*/
|
||||
|
||||
static int quote_c_style_counted(const char *name, int namelen,
|
||||
char *outbuf, FILE *outfp, int no_dq)
|
||||
static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
|
||||
struct strbuf *sb, FILE *fp, int no_dq)
|
||||
{
|
||||
#undef EMIT
|
||||
#define EMIT(c) \
|
||||
(outbuf ? (*outbuf++ = (c)) : outfp ? fputc(c, outfp) : (count++))
|
||||
#define EMIT(c) \
|
||||
do { \
|
||||
if (sb) strbuf_addch(sb, (c)); \
|
||||
if (fp) fputc((c), fp); \
|
||||
count++; \
|
||||
} while (0)
|
||||
#define EMITBUF(s, l) \
|
||||
do { \
|
||||
if (sb) strbuf_add(sb, (s), (l)); \
|
||||
if (fp) fwrite((s), (l), 1, fp); \
|
||||
count += (l); \
|
||||
} while (0)
|
||||
|
||||
#define EMITQ() EMIT('\\')
|
||||
size_t len, count = 0;
|
||||
const char *p = name;
|
||||
|
||||
const char *sp;
|
||||
unsigned char ch;
|
||||
int count = 0, needquote = 0;
|
||||
for (;;) {
|
||||
int ch;
|
||||
|
||||
if (!no_dq)
|
||||
EMIT('"');
|
||||
for (sp = name; sp < name + namelen; sp++) {
|
||||
ch = *sp;
|
||||
if (!ch)
|
||||
len = next_quote_pos(p, maxlen);
|
||||
if (len == maxlen || !p[len])
|
||||
break;
|
||||
if ((ch < ' ') || (ch == '"') || (ch == '\\') ||
|
||||
(quote_path_fully && (ch >= 0177))) {
|
||||
needquote = 1;
|
||||
switch (ch) {
|
||||
case '\a': EMITQ(); ch = 'a'; break;
|
||||
case '\b': EMITQ(); ch = 'b'; break;
|
||||
case '\f': EMITQ(); ch = 'f'; break;
|
||||
case '\n': EMITQ(); ch = 'n'; break;
|
||||
case '\r': EMITQ(); ch = 'r'; break;
|
||||
case '\t': EMITQ(); ch = 't'; break;
|
||||
case '\v': EMITQ(); ch = 'v'; break;
|
||||
|
||||
case '\\': /* fallthru */
|
||||
case '"': EMITQ(); break;
|
||||
default:
|
||||
/* octal */
|
||||
EMITQ();
|
||||
EMIT(((ch >> 6) & 03) + '0');
|
||||
EMIT(((ch >> 3) & 07) + '0');
|
||||
ch = (ch & 07) + '0';
|
||||
break;
|
||||
}
|
||||
if (!no_dq && p == name)
|
||||
EMIT('"');
|
||||
|
||||
EMITBUF(p, len);
|
||||
EMIT('\\');
|
||||
p += len;
|
||||
ch = (unsigned char)*p++;
|
||||
if (sq_lookup[ch] >= ' ') {
|
||||
EMIT(sq_lookup[ch]);
|
||||
} else {
|
||||
EMIT(((ch >> 6) & 03) + '0');
|
||||
EMIT(((ch >> 3) & 07) + '0');
|
||||
EMIT(((ch >> 0) & 07) + '0');
|
||||
}
|
||||
EMIT(ch);
|
||||
}
|
||||
|
||||
EMITBUF(p, len);
|
||||
if (p == name) /* no ending quote needed */
|
||||
return 0;
|
||||
|
||||
if (!no_dq)
|
||||
EMIT('"');
|
||||
if (outbuf)
|
||||
*outbuf = 0;
|
||||
|
||||
return needquote ? count : 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
|
||||
size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, int nodq)
|
||||
{
|
||||
int cnt = strlen(name);
|
||||
return quote_c_style_counted(name, cnt, outbuf, outfp, no_dq);
|
||||
return quote_c_style_counted(name, -1, sb, fp, nodq);
|
||||
}
|
||||
|
||||
void write_name_quoted(const char *name, FILE *fp, int terminator)
|
||||
{
|
||||
if (terminator) {
|
||||
quote_c_style(name, NULL, fp, 0);
|
||||
} else {
|
||||
fputs(name, fp);
|
||||
}
|
||||
fputc(terminator, fp);
|
||||
}
|
||||
|
||||
extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
|
||||
const char *name, FILE *fp, int terminator)
|
||||
{
|
||||
int needquote = 0;
|
||||
|
||||
if (terminator) {
|
||||
needquote = next_quote_pos(pfx, pfxlen) < pfxlen
|
||||
|| name[next_quote_pos(name, -1)];
|
||||
}
|
||||
if (needquote) {
|
||||
fputc('"', fp);
|
||||
quote_c_style_counted(pfx, pfxlen, NULL, fp, 1);
|
||||
quote_c_style(name, NULL, fp, 1);
|
||||
fputc('"', fp);
|
||||
} else {
|
||||
fwrite(pfx, pfxlen, 1, fp);
|
||||
fputs(name, fp);
|
||||
}
|
||||
fputc(terminator, fp);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -306,37 +366,6 @@ int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void write_name_quoted(const char *prefix, int prefix_len,
|
||||
const char *name, int quote, FILE *out)
|
||||
{
|
||||
int needquote;
|
||||
|
||||
if (!quote) {
|
||||
no_quote:
|
||||
if (prefix_len)
|
||||
fprintf(out, "%.*s", prefix_len, prefix);
|
||||
fputs(name, out);
|
||||
return;
|
||||
}
|
||||
|
||||
needquote = 0;
|
||||
if (prefix_len)
|
||||
needquote = quote_c_style_counted(prefix, prefix_len,
|
||||
NULL, NULL, 0);
|
||||
if (!needquote)
|
||||
needquote = quote_c_style(name, NULL, NULL, 0);
|
||||
if (needquote) {
|
||||
fputc('"', out);
|
||||
if (prefix_len)
|
||||
quote_c_style_counted(prefix, prefix_len,
|
||||
NULL, out, 1);
|
||||
quote_c_style(name, NULL, out, 1);
|
||||
fputc('"', out);
|
||||
}
|
||||
else
|
||||
goto no_quote;
|
||||
}
|
||||
|
||||
/* quoting as a string literal for other languages */
|
||||
|
||||
void perl_quote_print(FILE *stream, const char *src)
|
||||
|
Reference in New Issue
Block a user