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