Skip to content

Commit 388c7f8

Browse files
committed
mailmap: remove email copy and length limitation
In map_user(), we have email pointer that points at the beginning of an e-mail address, but the buffer is not terminated with a NUL after the e-mail address. It typically has ">" after the address, and it could have even more if it comes from author/committer line in a commit object. Or it may not have ">" after it. We used to copy the e-mail address proper into a temporary buffer before asking the string-list API to find the e-mail address in the mailmap, because string_list_lookup() function only takes a NUL terminated full string. Introduce a helper function lookup_prefix that takes the email pointer and the length, and finds a matching entry in the string list used for the mailmap, by doing the following: - First ask string_list_find_insert_index() where in its sorted list the e-mail address we have (including the possible trailing junk ">...") would be inserted. - It could find an exact match (e.g. we had a clean e-mail address without any trailing junk). We can return the item in that case. - Or it could return the index of an item that sorts after the e-mail address we have. - If we did not find an exact match against a clean e-mail address, then the record we are looking for in the mailmap has to exist before the index returned by the function (i.e. "email>junk" always sorts later than "email"). Iterate, starting from that index, down the map->items[] array until we find the exact record we are looking for, or we see a record with a key that definitely sorts earlier than the e-mail we are looking for (i.e. when we are looking for "email" in "email>junk", a record in the mailmap that begins with "emaik" strictly sorts before "email", if such a key existed in the mailmap). This, together with the earlier enhancement to support case-insensitive sorting, allow us to remove an extra copy of email buffer to downcase it. A part of this is based on Antoine Pelisse's previous work. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3c020bd commit 388c7f8

File tree

1 file changed

+59
-16
lines changed

1 file changed

+59
-16
lines changed

mailmap.c

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ static int read_single_mailmap(struct string_list *map, const char *filename, ch
174174
int read_mailmap(struct string_list *map, char **repo_abbrev)
175175
{
176176
map->strdup_strings = 1;
177+
map->cmp = strcasecmp;
177178
/* each failure returns 1, so >1 means both calls failed */
178179
return read_single_mailmap(map, ".mailmap", repo_abbrev) +
179180
read_single_mailmap(map, git_mailmap_file, repo_abbrev) > 1;
@@ -187,14 +188,64 @@ void clear_mailmap(struct string_list *map)
187188
debug_mm("mailmap: cleared\n");
188189
}
189190

191+
/*
192+
* Look for an entry in map that match string[0:len]; string[len]
193+
* does not have to be NUL (but it could be).
194+
*/
195+
static struct string_list_item *lookup_prefix(struct string_list *map,
196+
const char *string, size_t len)
197+
{
198+
int i = string_list_find_insert_index(map, string, 1);
199+
if (i < 0) {
200+
/* exact match */
201+
i = -1 - i;
202+
if (!string[len])
203+
return &map->items[i];
204+
/*
205+
* that map entry matches exactly to the string, including
206+
* the cruft at the end beyond "len". That is not a match
207+
* with string[0:len] that we are looking for.
208+
*/
209+
} else if (!string[len]) {
210+
/*
211+
* asked with the whole string, and got nothing. No
212+
* matching entry can exist in the map.
213+
*/
214+
return NULL;
215+
}
216+
217+
/*
218+
* i is at the exact match to an overlong key, or location the
219+
* overlong key would be inserted, which must come after the
220+
* real location of the key if one exists.
221+
*/
222+
while (0 <= --i && i < map->nr) {
223+
int cmp = strncasecmp(map->items[i].string, string, len);
224+
if (cmp < 0)
225+
/*
226+
* "i" points at a key definitely below the prefix;
227+
* the map does not have string[0:len] in it.
228+
*/
229+
break;
230+
else if (!cmp && !map->items[i].string[len])
231+
/* found it */
232+
return &map->items[i];
233+
/*
234+
* otherwise, the string at "i" may be string[0:len]
235+
* followed by a string that sorts later than string[len:];
236+
* keep trying.
237+
*/
238+
}
239+
return NULL;
240+
}
241+
190242
int map_user(struct string_list *map,
191243
char *email, int maxlen_email, char *name, int maxlen_name)
192244
{
193245
char *end_of_email;
194246
struct string_list_item *item;
195247
struct mailmap_entry *me;
196-
char buf[1024], *mailbuf;
197-
int i;
248+
size_t maillen;
198249

199250
/* figure out space requirement for email */
200251
end_of_email = strchr(email, '>');
@@ -204,18 +255,12 @@ int map_user(struct string_list *map,
204255
if (!end_of_email)
205256
return 0;
206257
}
207-
if (end_of_email - email + 1 < sizeof(buf))
208-
mailbuf = buf;
209-
else
210-
mailbuf = xmalloc(end_of_email - email + 1);
211-
212-
/* downcase the email address */
213-
for (i = 0; i < end_of_email - email; i++)
214-
mailbuf[i] = tolower(email[i]);
215-
mailbuf[i] = 0;
216-
217-
debug_mm("map_user: map '%s' <%s>\n", name, mailbuf);
218-
item = string_list_lookup(map, mailbuf);
258+
259+
maillen = end_of_email - email;
260+
261+
debug_mm("map_user: map '%s' <%.*s>\n", name, maillen, email);
262+
263+
item = lookup_prefix(map, email, maillen);
219264
if (item != NULL) {
220265
me = (struct mailmap_entry *)item->util;
221266
if (me->namemap.nr) {
@@ -226,8 +271,6 @@ int map_user(struct string_list *map,
226271
item = subitem;
227272
}
228273
}
229-
if (mailbuf != buf)
230-
free(mailbuf);
231274
if (item != NULL) {
232275
struct mailmap_info *mi = (struct mailmap_info *)item->util;
233276
if (mi->name == NULL && (mi->email == NULL || maxlen_email == 0)) {

0 commit comments

Comments
 (0)