#define DEBUG_MAILMAP 0
#if DEBUG_MAILMAP
#define debug_mm(...) fprintf(stderr, __VA_ARGS__)
+#define debug_str(X) ((X) ? (X) : "(none)")
#else
static inline void debug_mm(const char *format, ...) {}
+static inline const char *debug_str(const char *s) { return s; }
#endif
const char *git_mailmap_file;
+const char *git_mailmap_blob;
struct mailmap_info {
char *name;
static void free_mailmap_info(void *p, const char *s)
{
struct mailmap_info *mi = (struct mailmap_info *)p;
- debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n", s, mi->name, mi->email);
+ debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n",
+ s, debug_str(mi->name), debug_str(mi->email));
free(mi->name);
free(mi->email);
}
static void free_mailmap_entry(void *p, const char *s)
{
struct mailmap_entry *me = (struct mailmap_entry *)p;
- debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n", s, me->namemap.nr);
- debug_mm("mailmap: - simple: '%s' <%s>\n", me->name, me->email);
+ 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));
+
free(me->name);
free(me->email);
string_list_clear_func(&me->namemap, free_mailmap_info);
}
+/*
+ * 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);
+}
+
static void add_mapping(struct string_list *map,
- char *new_name, char *new_email, char *old_name, char *old_email)
+ char *new_name, char *new_email,
+ char *old_name, char *old_email)
{
struct mailmap_entry *me;
int index;
- char *p;
-
- if (old_email)
- for (p = old_email; *p; p++)
- *p = tolower(*p);
- if (new_email)
- for (p = new_email; *p; p++)
- *p = tolower(*p);
if (old_email == NULL) {
old_email = new_email;
if ((index = string_list_find_insert_index(map, old_email, 1)) < 0) {
/* mailmap entry exists, invert index value */
index = -1 - index;
+ me = (struct mailmap_entry *)map->items[index].util;
} else {
/* create mailmap entry */
- struct string_list_item *item = string_list_insert_at_index(map, index, old_email);
- item->util = xcalloc(1, sizeof(struct mailmap_entry));
- ((struct mailmap_entry *)item->util)->namemap.strdup_strings = 1;
+ struct string_list_item *item;
+
+ item = string_list_insert_at_index(map, index, old_email);
+ me = xcalloc(1, sizeof(struct mailmap_entry));
+ me->namemap.strdup_strings = 1;
+ me->namemap.cmp = namemap_cmp;
+ item->util = me;
}
- me = (struct mailmap_entry *)map->items[index].util;
if (old_name == NULL) {
- debug_mm("mailmap: adding (simple) entry for %s at index %d\n", old_email, index);
+ debug_mm("mailmap: adding (simple) entry for %s at index %d\n",
+ old_email, index);
/* Replace current name and new email for simple entry */
if (new_name) {
free(me->name);
}
} else {
struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
- debug_mm("mailmap: adding (complex) entry for %s at index %d\n", old_email, index);
+ debug_mm("mailmap: adding (complex) entry for %s at index %d\n",
+ old_email, index);
if (new_name)
mi->name = xstrdup(new_name);
if (new_email)
}
debug_mm("mailmap: '%s' <%s> -> '%s' <%s>\n",
- old_name, old_email, new_name, new_email);
+ debug_str(old_name), old_email,
+ debug_str(new_name), debug_str(new_email));
}
static char *parse_name_and_email(char *buffer, char **name,
- char **email, int allow_empty_email)
+ char **email, int allow_empty_email)
{
char *left, *right, *nstart, *nend;
*name = *email = NULL;
while (nend > nstart && isspace(*nend))
--nend;
- *name = (nstart < nend ? nstart : NULL);
+ *name = (nstart <= nend ? nstart : NULL);
*email = left+1;
*(nend+1) = '\0';
*right++ = '\0';
return (*right == '\0' ? NULL : right);
}
-static int read_single_mailmap(struct string_list *map, const char *filename, char **repo_abbrev)
+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;
+
+ free(*repo_abbrev);
+ *repo_abbrev = xmalloc(len);
+
+ for (cp = buffer + abblen; isspace(*cp); cp++)
+ ; /* nothing */
+ strcpy(*repo_abbrev, cp);
+ }
+ 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)
{
char buffer[1024];
- FILE *f = (filename == NULL ? NULL : fopen(filename, "r"));
+ FILE *f;
- if (f == NULL)
- return 1;
- while (fgets(buffer, sizeof(buffer), f) != NULL) {
- 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)
- continue;
-
- if (len && buffer[len - 1] == '\n')
- buffer[--len] = 0;
- if (!strncmp(buffer, abbrev, abblen)) {
- char *cp;
-
- if (repo_abbrev)
- free(*repo_abbrev);
- *repo_abbrev = xmalloc(len);
-
- for (cp = buffer + abblen; isspace(*cp); cp++)
- ; /* nothing */
- strcpy(*repo_abbrev, cp);
- }
- continue;
- }
- if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
- parse_name_and_email(name2, &name2, &email2, 1);
+ if (!filename)
+ return 0;
- if (email1)
- add_mapping(map, name1, email1, name2, email2);
+ f = fopen(filename, "r");
+ if (!f) {
+ if (errno == ENOENT)
+ return 0;
+ return error("unable to open mailmap at %s: %s",
+ filename, strerror(errno));
}
+
+ while (fgets(buffer, sizeof(buffer), f) != NULL)
+ read_mailmap_line(map, buffer, repo_abbrev);
fclose(f);
return 0;
}
+static void read_mailmap_string(struct string_list *map, char *buf,
+ char **repo_abbrev)
+{
+ while (*buf) {
+ char *end = strchrnul(buf, '\n');
+
+ if (*end)
+ *end++ = '\0';
+
+ read_mailmap_line(map, buf, repo_abbrev);
+ buf = end;
+ }
+}
+
+static int read_mailmap_blob(struct string_list *map,
+ const char *name,
+ char **repo_abbrev)
+{
+ unsigned char sha1[20];
+ char *buf;
+ unsigned long size;
+ enum object_type type;
+
+ if (!name)
+ return 0;
+ if (get_sha1(name, sha1) < 0)
+ return 0;
+
+ buf = read_sha1_file(sha1, &type, &size);
+ if (!buf)
+ return error("unable to read mailmap object at %s", name);
+ if (type != OBJ_BLOB)
+ return error("mailmap is not a blob: %s", name);
+
+ read_mailmap_string(map, buf, repo_abbrev);
+
+ free(buf);
+ return 0;
+}
+
int read_mailmap(struct string_list *map, char **repo_abbrev)
{
+ int err = 0;
+
map->strdup_strings = 1;
- map->cmp = strcasecmp;
- /* each failure returns 1, so >1 means both calls failed */
- return read_single_mailmap(map, ".mailmap", repo_abbrev) +
- read_single_mailmap(map, git_mailmap_file, repo_abbrev) > 1;
+ map->cmp = namemap_cmp;
+
+ if (!git_mailmap_blob && is_bare_repository())
+ git_mailmap_blob = "HEAD:.mailmap";
+
+ err |= read_mailmap_file(map, ".mailmap", repo_abbrev);
+ err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev);
+ err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev);
+ return err;
}
void clear_mailmap(struct string_list *map)
}
int map_user(struct string_list *map,
- const char **email, size_t *emaillen,
- const char **name, size_t *namelen)
+ const char **email, size_t *emaillen,
+ const char **name, size_t *namelen)
{
struct string_list_item *item;
struct mailmap_entry *me;
debug_mm("map_user: map '%.*s' <%.*s>\n",
- *name, *namelen, *emaillen, *email);
+ (int)*namelen, debug_str(*name),
+ (int)*emaillen, debug_str(*email));
item = lookup_prefix(map, *email, *emaillen);
if (item != NULL) {
me = (struct mailmap_entry *)item->util;
if (me->namemap.nr) {
- /* The item has multiple items, so we'll look up on name too */
- /* If the name is not found, we choose the simple entry */
+ /*
+ * The item has multiple items, so we'll look up on
+ * name too. If the name is not found, we choose the
+ * simple entry.
+ */
struct string_list_item *subitem;
subitem = lookup_prefix(&me->namemap, *name, *namelen);
if (subitem)
*name = mi->name;
*namelen = strlen(*name);
}
- debug_mm("map_user: to '%.*s' <.*%s>\n", *namelen, *name,
- *emaillen, *email);
+ debug_mm("map_user: to '%.*s' <%.*s>\n",
+ (int)*namelen, debug_str(*name),
+ (int)*emaillen, debug_str(*email));
return 1;
}
debug_mm("map_user: --\n");