Update git-apply to use C-style quoting for funny pathnames.

This is a backport so that maintenance branch can understand
diff output that uses C-style quoting produced by newer tools.

Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Junio C Hamano 2005-10-16 11:05:14 -07:00
parent 585793d96c
commit ea56188a24

208
apply.c
View File

@ -15,6 +15,7 @@
#include <ctype.h> #include <ctype.h>
#include <fnmatch.h> #include <fnmatch.h>
#include "cache.h" #include "cache.h"
#include "quote.h"
// We default to the merge behaviour, since that's what most people would // We default to the merge behaviour, since that's what most people would
// expect. // expect.
@ -142,6 +143,35 @@ static char * find_name(const char *line, char *def, int p_value, int terminate)
const char *start = line; const char *start = line;
char *name; char *name;
if (*line == '"') {
/* Proposed "new-style" GNU patch/diff format; see
* http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
*/
name = unquote_c_style(line, NULL);
if (name) {
char *cp = name;
while (p_value) {
cp = strchr(name, '/');
if (!cp)
break;
cp++;
p_value--;
}
if (cp) {
/* name can later be freed, so we need
* to memmove, not just return cp
*/
memmove(name, cp, strlen(cp) + 1);
free(def);
return name;
}
else {
free(name);
name = NULL;
}
}
}
for (;;) { for (;;) {
char c = *line; char c = *line;
@ -231,37 +261,29 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
*/ */
static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew) static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
{ {
int len;
const char *name;
if (!orig_name && !isnull) if (!orig_name && !isnull)
return find_name(line, NULL, 1, 0); return find_name(line, NULL, 1, 0);
name = "/dev/null";
len = 9;
if (orig_name) { if (orig_name) {
int len;
const char *name;
char *another;
name = orig_name; name = orig_name;
len = strlen(name); len = strlen(name);
if (isnull) if (isnull)
die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr); die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
} another = find_name(line, NULL, 1, 0);
if (!another || memcmp(another, name, len))
if (*name == '/') die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
goto absolute_path; free(another);
for (;;) {
char c = *line++;
if (c == '\n')
break;
if (c != '/')
continue;
absolute_path:
if (memcmp(line, name, len) || line[len] != '\n')
break;
return orig_name; return orig_name;
} }
die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr); else {
return NULL; /* expect "/dev/null" */
if (memcmp("/dev/null", line, 9) || line[9] != '\n')
die("git-apply: bad git-diff - expected /dev/null on line %d", linenr);
return NULL;
}
} }
static int gitdiff_oldname(const char *line, struct patch *patch) static int gitdiff_oldname(const char *line, struct patch *patch)
@ -353,29 +375,124 @@ static int gitdiff_unrecognized(const char *line, struct patch *patch)
return -1; return -1;
} }
static char *git_header_name(char *line) static const char *stop_at_slash(const char *line, int llen)
{
int i;
for (i = 0; i < llen; i++) {
int ch = line[i];
if (ch == '/')
return line + i;
}
return NULL;
}
/* This is to extract the same name that appears on "diff --git"
* line. We do not find and return anything if it is a rename
* patch, and it is OK because we will find the name elsewhere.
* We need to reliably find name only when it is mode-change only,
* creation or deletion of an empty file. In any of these cases,
* both sides are the same name under a/ and b/ respectively.
*/
static char *git_header_name(char *line, int llen)
{ {
int len; int len;
char *name, *second; const char *name;
const char *second = NULL;
/* line += strlen("diff --git ");
* Find the first '/' llen -= strlen("diff --git ");
*/
name = line; if (*line == '"') {
for (;;) { const char *cp;
char c = *name++; char *first = unquote_c_style(line, &second);
if (c == '\n') if (!first)
return NULL; return NULL;
if (c == '/')
break; /* advance to the first slash */
cp = stop_at_slash(first, strlen(first));
if (!cp || cp == first) {
/* we do not accept absolute paths */
free_first_and_fail:
free(first);
return NULL;
}
len = strlen(cp+1);
memmove(first, cp+1, len+1); /* including NUL */
/* second points at one past closing dq of name.
* find the second name.
*/
while ((second < line + llen) && isspace(*second))
second++;
if (line + llen <= second)
goto free_first_and_fail;
if (*second == '"') {
char *sp = unquote_c_style(second, NULL);
if (!sp)
goto free_first_and_fail;
cp = stop_at_slash(sp, strlen(sp));
if (!cp || cp == sp) {
free_both_and_fail:
free(sp);
goto free_first_and_fail;
}
/* They must match, otherwise ignore */
if (strcmp(cp+1, first))
goto free_both_and_fail;
free(sp);
return first;
}
/* unquoted second */
cp = stop_at_slash(second, line + llen - second);
if (!cp || cp == second)
goto free_first_and_fail;
cp++;
if (line + llen - cp != len + 1 ||
memcmp(first, cp, len))
goto free_first_and_fail;
return first;
} }
/* /* unquoted first name */
* We don't accept absolute paths (/dev/null) as possibly valid name = stop_at_slash(line, llen);
*/ if (!name || name == line)
if (name == line+1)
return NULL; return NULL;
name++;
/* since the first name is unquoted, a dq if exists must be
* the beginning of the second name.
*/
for (second = name; second < line + llen; second++) {
if (*second == '"') {
const char *cp = second;
const char *np;
char *sp = unquote_c_style(second, NULL);
if (!sp)
return NULL;
np = stop_at_slash(sp, strlen(sp));
if (!np || np == sp) {
free_second_and_fail:
free(sp);
return NULL;
}
np++;
len = strlen(np);
if (len < cp - name &&
!strncmp(np, name, len) &&
isspace(name[len])) {
/* Good */
memmove(sp, np, len + 1);
return sp;
}
goto free_second_and_fail;
}
}
/* /*
* Accept a name only if it shows up twice, exactly the same * Accept a name only if it shows up twice, exactly the same
* form. * form.
@ -423,7 +540,7 @@ static int parse_git_header(char *line, int len, unsigned int size, struct patch
* or removing or adding empty files), so we get * or removing or adding empty files), so we get
* the default name from the header. * the default name from the header.
*/ */
patch->def_name = git_header_name(line + strlen("diff --git ")); patch->def_name = git_header_name(line, len);
line += len; line += len;
size -= len; size -= len;
@ -756,11 +873,18 @@ static void show_stats(struct patch *patch)
{ {
const char *prefix = ""; const char *prefix = "";
char *name = patch->new_name; char *name = patch->new_name;
char *qname = NULL;
int len, max, add, del, total; int len, max, add, del, total;
if (!name) if (!name)
name = patch->old_name; name = patch->old_name;
if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
qname = xmalloc(len + 1);
quote_c_style(name, qname, NULL, 0);
name = qname;
}
/* /*
* "scale" the filename * "scale" the filename
*/ */
@ -798,6 +922,8 @@ static void show_stats(struct patch *patch)
printf(" %s%-*s |%5d %.*s%.*s\n", prefix, printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
len, name, patch->lines_added + patch->lines_deleted, len, name, patch->lines_added + patch->lines_deleted,
add, pluses, del, minuses); add, pluses, del, minuses);
if (qname)
free(qname);
} }
static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size) static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
@ -1220,12 +1346,16 @@ static void patch_stats(struct patch *patch)
if (lines > max_change) if (lines > max_change)
max_change = lines; max_change = lines;
if (patch->old_name) { if (patch->old_name) {
int len = strlen(patch->old_name); int len = quote_c_style(patch->old_name, NULL, NULL, 0);
if (!len)
len = strlen(patch->old_name);
if (len > max_len) if (len > max_len)
max_len = len; max_len = len;
} }
if (patch->new_name) { if (patch->new_name) {
int len = strlen(patch->new_name); int len = quote_c_style(patch->new_name, NULL, NULL, 0);
if (!len)
len = strlen(patch->new_name);
if (len > max_len) if (len > max_len)
max_len = len; max_len = len;
} }