1/*
2 * Copyright (C) 2005 Junio C Hamano
3 */
4#include <limits.h>
5#include "cache.h"
6#include "strbuf.h"
7#include "diff.h"
8
9static int matches_pathspec(const char *name, const char **spec, int cnt)
10{
11 int i;
12 int namelen = strlen(name);
13 for (i = 0; i < cnt; i++) {
14 int speclen = strlen(spec[i]);
15 if (! strncmp(spec[i], name, speclen) &&
16 speclen <= namelen &&
17 (name[speclen] == 0 ||
18 name[speclen] == '/'))
19 return 1;
20 }
21 return 0;
22}
23
24static int parse_oneside_change(const char *cp, struct diff_spec *one,
25 char *path)
26{
27 int ch;
28
29 one->file_valid = one->sha1_valid = 1;
30 one->mode = 0;
31 while ((ch = *cp) && '0' <= ch && ch <= '7') {
32 one->mode = (one->mode << 3) | (ch - '0');
33 cp++;
34 }
35
36 if (strncmp(cp, "\tblob\t", 6))
37 return -1;
38 cp += 6;
39 if (get_sha1_hex(cp, one->blob_sha1))
40 return -1;
41 cp += 40;
42 if (*cp++ != '\t')
43 return -1;
44 strcpy(path, cp);
45 return 0;
46}
47
48static int parse_diff_raw_output(const char *buf,
49 const char **spec, int cnt, int reverse)
50{
51 struct diff_spec old, new;
52 char path[PATH_MAX];
53 const char *cp = buf;
54 int ch;
55
56 switch (*cp++) {
57 case 'U':
58 if (!cnt || matches_pathspec(cp + 1, spec, cnt))
59 diff_unmerge(cp + 1);
60 return 0;
61 case '+':
62 old.file_valid = 0;
63 parse_oneside_change(cp, &new, path);
64 break;
65 case '-':
66 new.file_valid = 0;
67 parse_oneside_change(cp, &old, path);
68 break;
69 case '*':
70 old.file_valid = old.sha1_valid =
71 new.file_valid = new.sha1_valid = 1;
72 old.mode = new.mode = 0;
73 while ((ch = *cp) && ('0' <= ch && ch <= '7')) {
74 old.mode = (old.mode << 3) | (ch - '0');
75 cp++;
76 }
77 if (strncmp(cp, "->", 2))
78 return -1;
79 cp += 2;
80 while ((ch = *cp) && ('0' <= ch && ch <= '7')) {
81 new.mode = (new.mode << 3) | (ch - '0');
82 cp++;
83 }
84 if (strncmp(cp, "\tblob\t", 6))
85 return -1;
86 cp += 6;
87 if (get_sha1_hex(cp, old.blob_sha1))
88 return -1;
89 cp += 40;
90 if (strncmp(cp, "->", 2))
91 return -1;
92 cp += 2;
93 if (get_sha1_hex(cp, new.blob_sha1))
94 return -1;
95 cp += 40;
96 if (*cp++ != '\t')
97 return -1;
98 strcpy(path, cp);
99 break;
100 default:
101 return -1;
102 }
103 if (!cnt || matches_pathspec(path, spec, cnt)) {
104 if (reverse)
105 run_external_diff(path, &new, &old);
106 else
107 run_external_diff(path, &old, &new);
108 }
109 return 0;
110}
111
112static const char *diff_helper_usage =
113"git-diff-helper [-R] [-z] paths...";
114
115int main(int ac, const char **av) {
116 struct strbuf sb;
117 int reverse = 0;
118 int line_termination = '\n';
119
120 strbuf_init(&sb);
121
122 while (1 < ac && av[1][0] == '-') {
123 if (av[1][1] == 'R')
124 reverse = 1;
125 else if (av[1][1] == 'z')
126 line_termination = 0;
127 else
128 usage(diff_helper_usage);
129 ac--; av++;
130 }
131 /* the remaining parameters are paths patterns */
132
133 while (1) {
134 int status;
135 read_line(&sb, stdin, line_termination);
136 if (sb.eof)
137 break;
138 status = parse_diff_raw_output(sb.buf, av+1, ac-1, reverse);
139 if (status)
140 fprintf(stderr, "cannot parse %s\n", sb.buf);
141 }
142 return 0;
143}