mailmap.c 8.85 KB
Newer Older
1
#include "cache.h"
2
#include "string-list.h"
3
#include "mailmap.h"
4

5 6 7
#define DEBUG_MAILMAP 0
#if DEBUG_MAILMAP
#define debug_mm(...) fprintf(stderr, __VA_ARGS__)
8
#define debug_str(X) ((X) ? (X) : "(none)")
9 10
#else
static inline void debug_mm(const char *format, ...) {}
11
static inline const char *debug_str(const char *s) { return s; }
12 13
#endif

14
const char *git_mailmap_file;
15
const char *git_mailmap_blob;
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

struct mailmap_info {
	char *name;
	char *email;
};

struct mailmap_entry {
	/* name and email for the simple mail-only case */
	char *name;
	char *email;

	/* name and email for the complex mail and name matching case */
	struct string_list namemap;
};

static void free_mailmap_info(void *p, const char *s)
{
	struct mailmap_info *mi = (struct mailmap_info *)p;
Junio C Hamano's avatar
Junio C Hamano committed
34 35
	debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n",
		 s, debug_str(mi->name), debug_str(mi->email));
36 37 38 39 40 41 42
	free(mi->name);
	free(mi->email);
}

static void free_mailmap_entry(void *p, const char *s)
{
	struct mailmap_entry *me = (struct mailmap_entry *)p;
Junio C Hamano's avatar
Junio C Hamano committed
43 44 45 46
	debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n",
		 s, me->namemap.nr);
	debug_mm("mailmap: - simple: '%s' <%s>\n",
		 debug_str(me->name), debug_str(me->email));
47

48 49 50 51 52 53 54
	free(me->name);
	free(me->email);

	me->namemap.strdup_strings = 1;
	string_list_clear_func(&me->namemap, free_mailmap_info);
}

55 56 57 58 59 60 61 62 63 64 65 66 67 68
/*
 * On some systems (e.g. MinGW 4.0), string.h has _only_ inline
 * definition of strcasecmp and no non-inline implementation is
 * supplied anywhere, which is, eh, "unusual"; we cannot take an
 * address of such a function to store it in namemap.cmp.  This is
 * here as a workaround---do not assign strcasecmp directly to
 * namemap.cmp until we know no systems that matter have such an
 * "unusual" string.h.
 */
static int namemap_cmp(const char *a, const char *b)
{
	return strcasecmp(a, b);
}

69
static void add_mapping(struct string_list *map,
Junio C Hamano's avatar
Junio C Hamano committed
70 71
			char *new_name, char *new_email,
			char *old_name, char *old_email)
72 73
{
	struct mailmap_entry *me;
74
	struct string_list_item *item;
75

76 77 78 79 80
	if (old_email == NULL) {
		old_email = new_email;
		new_email = NULL;
	}

81 82 83
	item = string_list_insert(map, old_email);
	if (item->util) {
		me = (struct mailmap_entry *)item->util;
84
	} else {
85 86
		me = xcalloc(1, sizeof(struct mailmap_entry));
		me->namemap.strdup_strings = 1;
87
		me->namemap.cmp = namemap_cmp;
88
		item->util = me;
89 90 91
	}

	if (old_name == NULL) {
92 93
		debug_mm("mailmap: adding (simple) entry for '%s'\n", old_email);

94
		/* Replace current name and new email for simple entry */
95 96
		if (new_name) {
			free(me->name);
97
			me->name = xstrdup(new_name);
98 99 100
		}
		if (new_email) {
			free(me->email);
101
			me->email = xstrdup(new_email);
102
		}
103
	} else {
104
		struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
105
		debug_mm("mailmap: adding (complex) entry for '%s'\n", old_email);
106 107
		mi->name = xstrdup_or_null(new_name);
		mi->email = xstrdup_or_null(new_email);
108
		string_list_insert(&me->namemap, old_name)->util = mi;
109 110 111
	}

	debug_mm("mailmap:  '%s' <%s> -> '%s' <%s>\n",
Junio C Hamano's avatar
Junio C Hamano committed
112 113
		 debug_str(old_name), old_email,
		 debug_str(new_name), debug_str(new_email));
114 115
}

116
static char *parse_name_and_email(char *buffer, char **name,
Junio C Hamano's avatar
Junio C Hamano committed
117
				  char **email, int allow_empty_email)
118 119
{
	char *left, *right, *nstart, *nend;
120
	*name = *email = NULL;
121 122 123 124 125

	if ((left = strchr(buffer, '<')) == NULL)
		return NULL;
	if ((right = strchr(left+1, '>')) == NULL)
		return NULL;
126
	if (!allow_empty_email && (left+1 == right))
127 128 129 130 131 132 133
		return NULL;

	/* remove whitespace from beginning and end of name */
	nstart = buffer;
	while (isspace(*nstart) && nstart < left)
		++nstart;
	nend = left-1;
134
	while (nend > nstart && isspace(*nend))
135 136
		--nend;

137
	*name = (nstart <= nend ? nstart : NULL);
138 139 140 141 142 143 144
	*email = left+1;
	*(nend+1) = '\0';
	*right++ = '\0';

	return (*right == '\0' ? NULL : right);
}

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
static void read_mailmap_line(struct string_list *map, char *buffer,
			      char **repo_abbrev)
{
	char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL;
	if (buffer[0] == '#') {
		static const char abbrev[] = "# repo-abbrev:";
		int abblen = sizeof(abbrev) - 1;
		int len = strlen(buffer);

		if (!repo_abbrev)
			return;

		if (len && buffer[len - 1] == '\n')
			buffer[--len] = 0;
		if (!strncmp(buffer, abbrev, abblen)) {
			char *cp;

162
			free(*repo_abbrev);
163 164 165

			for (cp = buffer + abblen; isspace(*cp); cp++)
				; /* nothing */
166
			*repo_abbrev = xstrdup(cp);
167 168 169 170 171 172 173 174 175 176 177 178
		}
		return;
	}
	if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
		parse_name_and_email(name2, &name2, &email2, 1);

	if (email1)
		add_mapping(map, name1, email1, name2, email2);
}

static int read_mailmap_file(struct string_list *map, const char *filename,
			     char **repo_abbrev)
179 180
{
	char buffer[1024];
181 182 183 184 185 186 187 188 189
	FILE *f;

	if (!filename)
		return 0;

	f = fopen(filename, "r");
	if (!f) {
		if (errno == ENOENT)
			return 0;
Duy Nguyen's avatar
Duy Nguyen committed
190
		return error_errno("unable to open mailmap at %s", filename);
191
	}
192

193 194
	while (fgets(buffer, sizeof(buffer), f) != NULL)
		read_mailmap_line(map, buffer, repo_abbrev);
195 196 197 198
	fclose(f);
	return 0;
}

199 200
static void read_mailmap_string(struct string_list *map, char *buf,
				char **repo_abbrev)
201
{
202 203
	while (*buf) {
		char *end = strchrnul(buf, '\n');
204

205 206
		if (*end)
			*end++ = '\0';
207

208 209
		read_mailmap_line(map, buf, repo_abbrev);
		buf = end;
210 211 212 213 214 215 216
	}
}

static int read_mailmap_blob(struct string_list *map,
			     const char *name,
			     char **repo_abbrev)
{
217
	struct object_id oid;
218 219 220 221 222
	char *buf;
	unsigned long size;
	enum object_type type;

	if (!name)
223
		return 0;
224
	if (get_oid(name, &oid) < 0)
225
		return 0;
226

227
	buf = read_sha1_file(oid.hash, &type, &size);
228
	if (!buf)
229
		return error("unable to read mailmap object at %s", name);
230
	if (type != OBJ_BLOB)
231
		return error("mailmap is not a blob: %s", name);
232

233
	read_mailmap_string(map, buf, repo_abbrev);
234 235 236 237 238

	free(buf);
	return 0;
}

239 240
int read_mailmap(struct string_list *map, char **repo_abbrev)
{
241
	int err = 0;
242

243
	map->strdup_strings = 1;
244
	map->cmp = namemap_cmp;
245 246 247 248

	if (!git_mailmap_blob && is_bare_repository())
		git_mailmap_blob = "HEAD:.mailmap";

249
	err |= read_mailmap_file(map, ".mailmap", repo_abbrev);
250 251
	if (startup_info->have_repository)
		err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev);
252 253
	err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev);
	return err;
254 255
}

256 257 258 259 260 261 262 263
void clear_mailmap(struct string_list *map)
{
	debug_mm("mailmap: clearing %d entries...\n", map->nr);
	map->strdup_strings = 1;
	string_list_clear_func(map, free_mailmap_entry);
	debug_mm("mailmap: cleared\n");
}

264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
/*
 * Look for an entry in map that match string[0:len]; string[len]
 * does not have to be NUL (but it could be).
 */
static struct string_list_item *lookup_prefix(struct string_list *map,
					      const char *string, size_t len)
{
	int i = string_list_find_insert_index(map, string, 1);
	if (i < 0) {
		/* exact match */
		i = -1 - i;
		if (!string[len])
			return &map->items[i];
		/*
		 * that map entry matches exactly to the string, including
		 * the cruft at the end beyond "len".  That is not a match
		 * with string[0:len] that we are looking for.
		 */
	} else if (!string[len]) {
		/*
		 * asked with the whole string, and got nothing.  No
		 * matching entry can exist in the map.
		 */
		return NULL;
	}

	/*
	 * i is at the exact match to an overlong key, or location the
	 * overlong key would be inserted, which must come after the
	 * real location of the key if one exists.
	 */
	while (0 <= --i && i < map->nr) {
		int cmp = strncasecmp(map->items[i].string, string, len);
		if (cmp < 0)
			/*
			 * "i" points at a key definitely below the prefix;
			 * the map does not have string[0:len] in it.
			 */
			break;
		else if (!cmp && !map->items[i].string[len])
			/* found it */
			return &map->items[i];
		/*
		 * otherwise, the string at "i" may be string[0:len]
		 * followed by a string that sorts later than string[len:];
		 * keep trying.
		 */
	}
	return NULL;
}

315
int map_user(struct string_list *map,
Junio C Hamano's avatar
Junio C Hamano committed
316 317
	     const char **email, size_t *emaillen,
	     const char **name, size_t *namelen)
318
{
319
	struct string_list_item *item;
320
	struct mailmap_entry *me;
321

322
	debug_mm("map_user: map '%.*s' <%.*s>\n",
Junio C Hamano's avatar
Junio C Hamano committed
323 324
		 (int)*namelen, debug_str(*name),
		 (int)*emaillen, debug_str(*email));
325

326
	item = lookup_prefix(map, *email, *emaillen);
327 328 329
	if (item != NULL) {
		me = (struct mailmap_entry *)item->util;
		if (me->namemap.nr) {
Junio C Hamano's avatar
Junio C Hamano committed
330 331 332 333 334
			/*
			 * The item has multiple items, so we'll look up on
			 * name too. If the name is not found, we choose the
			 * simple entry.
			 */
335 336
			struct string_list_item *subitem;
			subitem = lookup_prefix(&me->namemap, *name, *namelen);
337 338 339 340
			if (subitem)
				item = subitem;
		}
	}
341
	if (item != NULL) {
342
		struct mailmap_info *mi = (struct mailmap_info *)item->util;
343
		if (mi->name == NULL && mi->email == NULL) {
344 345 346
			debug_mm("map_user:  -- (no simple mapping)\n");
			return 0;
		}
347 348 349 350 351 352 353 354
		if (mi->email) {
				*email = mi->email;
				*emaillen = strlen(*email);
		}
		if (mi->name) {
				*name = mi->name;
				*namelen = strlen(*name);
		}
Junio C Hamano's avatar
Junio C Hamano committed
355 356 357
		debug_mm("map_user:  to '%.*s' <%.*s>\n",
			 (int)*namelen, debug_str(*name),
			 (int)*emaillen, debug_str(*email));
358 359
		return 1;
	}
360
	debug_mm("map_user:  --\n");
361 362
	return 0;
}