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