reflog: use parse_config_key in config callback
[gitweb.git] / mailmap.c
index 8c3196c7d76ff90e90a9fb4ff569aff6a429861a..b16542febec14ed86fd7ef21ba19899d08c95a64 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -10,6 +10,7 @@ static inline void debug_mm(const char *format, ...) {}
 #endif
 
 const char *git_mailmap_file;
+const char *git_mailmap_blob;
 
 struct mailmap_info {
        char *name;
@@ -118,7 +119,7 @@ static char *parse_name_and_email(char *buffer, char **name,
        while (isspace(*nstart) && nstart < left)
                ++nstart;
        nend = left-1;
-       while (isspace(*nend) && nend > nstart)
+       while (nend > nstart && isspace(*nend))
                --nend;
 
        *name = (nstart < nend ? nstart : NULL);
@@ -129,54 +130,119 @@ static char *parse_name_and_email(char *buffer, char **name,
        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;
+
+                       if (repo_abbrev)
+                               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_buf(struct string_list *map,
+                            const char *buf, unsigned long len,
+                            char **repo_abbrev)
+{
+       while (len) {
+               const char *end = strchrnul(buf, '\n');
+               unsigned long linelen = end - buf + 1;
+               char *line = xmemdupz(buf, linelen);
+
+               read_mailmap_line(map, line, repo_abbrev);
+
+               free(line);
+               buf += linelen;
+               len -= linelen;
+       }
+}
+
+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_buf(map, buf, size, repo_abbrev);
+
+       free(buf);
+       return 0;
+}
+
 int read_mailmap(struct string_list *map, char **repo_abbrev)
 {
+       int err = 0;
+
        map->strdup_strings = 1;
-       /* 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;
+
+       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)
@@ -190,27 +256,27 @@ void clear_mailmap(struct string_list *map)
 int map_user(struct string_list *map,
             char *email, int maxlen_email, char *name, int maxlen_name)
 {
-       char *p;
+       char *end_of_email;
        struct string_list_item *item;
        struct mailmap_entry *me;
        char buf[1024], *mailbuf;
        int i;
 
        /* figure out space requirement for email */
-       p = strchr(email, '>');
-       if (!p) {
+       end_of_email = strchr(email, '>');
+       if (!end_of_email) {
                /* email passed in might not be wrapped in <>, but end with a \0 */
-               p = memchr(email, '\0', maxlen_email);
-               if (!p)
+               end_of_email = memchr(email, '\0', maxlen_email);
+               if (!end_of_email)
                        return 0;
        }
-       if (p - email + 1 < sizeof(buf))
+       if (end_of_email - email + 1 < sizeof(buf))
                mailbuf = buf;
        else
-               mailbuf = xmalloc(p - email + 1);
+               mailbuf = xmalloc(end_of_email - email + 1);
 
        /* downcase the email address */
-       for (i = 0; i < p - email; i++)
+       for (i = 0; i < end_of_email - email; i++)
                mailbuf[i] = tolower(email[i]);
        mailbuf[i] = 0;
 
@@ -236,6 +302,8 @@ int map_user(struct string_list *map,
                }
                if (maxlen_email && mi->email)
                        strlcpy(email, mi->email, maxlen_email);
+               else
+                       *end_of_email = '\0';
                if (maxlen_name && mi->name)
                        strlcpy(name, mi->name, maxlen_name);
                debug_mm("map_user:  to '%s' <%s>\n", name, mi->email ? mi->email : "");