Skip to content

Commit cf9dfc6

Browse files
author
Junio C Hamano
committed
Update git-diff-* to use C-style quoting for funny pathnames.
Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent caf4f58 commit cf9dfc6

File tree

1 file changed

+95
-40
lines changed

1 file changed

+95
-40
lines changed

diff.c

Lines changed: 95 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,46 @@ static const char *diff_opts = "-pu";
1313

1414
static int use_size_cache;
1515

16+
static char *quote_one(const char *str)
17+
{
18+
int needlen;
19+
char *xp;
20+
21+
if (!str)
22+
return NULL;
23+
needlen = quote_c_style(str, NULL, NULL, 0);
24+
if (!needlen)
25+
return strdup(str);
26+
xp = xmalloc(needlen + 1);
27+
quote_c_style(str, xp, NULL, 0);
28+
return xp;
29+
}
30+
31+
static char *quote_two(const char *one, const char *two)
32+
{
33+
int need_one = quote_c_style(one, NULL, NULL, 1);
34+
int need_two = quote_c_style(two, NULL, NULL, 1);
35+
char *xp;
36+
37+
if (need_one + need_two) {
38+
if (!need_one) need_one = strlen(one);
39+
if (!need_two) need_one = strlen(two);
40+
41+
xp = xmalloc(need_one + need_two + 3);
42+
xp[0] = '"';
43+
quote_c_style(one, xp + 1, NULL, 1);
44+
quote_c_style(two, xp + need_one + 1, NULL, 1);
45+
strcpy(xp + need_one + need_two + 1, "\"");
46+
return xp;
47+
}
48+
need_one = strlen(one);
49+
need_two = strlen(two);
50+
xp = xmalloc(need_one + need_two + 1);
51+
strcpy(xp, one);
52+
strcpy(xp + need_one, two);
53+
return xp;
54+
}
55+
1656
static const char *external_diff(void)
1757
{
1858
static const char *external_diff_cmd = NULL;
@@ -133,55 +173,52 @@ static void builtin_diff(const char *name_a,
133173
int complete_rewrite)
134174
{
135175
int i, next_at, cmd_size;
136-
const char *const diff_cmd = "diff -L%s%s -L%s%s";
176+
const char *const diff_cmd = "diff -L%s -L%s";
137177
const char *const diff_arg = "%s %s||:"; /* "||:" is to return 0 */
138178
const char *input_name_sq[2];
139-
const char *path0[2];
140-
const char *path1[2];
141-
const char *name_sq[2];
179+
const char *label_path[2];
142180
char *cmd;
143181

144-
name_sq[0] = sq_quote(name_a);
145-
name_sq[1] = sq_quote(name_b);
146-
147-
/* diff_cmd and diff_arg have 6 %s in total which makes
148-
* the sum of these strings 12 bytes larger than required.
182+
/* diff_cmd and diff_arg have 4 %s in total which makes
183+
* the sum of these strings 8 bytes larger than required.
149184
* we use 2 spaces around diff-opts, and we need to count
150-
* terminating NUL, so we subtract 9 here.
185+
* terminating NUL; we used to subtract 5 here, but we do not
186+
* care about small leaks in this subprocess that is about
187+
* to exec "diff" anymore.
151188
*/
152-
cmd_size = (strlen(diff_cmd) + strlen(diff_opts) +
153-
strlen(diff_arg) - 9);
189+
cmd_size = (strlen(diff_cmd) + strlen(diff_opts) + strlen(diff_arg)
190+
+ 128);
191+
154192
for (i = 0; i < 2; i++) {
155193
input_name_sq[i] = sq_quote(temp[i].name);
156-
if (!strcmp(temp[i].name, "/dev/null")) {
157-
path0[i] = "/dev/null";
158-
path1[i] = "";
159-
} else {
160-
path0[i] = i ? "b/" : "a/";
161-
path1[i] = name_sq[i];
162-
}
163-
cmd_size += (strlen(path0[i]) + strlen(path1[i]) +
164-
strlen(input_name_sq[i]));
194+
if (!strcmp(temp[i].name, "/dev/null"))
195+
label_path[i] = "/dev/null";
196+
else if (!i)
197+
label_path[i] = sq_quote(quote_two("a/", name_a));
198+
else
199+
label_path[i] = sq_quote(quote_two("b/", name_b));
200+
cmd_size += (strlen(label_path[i]) + strlen(input_name_sq[i]));
165201
}
166202

167203
cmd = xmalloc(cmd_size);
168204

169205
next_at = 0;
170206
next_at += snprintf(cmd+next_at, cmd_size-next_at,
171-
diff_cmd,
172-
path0[0], path1[0], path0[1], path1[1]);
207+
diff_cmd, label_path[0], label_path[1]);
173208
next_at += snprintf(cmd+next_at, cmd_size-next_at,
174209
" %s ", diff_opts);
175210
next_at += snprintf(cmd+next_at, cmd_size-next_at,
176211
diff_arg, input_name_sq[0], input_name_sq[1]);
177212

178-
printf("diff --git a/%s b/%s\n", name_a, name_b);
179-
if (!path1[0][0]) {
213+
printf("diff --git %s %s\n",
214+
quote_two("a/", name_a), quote_two("b/", name_b));
215+
if (label_path[0][0] == '/') {
216+
/* dev/null */
180217
printf("new file mode %s\n", temp[1].mode);
181218
if (xfrm_msg && xfrm_msg[0])
182219
puts(xfrm_msg);
183220
}
184-
else if (!path1[1][0]) {
221+
else if (label_path[1][0] == '/') {
185222
printf("deleted file mode %s\n", temp[0].mode);
186223
if (xfrm_msg && xfrm_msg[0])
187224
puts(xfrm_msg);
@@ -619,6 +656,7 @@ static void run_diff(struct diff_filepair *p)
619656
struct diff_filespec *two;
620657
const char *name;
621658
const char *other;
659+
char *name_munged, *other_munged;
622660
int complete_rewrite = 0;
623661
int len;
624662

@@ -631,6 +669,8 @@ static void run_diff(struct diff_filepair *p)
631669

632670
name = p->one->path;
633671
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
672+
name_munged = quote_one(name);
673+
other_munged = quote_one(other);
634674
one = p->one; two = p->two;
635675

636676
diff_fill_sha1_info(one);
@@ -644,15 +684,15 @@ static void run_diff(struct diff_filepair *p)
644684
"copy from %s\n"
645685
"copy to %s\n",
646686
(int)(0.5 + p->score * 100.0/MAX_SCORE),
647-
name, other);
687+
name_munged, other_munged);
648688
break;
649689
case DIFF_STATUS_RENAMED:
650690
len += snprintf(msg + len, sizeof(msg) - len,
651691
"similarity index %d%%\n"
652692
"rename from %s\n"
653693
"rename to %s\n",
654694
(int)(0.5 + p->score * 100.0/MAX_SCORE),
655-
name, other);
695+
name_munged, other_munged);
656696
break;
657697
case DIFF_STATUS_MODIFIED:
658698
if (p->score) {
@@ -702,6 +742,9 @@ static void run_diff(struct diff_filepair *p)
702742
else
703743
run_external_diff(pgm, name, other, one, two, xfrm_msg,
704744
complete_rewrite);
745+
746+
free(name_munged);
747+
free(other_munged);
705748
}
706749

707750
void diff_setup(struct diff_options *options)
@@ -878,16 +921,13 @@ static void diff_flush_raw(struct diff_filepair *p,
878921
{
879922
int two_paths;
880923
char status[10];
924+
const char *path_one, *path_two;
881925

926+
path_one = p->one->path;
927+
path_two = p->two->path;
882928
if (line_termination) {
883-
const char *const err =
884-
"path %s cannot be expressed without -z";
885-
if (strchr(p->one->path, line_termination) ||
886-
strchr(p->one->path, inter_name_termination))
887-
die(err, p->one->path);
888-
if (strchr(p->two->path, line_termination) ||
889-
strchr(p->two->path, inter_name_termination))
890-
die(err, p->two->path);
929+
path_one = quote_one(path_one);
930+
path_two = quote_one(path_two);
891931
}
892932

893933
if (p->score)
@@ -915,16 +955,29 @@ static void diff_flush_raw(struct diff_filepair *p,
915955
p->one->mode, p->two->mode, sha1_to_hex(p->one->sha1));
916956
printf("%s ", sha1_to_hex(p->two->sha1));
917957
}
918-
printf("%s%c%s",status, inter_name_termination, p->one->path);
958+
printf("%s%c%s", status, inter_name_termination, path_one);
919959
if (two_paths)
920-
printf("%c%s", inter_name_termination, p->two->path);
960+
printf("%c%s", inter_name_termination, path_two);
921961
putchar(line_termination);
962+
if (path_one != p->one->path)
963+
free((void*)path_one);
964+
if (path_two != p->two->path)
965+
free((void*)path_two);
922966
}
923967

924968
static void diff_flush_name(struct diff_filepair *p,
969+
int inter_name_termination,
925970
int line_termination)
926971
{
927-
printf("%s%c", p->two->path, line_termination);
972+
char *path = p->two->path;
973+
974+
if (line_termination)
975+
path = quote_one(p->two->path);
976+
else
977+
path = p->two->path;
978+
printf("%s%c", path, line_termination);
979+
if (p->two->path != path)
980+
free(path);
928981
}
929982

930983
int diff_unmodified_pair(struct diff_filepair *p)
@@ -1111,7 +1164,9 @@ void diff_flush(struct diff_options *options)
11111164
diff_output_format);
11121165
break;
11131166
case DIFF_FORMAT_NAME:
1114-
diff_flush_name(p, line_termination);
1167+
diff_flush_name(p,
1168+
inter_name_termination,
1169+
line_termination);
11151170
break;
11161171
}
11171172
diff_free_filepair(q->queue[i]);

0 commit comments

Comments
 (0)