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