1 | #include "cache.h"
|
---|
2 | #include "string-list.h"
|
---|
3 | #include "mailmap.h"
|
---|
4 |
|
---|
5 | #define DEBUG_MAILMAP 0
|
---|
6 | #if DEBUG_MAILMAP
|
---|
7 | #define debug_mm(...) fprintf(stderr, __VA_ARGS__)
|
---|
8 | #define debug_str(X) ((X) ? (X) : "(none)")
|
---|
9 | #else
|
---|
10 | static inline void debug_mm(const char *format, ...) {}
|
---|
11 | static inline const char *debug_str(const char *s) { return s; }
|
---|
12 | #endif
|
---|
13 |
|
---|
14 | const char *git_mailmap_file;
|
---|
15 | const char *git_mailmap_blob;
|
---|
16 |
|
---|
17 | struct mailmap_info {
|
---|
18 | char *name;
|
---|
19 | char *email;
|
---|
20 | };
|
---|
21 |
|
---|
22 | struct mailmap_entry {
|
---|
23 | /* name and email for the simple mail-only case */
|
---|
24 | char *name;
|
---|
25 | char *email;
|
---|
26 |
|
---|
27 | /* name and email for the complex mail and name matching case */
|
---|
28 | struct string_list namemap;
|
---|
29 | };
|
---|
30 |
|
---|
31 | static void free_mailmap_info(void *p, const char *s)
|
---|
32 | {
|
---|
33 | struct mailmap_info *mi = (struct mailmap_info *)p;
|
---|
34 | debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n",
|
---|
35 | s, debug_str(mi->name), debug_str(mi->email));
|
---|
36 | free(mi->name);
|
---|
37 | free(mi->email);
|
---|
38 | }
|
---|
39 |
|
---|
40 | static void free_mailmap_entry(void *p, const char *s)
|
---|
41 | {
|
---|
42 | struct mailmap_entry *me = (struct mailmap_entry *)p;
|
---|
43 | debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n",
|
---|
44 | s, me->namemap.nr);
|
---|
45 | debug_mm("mailmap: - simple: '%s' <%s>\n",
|
---|
46 | debug_str(me->name), debug_str(me->email));
|
---|
47 |
|
---|
48 | free(me->name);
|
---|
49 | free(me->email);
|
---|
50 |
|
---|
51 | me->namemap.strdup_strings = 1;
|
---|
52 | string_list_clear_func(&me->namemap, free_mailmap_info);
|
---|
53 | }
|
---|
54 |
|
---|
55 | /*
|
---|
56 | * On some systems (e.g. MinGW 4.0), string.h has _only_ inline
|
---|
57 | * definition of strcasecmp and no non-inline implementation is
|
---|
58 | * supplied anywhere, which is, eh, "unusual"; we cannot take an
|
---|
59 | * address of such a function to store it in namemap.cmp. This is
|
---|
60 | * here as a workaround---do not assign strcasecmp directly to
|
---|
61 | * namemap.cmp until we know no systems that matter have such an
|
---|
62 | * "unusual" string.h.
|
---|
63 | */
|
---|
64 | static int namemap_cmp(const char *a, const char *b)
|
---|
65 | {
|
---|
66 | return strcasecmp(a, b);
|
---|
67 | }
|
---|
68 |
|
---|
69 | static void add_mapping(struct string_list *map,
|
---|
70 | char *new_name, char *new_email,
|
---|
71 | char *old_name, char *old_email)
|
---|
72 | {
|
---|
73 | struct mailmap_entry *me;
|
---|
74 | int index;
|
---|
75 |
|
---|
76 | if (old_email == NULL) {
|
---|
77 | old_email = new_email;
|
---|
78 | new_email = NULL;
|
---|
79 | }
|
---|
80 |
|
---|
81 | if ((index = string_list_find_insert_index(map, old_email, 1)) < 0) {
|
---|
82 | /* mailmap entry exists, invert index value */
|
---|
83 | index = -1 - index;
|
---|
84 | me = (struct mailmap_entry *)map->items[index].util;
|
---|
85 | } else {
|
---|
86 | /* create mailmap entry */
|
---|
87 | struct string_list_item *item;
|
---|
88 |
|
---|
89 | item = string_list_insert_at_index(map, index, old_email);
|
---|
90 | me = xcalloc(1, sizeof(struct mailmap_entry));
|
---|
91 | me->namemap.strdup_strings = 1;
|
---|
92 | me->namemap.cmp = namemap_cmp;
|
---|
93 | item->util = me;
|
---|
94 | }
|
---|
95 |
|
---|
96 | if (old_name == NULL) {
|
---|
97 | debug_mm("mailmap: adding (simple) entry for %s at index %d\n",
|
---|
98 | old_email, index);
|
---|
99 | /* Replace current name and new email for simple entry */
|
---|
100 | if (new_name) {
|
---|
101 | free(me->name);
|
---|
102 | me->name = xstrdup(new_name);
|
---|
103 | }
|
---|
104 | if (new_email) {
|
---|
105 | free(me->email);
|
---|
106 | me->email = xstrdup(new_email);
|
---|
107 | }
|
---|
108 | } else {
|
---|
109 | struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
|
---|
110 | debug_mm("mailmap: adding (complex) entry for %s at index %d\n",
|
---|
111 | old_email, index);
|
---|
112 | if (new_name)
|
---|
113 | mi->name = xstrdup(new_name);
|
---|
114 | if (new_email)
|
---|
115 | mi->email = xstrdup(new_email);
|
---|
116 | string_list_insert(&me->namemap, old_name)->util = mi;
|
---|
117 | }
|
---|
118 |
|
---|
119 | debug_mm("mailmap: '%s' <%s> -> '%s' <%s>\n",
|
---|
120 | debug_str(old_name), old_email,
|
---|
121 | debug_str(new_name), debug_str(new_email));
|
---|
122 | }
|
---|
123 |
|
---|
124 | static char *parse_name_and_email(char *buffer, char **name,
|
---|
125 | char **email, int allow_empty_email)
|
---|
126 | {
|
---|
127 | char *left, *right, *nstart, *nend;
|
---|
128 | *name = *email = NULL;
|
---|
129 |
|
---|
130 | if ((left = strchr(buffer, '<')) == NULL)
|
---|
131 | return NULL;
|
---|
132 | if ((right = strchr(left+1, '>')) == NULL)
|
---|
133 | return NULL;
|
---|
134 | if (!allow_empty_email && (left+1 == right))
|
---|
135 | return NULL;
|
---|
136 |
|
---|
137 | /* remove whitespace from beginning and end of name */
|
---|
138 | nstart = buffer;
|
---|
139 | while (isspace(*nstart) && nstart < left)
|
---|
140 | ++nstart;
|
---|
141 | nend = left-1;
|
---|
142 | while (nend > nstart && isspace(*nend))
|
---|
143 | --nend;
|
---|
144 |
|
---|
145 | *name = (nstart <= nend ? nstart : NULL);
|
---|
146 | *email = left+1;
|
---|
147 | *(nend+1) = '\0';
|
---|
148 | *right++ = '\0';
|
---|
149 |
|
---|
150 | return (*right == '\0' ? NULL : right);
|
---|
151 | }
|
---|
152 |
|
---|
153 | static void read_mailmap_line(struct string_list *map, char *buffer,
|
---|
154 | char **repo_abbrev)
|
---|
155 | {
|
---|
156 | char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL;
|
---|
157 | if (buffer[0] == '#') {
|
---|
158 | static const char abbrev[] = "# repo-abbrev:";
|
---|
159 | int abblen = sizeof(abbrev) - 1;
|
---|
160 | int len = strlen(buffer);
|
---|
161 |
|
---|
162 | if (!repo_abbrev)
|
---|
163 | return;
|
---|
164 |
|
---|
165 | if (len && buffer[len - 1] == '\n')
|
---|
166 | buffer[--len] = 0;
|
---|
167 | if (!strncmp(buffer, abbrev, abblen)) {
|
---|
168 | char *cp;
|
---|
169 |
|
---|
170 | free(*repo_abbrev);
|
---|
171 | *repo_abbrev = xmalloc(len);
|
---|
172 |
|
---|
173 | for (cp = buffer + abblen; isspace(*cp); cp++)
|
---|
174 | ; /* nothing */
|
---|
175 | strcpy(*repo_abbrev, cp);
|
---|
176 | }
|
---|
177 | return;
|
---|
178 | }
|
---|
179 | if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
|
---|
180 | parse_name_and_email(name2, &name2, &email2, 1);
|
---|
181 |
|
---|
182 | if (email1)
|
---|
183 | add_mapping(map, name1, email1, name2, email2);
|
---|
184 | }
|
---|
185 |
|
---|
186 | static int read_mailmap_file(struct string_list *map, const char *filename,
|
---|
187 | char **repo_abbrev)
|
---|
188 | {
|
---|
189 | char buffer[1024];
|
---|
190 | FILE *f;
|
---|
191 |
|
---|
192 | if (!filename)
|
---|
193 | return 0;
|
---|
194 |
|
---|
195 | f = fopen(filename, "r");
|
---|
196 | if (!f) {
|
---|
197 | if (errno == ENOENT)
|
---|
198 | return 0;
|
---|
199 | return error("unable to open mailmap at %s: %s",
|
---|
200 | filename, strerror(errno));
|
---|
201 | }
|
---|
202 |
|
---|
203 | while (fgets(buffer, sizeof(buffer), f) != NULL)
|
---|
204 | read_mailmap_line(map, buffer, repo_abbrev);
|
---|
205 | fclose(f);
|
---|
206 | return 0;
|
---|
207 | }
|
---|
208 |
|
---|
209 | static void read_mailmap_string(struct string_list *map, char *buf,
|
---|
210 | char **repo_abbrev)
|
---|
211 | {
|
---|
212 | while (*buf) {
|
---|
213 | char *end = strchrnul(buf, '\n');
|
---|
214 |
|
---|
215 | if (*end)
|
---|
216 | *end++ = '\0';
|
---|
217 |
|
---|
218 | read_mailmap_line(map, buf, repo_abbrev);
|
---|
219 | buf = end;
|
---|
220 | }
|
---|
221 | }
|
---|
222 |
|
---|
223 | static int read_mailmap_blob(struct string_list *map,
|
---|
224 | const char *name,
|
---|
225 | char **repo_abbrev)
|
---|
226 | {
|
---|
227 | unsigned char sha1[20];
|
---|
228 | char *buf;
|
---|
229 | unsigned long size;
|
---|
230 | enum object_type type;
|
---|
231 |
|
---|
232 | if (!name)
|
---|
233 | return 0;
|
---|
234 | if (get_sha1(name, sha1) < 0)
|
---|
235 | return 0;
|
---|
236 |
|
---|
237 | buf = read_sha1_file(sha1, &type, &size);
|
---|
238 | if (!buf)
|
---|
239 | return error("unable to read mailmap object at %s", name);
|
---|
240 | if (type != OBJ_BLOB)
|
---|
241 | return error("mailmap is not a blob: %s", name);
|
---|
242 |
|
---|
243 | read_mailmap_string(map, buf, repo_abbrev);
|
---|
244 |
|
---|
245 | free(buf);
|
---|
246 | return 0;
|
---|
247 | }
|
---|
248 |
|
---|
249 | int read_mailmap(struct string_list *map, char **repo_abbrev)
|
---|
250 | {
|
---|
251 | int err = 0;
|
---|
252 |
|
---|
253 | map->strdup_strings = 1;
|
---|
254 | map->cmp = namemap_cmp;
|
---|
255 |
|
---|
256 | if (!git_mailmap_blob && is_bare_repository())
|
---|
257 | git_mailmap_blob = "HEAD:.mailmap";
|
---|
258 |
|
---|
259 | err |= read_mailmap_file(map, ".mailmap", repo_abbrev);
|
---|
260 | err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev);
|
---|
261 | err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev);
|
---|
262 | return err;
|
---|
263 | }
|
---|
264 |
|
---|
265 | void clear_mailmap(struct string_list *map)
|
---|
266 | {
|
---|
267 | debug_mm("mailmap: clearing %d entries...\n", map->nr);
|
---|
268 | map->strdup_strings = 1;
|
---|
269 | string_list_clear_func(map, free_mailmap_entry);
|
---|
270 | debug_mm("mailmap: cleared\n");
|
---|
271 | }
|
---|
272 |
|
---|
273 | /*
|
---|
274 | * Look for an entry in map that match string[0:len]; string[len]
|
---|
275 | * does not have to be NUL (but it could be).
|
---|
276 | */
|
---|
277 | static struct string_list_item *lookup_prefix(struct string_list *map,
|
---|
278 | const char *string, size_t len)
|
---|
279 | {
|
---|
280 | int i = string_list_find_insert_index(map, string, 1);
|
---|
281 | if (i < 0) {
|
---|
282 | /* exact match */
|
---|
283 | i = -1 - i;
|
---|
284 | if (!string[len])
|
---|
285 | return &map->items[i];
|
---|
286 | /*
|
---|
287 | * that map entry matches exactly to the string, including
|
---|
288 | * the cruft at the end beyond "len". That is not a match
|
---|
289 | * with string[0:len] that we are looking for.
|
---|
290 | */
|
---|
291 | } else if (!string[len]) {
|
---|
292 | /*
|
---|
293 | * asked with the whole string, and got nothing. No
|
---|
294 | * matching entry can exist in the map.
|
---|
295 | */
|
---|
296 | return NULL;
|
---|
297 | }
|
---|
298 |
|
---|
299 | /*
|
---|
300 | * i is at the exact match to an overlong key, or location the
|
---|
301 | * overlong key would be inserted, which must come after the
|
---|
302 | * real location of the key if one exists.
|
---|
303 | */
|
---|
304 | while (0 <= --i && i < map->nr) {
|
---|
305 | int cmp = strncasecmp(map->items[i].string, string, len);
|
---|
306 | if (cmp < 0)
|
---|
307 | /*
|
---|
308 | * "i" points at a key definitely below the prefix;
|
---|
309 | * the map does not have string[0:len] in it.
|
---|
310 | */
|
---|
311 | break;
|
---|
312 | else if (!cmp && !map->items[i].string[len])
|
---|
313 | /* found it */
|
---|
314 | return &map->items[i];
|
---|
315 | /*
|
---|
316 | * otherwise, the string at "i" may be string[0:len]
|
---|
317 | * followed by a string that sorts later than string[len:];
|
---|
318 | * keep trying.
|
---|
319 | */
|
---|
320 | }
|
---|
321 | return NULL;
|
---|
322 | }
|
---|
323 |
|
---|
324 | int map_user(struct string_list *map,
|
---|
325 | const char **email, size_t *emaillen,
|
---|
326 | const char **name, size_t *namelen)
|
---|
327 | {
|
---|
328 | struct string_list_item *item;
|
---|
329 | struct mailmap_entry *me;
|
---|
330 |
|
---|
331 | debug_mm("map_user: map '%.*s' <%.*s>\n",
|
---|
332 | (int)*namelen, debug_str(*name),
|
---|
333 | (int)*emaillen, debug_str(*email));
|
---|
334 |
|
---|
335 | item = lookup_prefix(map, *email, *emaillen);
|
---|
336 | if (item != NULL) {
|
---|
337 | me = (struct mailmap_entry *)item->util;
|
---|
338 | if (me->namemap.nr) {
|
---|
339 | /*
|
---|
340 | * The item has multiple items, so we'll look up on
|
---|
341 | * name too. If the name is not found, we choose the
|
---|
342 | * simple entry.
|
---|
343 | */
|
---|
344 | struct string_list_item *subitem;
|
---|
345 | subitem = lookup_prefix(&me->namemap, *name, *namelen);
|
---|
346 | if (subitem)
|
---|
347 | item = subitem;
|
---|
348 | }
|
---|
349 | }
|
---|
350 | if (item != NULL) {
|
---|
351 | struct mailmap_info *mi = (struct mailmap_info *)item->util;
|
---|
352 | if (mi->name == NULL && mi->email == NULL) {
|
---|
353 | debug_mm("map_user: -- (no simple mapping)\n");
|
---|
354 | return 0;
|
---|
355 | }
|
---|
356 | if (mi->email) {
|
---|
357 | *email = mi->email;
|
---|
358 | *emaillen = strlen(*email);
|
---|
359 | }
|
---|
360 | if (mi->name) {
|
---|
361 | *name = mi->name;
|
---|
362 | *namelen = strlen(*name);
|
---|
363 | }
|
---|
364 | debug_mm("map_user: to '%.*s' <%.*s>\n",
|
---|
365 | (int)*namelen, debug_str(*name),
|
---|
366 | (int)*emaillen, debug_str(*email));
|
---|
367 | return 1;
|
---|
368 | }
|
---|
369 | debug_mm("map_user: --\n");
|
---|
370 | return 0;
|
---|
371 | }
|
---|