Merge branch 'ns/am-raw-email'
[git] / userdiff.c
1 #include "userdiff.h"
2 #include "cache.h"
3 #include "attr.h"
4
5 static struct userdiff_driver *drivers;
6 static int ndrivers;
7 static int drivers_alloc;
8
9 #define PATTERNS(name, pattern, word_regex)                     \
10         { name, NULL, -1, { pattern, REG_EXTENDED }, word_regex }
11 static struct userdiff_driver builtin_drivers[] = {
12 PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
13          "[^<>= \t]+|[^[:space:]]|[\x80-\xff]+"),
14 PATTERNS("java",
15          "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
16          "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$",
17          /* -- */
18          "[a-zA-Z_][a-zA-Z0-9_]*"
19          "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
20          "|[-+*/<>%&^|=!]="
21          "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"
22          "|[^[:space:]]|[\x80-\xff]+"),
23 PATTERNS("objc",
24          /* Negate C statements that can look like functions */
25          "!^[ \t]*(do|for|if|else|return|switch|while)\n"
26          /* Objective-C methods */
27          "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n"
28          /* C functions */
29          "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$\n"
30          /* Objective-C class/protocol definitions */
31          "^(@(implementation|interface|protocol)[ \t].*)$",
32          /* -- */
33          "[a-zA-Z_][a-zA-Z0-9_]*"
34          "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
35          "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
36          "|[^[:space:]]|[\x80-\xff]+"),
37 PATTERNS("pascal",
38          "^((procedure|function|constructor|destructor|interface|"
39                 "implementation|initialization|finalization)[ \t]*.*)$"
40          "\n"
41          "^(.*=[ \t]*(class|record).*)$",
42          /* -- */
43          "[a-zA-Z_][a-zA-Z0-9_]*"
44          "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
45          "|<>|<=|>=|:=|\\.\\."
46          "|[^[:space:]]|[\x80-\xff]+"),
47 PATTERNS("php", "^[\t ]*((function|class).*)",
48          /* -- */
49          "[a-zA-Z_][a-zA-Z0-9_]*"
50          "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
51          "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->"
52          "|[^[:space:]]|[\x80-\xff]+"),
53 PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$",
54          /* -- */
55          "[a-zA-Z_][a-zA-Z0-9_]*"
56          "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
57          "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"
58          "|[^[:space:]|[\x80-\xff]+"),
59          /* -- */
60 PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
61          /* -- */
62          "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
63          "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
64          "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"
65          "|[^[:space:]|[\x80-\xff]+"),
66 PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
67          "[={}\"]|[^={}\" \t]+"),
68 PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
69          "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+|[^[:space:]]"),
70 PATTERNS("cpp",
71          /* Jump targets or access declarations */
72          "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n"
73          /* C/++ functions/methods at top level */
74          "^([A-Za-z_][A-Za-z_0-9]*([ \t]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n"
75          /* compound type at top level */
76          "^((struct|class|enum)[^;]*)$",
77          /* -- */
78          "[a-zA-Z_][a-zA-Z0-9_]*"
79          "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
80          "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
81          "|[^[:space:]]|[\x80-\xff]+"),
82 { "default", NULL, -1, { NULL, 0 } },
83 };
84 #undef PATTERNS
85
86 static struct userdiff_driver driver_true = {
87         "diff=true",
88         NULL,
89         0,
90         { NULL, 0 }
91 };
92
93 static struct userdiff_driver driver_false = {
94         "!diff",
95         NULL,
96         1,
97         { NULL, 0 }
98 };
99
100 static struct userdiff_driver *userdiff_find_by_namelen(const char *k, int len)
101 {
102         int i;
103         for (i = 0; i < ndrivers; i++) {
104                 struct userdiff_driver *drv = drivers + i;
105                 if (!strncmp(drv->name, k, len) && !drv->name[len])
106                         return drv;
107         }
108         for (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {
109                 struct userdiff_driver *drv = builtin_drivers + i;
110                 if (!strncmp(drv->name, k, len) && !drv->name[len])
111                         return drv;
112         }
113         return NULL;
114 }
115
116 static struct userdiff_driver *parse_driver(const char *var,
117                 const char *value, const char *type)
118 {
119         struct userdiff_driver *drv;
120         const char *dot;
121         const char *name;
122         int namelen;
123
124         if (prefixcmp(var, "diff."))
125                 return NULL;
126         dot = strrchr(var, '.');
127         if (dot == var + 4)
128                 return NULL;
129         if (strcmp(type, dot+1))
130                 return NULL;
131
132         name = var + 5;
133         namelen = dot - name;
134         drv = userdiff_find_by_namelen(name, namelen);
135         if (!drv) {
136                 ALLOC_GROW(drivers, ndrivers+1, drivers_alloc);
137                 drv = &drivers[ndrivers++];
138                 memset(drv, 0, sizeof(*drv));
139                 drv->name = xmemdupz(name, namelen);
140                 drv->binary = -1;
141         }
142         return drv;
143 }
144
145 static int parse_funcname(struct userdiff_funcname *f, const char *k,
146                 const char *v, int cflags)
147 {
148         if (git_config_string(&f->pattern, k, v) < 0)
149                 return -1;
150         f->cflags = cflags;
151         return 1;
152 }
153
154 static int parse_string(const char **d, const char *k, const char *v)
155 {
156         if (git_config_string(d, k, v) < 0)
157                 return -1;
158         return 1;
159 }
160
161 static int parse_tristate(int *b, const char *k, const char *v)
162 {
163         if (v && !strcasecmp(v, "auto"))
164                 *b = -1;
165         else
166                 *b = git_config_bool(k, v);
167         return 1;
168 }
169
170 int userdiff_config(const char *k, const char *v)
171 {
172         struct userdiff_driver *drv;
173
174         if ((drv = parse_driver(k, v, "funcname")))
175                 return parse_funcname(&drv->funcname, k, v, 0);
176         if ((drv = parse_driver(k, v, "xfuncname")))
177                 return parse_funcname(&drv->funcname, k, v, REG_EXTENDED);
178         if ((drv = parse_driver(k, v, "binary")))
179                 return parse_tristate(&drv->binary, k, v);
180         if ((drv = parse_driver(k, v, "command")))
181                 return parse_string(&drv->external, k, v);
182         if ((drv = parse_driver(k, v, "textconv")))
183                 return parse_string(&drv->textconv, k, v);
184         if ((drv = parse_driver(k, v, "wordregex")))
185                 return parse_string(&drv->word_regex, k, v);
186
187         return 0;
188 }
189
190 struct userdiff_driver *userdiff_find_by_name(const char *name) {
191         int len = strlen(name);
192         return userdiff_find_by_namelen(name, len);
193 }
194
195 struct userdiff_driver *userdiff_find_by_path(const char *path)
196 {
197         static struct git_attr *attr;
198         struct git_attr_check check;
199
200         if (!attr)
201                 attr = git_attr("diff", 4);
202         check.attr = attr;
203
204         if (!path)
205                 return NULL;
206         if (git_checkattr(path, 1, &check))
207                 return NULL;
208
209         if (ATTR_TRUE(check.value))
210                 return &driver_true;
211         if (ATTR_FALSE(check.value))
212                 return &driver_false;
213         if (ATTR_UNSET(check.value))
214                 return NULL;
215         return userdiff_find_by_name(check.value);
216 }