Merge branch 'jk/ident-gecos-strbuf'
authorJunio C Hamano <gitster@pobox.com>
Tue, 29 May 2012 20:09:13 +0000 (13:09 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 29 May 2012 20:09:13 +0000 (13:09 -0700)
Fixes quite a lot of brokenness when ident information needs to be taken
from the system and cleans up the code.

By Jeff King
* jk/ident-gecos-strbuf: (22 commits)
format-patch: do not use bogus email addresses in message ids
ident: reject bogus email addresses with IDENT_STRICT
ident: rename IDENT_ERROR_ON_NO_NAME to IDENT_STRICT
format-patch: use GIT_COMMITTER_EMAIL in message ids
ident: let callers omit name with fmt_indent
ident: refactor NO_DATE flag in fmt_ident
ident: reword empty ident error message
format-patch: refactor get_patch_filename
ident: trim whitespace from default name/email
ident: use a dynamic strbuf in fmt_ident
ident: use full dns names to generate email addresses
ident: report passwd errors with a more friendly message
drop length limitations on gecos-derived names and emails
ident: don't write fallback username into git_default_name
fmt_ident: drop IDENT_WARN_ON_NO_NAME code
format-patch: use default email for generating message ids
ident: trim trailing newline from /etc/mailname
move git_default_* variables to ident.c
move identity config parsing to ident.c
fmt-merge-msg: don't use static buffer in record_person
...

1  2 
builtin/fmt-merge-msg.c
cache.h
ident.c
diff --combined builtin/fmt-merge-msg.c
index d42015d8672d7a929758f69371eacbbc59c92888,4ee6a88d754c3a64dd562ae835779c7ee5c660e5..bf93b043b79a0a3001d427f78009a1cdd26db8bb
@@@ -230,7 -230,7 +230,7 @@@ static void add_branch_desc(struct strb
  static void record_person(int which, struct string_list *people,
                          struct commit *commit)
  {
-       char name_buf[MAX_GITNAME], *name, *name_end;
+       char *name_buf, *name, *name_end;
        struct string_list_item *elem;
        const char *field = (which == 'a') ? "\nauthor " : "\ncommitter ";
  
                name_end--;
        while (isspace(*name_end) && name <= name_end)
                name_end--;
-       if (name_end < name || name + MAX_GITNAME <= name_end)
+       if (name_end < name)
                return;
-       memcpy(name_buf, name, name_end - name + 1);
-       name_buf[name_end - name + 1] = '\0';
+       name_buf = xmemdupz(name, name_end - name + 1);
  
        elem = string_list_lookup(people, name_buf);
        if (!elem) {
                elem->util = (void *)0;
        }
        elem->util = (void*)(util_as_integral(elem) + 1);
+       free(name_buf);
  }
  
  static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
@@@ -462,10 -462,7 +462,10 @@@ static void fmt_tag_signature(struct st
                strbuf_add(tagbuf, tag_body, buf + len - tag_body);
        }
        strbuf_complete_line(tagbuf);
 -      strbuf_add_lines(tagbuf, "# ", sig->buf, sig->len);
 +      if (sig->len) {
 +              strbuf_addch(tagbuf, '\n');
 +              strbuf_add_lines(tagbuf, "# ", sig->buf, sig->len);
 +      }
  }
  
  static void fmt_merge_msg_sigs(struct strbuf *out)
@@@ -630,7 -627,8 +630,7 @@@ int fmt_merge_msg(struct strbuf *in, st
                rev.ignore_merges = 1;
                rev.limited = 1;
  
 -              if (suffixcmp(out->buf, "\n"))
 -                      strbuf_addch(out, '\n');
 +              strbuf_complete_line(out);
  
                for (i = 0; i < origins.nr; i++)
                        shortlog(origins.items[i].string,
diff --combined cache.h
index cc5048c202b6799591e7121de91ee2c6cdce9997,f89f22db703f715372000b5f1036fb98c2bb985a..06413e1584dc762063feaa3728209a4d2864ebc7
+++ b/cache.h
@@@ -887,15 -887,19 +887,19 @@@ unsigned long approxidate_careful(cons
  unsigned long approxidate_relative(const char *date, const struct timeval *now);
  enum date_mode parse_date_format(const char *format);
  
- #define IDENT_WARN_ON_NO_NAME  1
- #define IDENT_ERROR_ON_NO_NAME 2
- #define IDENT_NO_DATE        4
+ #define IDENT_STRICT         1
+ #define IDENT_NO_DATE        2
+ #define IDENT_NO_NAME        4
  extern const char *git_author_info(int);
  extern const char *git_committer_info(int);
  extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
  extern const char *fmt_name(const char *name, const char *email);
+ extern const char *ident_default_name(void);
+ extern const char *ident_default_email(void);
+ extern const char *ident_default_date(void);
  extern const char *git_editor(void);
  extern const char *git_pager(int stdout_is_tty);
+ extern int git_ident_config(const char *, const char *, void *);
  
  struct ident_split {
        const char *name_begin;
@@@ -947,7 -951,6 +951,7 @@@ extern struct alternate_object_databas
        char base[FLEX_ARRAY]; /* more */
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
 +extern void read_info_alternates(const char * relative_base, int depth);
  extern void add_to_alternates_file(const char *reference);
  typedef int alt_odb_fn(struct alternate_object_database *, void *);
  extern void foreach_alt_odb(alt_odb_fn, void*);
@@@ -1139,9 -1142,6 +1143,6 @@@ struct config_include_data 
  #define CONFIG_INCLUDE_INIT { 0 }
  extern int git_config_include(const char *name, const char *value, void *data);
  
- #define MAX_GITNAME (1000)
- extern char git_default_email[MAX_GITNAME];
- extern char git_default_name[MAX_GITNAME];
  #define IDENT_NAME_GIVEN 01
  #define IDENT_MAIL_GIVEN 02
  #define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
diff --combined ident.c
index 5df094d175ab91afb9ead68431f0c99bde7fd03f,98852c7cc901550ccdd6363cf805845304e8ff28..443c0751bd1f63c059649f9f7f9174349cc1d270
+++ b/ident.c
@@@ -7,7 -7,10 +7,10 @@@
   */
  #include "cache.h"
  
+ static struct strbuf git_default_name = STRBUF_INIT;
+ static struct strbuf git_default_email = STRBUF_INIT;
  static char git_default_date[50];
+ int user_ident_explicitly_given;
  
  #ifdef NO_GECOS_IN_PWENT
  #define get_gecos(ignored) "&"
  #define get_gecos(struct_passwd) ((struct_passwd)->pw_gecos)
  #endif
  
- static void copy_gecos(const struct passwd *w, char *name, size_t sz)
+ static void copy_gecos(const struct passwd *w, struct strbuf *name)
  {
-       char *src, *dst;
-       size_t len, nlen;
-       nlen = strlen(w->pw_name);
+       char *src;
  
        /* Traditionally GECOS field had office phone numbers etc, separated
         * with commas.  Also & stands for capitalized form of the login name.
         */
  
-       for (len = 0, dst = name, src = get_gecos(w); len < sz; src++) {
+       for (src = get_gecos(w); *src && *src != ','; src++) {
                int ch = *src;
-               if (ch != '&') {
-                       *dst++ = ch;
-                       if (ch == 0 || ch == ',')
-                               break;
-                       len++;
-                       continue;
-               }
-               if (len + nlen < sz) {
+               if (ch != '&')
+                       strbuf_addch(name, ch);
+               else {
                        /* Sorry, Mr. McDonald... */
-                       *dst++ = toupper(*w->pw_name);
-                       memcpy(dst, w->pw_name + 1, nlen - 1);
-                       dst += nlen - 1;
-                       len += nlen;
+                       strbuf_addch(name, toupper(*w->pw_name));
+                       strbuf_addstr(name, w->pw_name + 1);
                }
        }
-       if (len < sz)
-               name[len] = 0;
-       else
-               die("Your parents must have hated you!");
  }
  
- static int add_mailname_host(char *buf, size_t len)
+ static int add_mailname_host(struct strbuf *buf)
  {
        FILE *mailname;
  
@@@ -61,7 -49,7 +49,7 @@@
                                strerror(errno));
                return -1;
        }
-       if (!fgets(buf, len, mailname)) {
+       if (strbuf_getline(buf, mailname, '\n') == EOF) {
                if (ferror(mailname))
                        warning("cannot read /etc/mailname: %s",
                                strerror(errno));
        return 0;
  }
  
- static void add_domainname(char *buf, size_t len)
+ static void add_domainname(struct strbuf *out)
  {
+       char buf[1024];
        struct hostent *he;
-       size_t namelen;
-       const char *domainname;
  
-       if (gethostname(buf, len)) {
+       if (gethostname(buf, sizeof(buf))) {
                warning("cannot get host name: %s", strerror(errno));
-               strlcpy(buf, "(none)", len);
+               strbuf_addstr(out, "(none)");
                return;
        }
-       namelen = strlen(buf);
-       if (memchr(buf, '.', namelen))
-               return;
-       he = gethostbyname(buf);
-       buf[namelen++] = '.';
-       buf += namelen;
-       len -= namelen;
-       if (he && (domainname = strchr(he->h_name, '.')))
-               strlcpy(buf, domainname + 1, len);
+       if (strchr(buf, '.'))
+               strbuf_addstr(out, buf);
+       else if ((he = gethostbyname(buf)) && strchr(he->h_name, '.'))
+               strbuf_addstr(out, he->h_name);
        else
-               strlcpy(buf, "(none)", len);
+               strbuf_addf(out, "%s.(none)", buf);
  }
  
- static void copy_email(const struct passwd *pw)
+ static void copy_email(const struct passwd *pw, struct strbuf *email)
  {
        /*
         * Make up a fake email address
         * (name + '@' + hostname [+ '.' + domainname])
         */
-       size_t len = strlen(pw->pw_name);
-       if (len > sizeof(git_default_email)/2)
-               die("Your sysadmin must hate you!");
-       memcpy(git_default_email, pw->pw_name, len);
-       git_default_email[len++] = '@';
-       if (!add_mailname_host(git_default_email + len,
-                               sizeof(git_default_email) - len))
+       strbuf_addstr(email, pw->pw_name);
+       strbuf_addch(email, '@');
+       if (!add_mailname_host(email))
                return; /* read from "/etc/mailname" (Debian) */
-       add_domainname(git_default_email + len,
-                       sizeof(git_default_email) - len);
+       add_domainname(email);
  }
  
static void setup_ident(const char **name, const char **emailp)
const char *ident_default_name(void)
  {
-       struct passwd *pw = NULL;
-       /* Get the name ("gecos") */
-       if (!*name && !git_default_name[0]) {
-               pw = getpwuid(getuid());
-               if (!pw)
-                       die("You don't exist. Go away!");
-               copy_gecos(pw, git_default_name, sizeof(git_default_name));
+       if (!git_default_name.len) {
+               copy_gecos(xgetpwuid_self(), &git_default_name);
+               strbuf_trim(&git_default_name);
        }
-       if (!*name)
-               *name = git_default_name;
+       return git_default_name.buf;
+ }
  
-       if (!*emailp && !git_default_email[0]) {
+ const char *ident_default_email(void)
+ {
+       if (!git_default_email.len) {
                const char *email = getenv("EMAIL");
  
                if (email && email[0]) {
-                       strlcpy(git_default_email, email,
-                               sizeof(git_default_email));
+                       strbuf_addstr(&git_default_email, email);
                        user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
-               } else {
-                       if (!pw)
-                               pw = getpwuid(getuid());
-                       if (!pw)
-                               die("You don't exist. Go away!");
-                       copy_email(pw);
-               }
+               } else
+                       copy_email(xgetpwuid_self(), &git_default_email);
+               strbuf_trim(&git_default_email);
        }
-       if (!*emailp)
-               *emailp = git_default_email;
-       /* And set the default date */
-       if (!git_default_date[0])
-               datestamp(git_default_date, sizeof(git_default_date));
+       return git_default_email.buf;
  }
  
static int add_raw(char *buf, size_t size, int offset, const char *str)
const char *ident_default_date(void)
  {
-       size_t len = strlen(str);
-       if (offset + len > size)
-               return size;
-       memcpy(buf + offset, str, len);
-       return offset + len;
+       if (!git_default_date[0])
+               datestamp(git_default_date, sizeof(git_default_date));
+       return git_default_date;
  }
  
  static int crud(unsigned char c)
   * Copy over a string to the destination, but avoid special
   * characters ('\n', '<' and '>') and remove crud at the end
   */
- static int copy(char *buf, size_t size, int offset, const char *src)
+ static void strbuf_addstr_without_crud(struct strbuf *sb, const char *src)
  {
        size_t i, len;
        unsigned char c;
        /*
         * Copy the rest to the buffer, but avoid the special
         * characters '\n' '<' and '>' that act as delimiters on
-        * an identification line
+        * an identification line. We can only remove crud, never add it,
+        * so 'len' is our maximum.
         */
+       strbuf_grow(sb, len);
        for (i = 0; i < len; i++) {
                c = *src++;
                switch (c) {
                case '\n': case '<': case '>':
                        continue;
                }
-               if (offset >= size)
-                       return size;
-               buf[offset++] = c;
+               sb->buf[sb->len++] = c;
        }
-       return offset;
+       sb->buf[sb->len] = '\0';
  }
  
  /*
@@@ -244,7 -205,7 +205,7 @@@ int split_ident_line(struct ident_spli
        if (!split->mail_begin)
                return status;
  
 -      for (cp = split->mail_begin - 2; line < cp; cp--)
 +      for (cp = split->mail_begin - 2; line <= cp; cp--)
                if (!isspace(*cp)) {
                        split->name_end = cp + 1;
                        break;
@@@ -304,57 -265,62 +265,62 @@@ static const char *env_hint 
  const char *fmt_ident(const char *name, const char *email,
                      const char *date_str, int flag)
  {
-       static char buffer[1000];
+       static struct strbuf ident = STRBUF_INIT;
        char date[50];
-       int i;
-       int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME);
-       int warn_on_no_name = (flag & IDENT_WARN_ON_NO_NAME);
-       int name_addr_only = (flag & IDENT_NO_DATE);
+       int strict = (flag & IDENT_STRICT);
+       int want_date = !(flag & IDENT_NO_DATE);
+       int want_name = !(flag & IDENT_NO_NAME);
  
-       setup_ident(&name, &email);
+       if (want_name && !name)
+               name = ident_default_name();
+       if (!email)
+               email = ident_default_email();
  
-       if (!*name) {
+       if (want_name && !*name) {
                struct passwd *pw;
  
-               if ((warn_on_no_name || error_on_no_name) &&
-                   name == git_default_name && env_hint) {
-                       fputs(env_hint, stderr);
-                       env_hint = NULL; /* warn only once */
+               if (strict) {
+                       if (name == git_default_name.buf)
+                               fputs(env_hint, stderr);
+                       die("empty ident name (for <%s>) not allowed", email);
                }
-               if (error_on_no_name)
-                       die("empty ident %s <%s> not allowed", name, email);
-               pw = getpwuid(getuid());
-               if (!pw)
-                       die("You don't exist. Go away!");
-               strlcpy(git_default_name, pw->pw_name,
-                       sizeof(git_default_name));
-               name = git_default_name;
+               pw = xgetpwuid_self();
+               name = pw->pw_name;
+       }
+       if (strict && email == git_default_email.buf &&
+           strstr(email, "(none)")) {
+               fputs(env_hint, stderr);
+               die("unable to auto-detect email address (got '%s')", email);
        }
  
-       strcpy(date, git_default_date);
-       if (!name_addr_only && date_str && date_str[0]) {
-               if (parse_date(date_str, date, sizeof(date)) < 0)
-                       die("invalid date format: %s", date_str);
+       if (want_date) {
+               if (date_str && date_str[0]) {
+                       if (parse_date(date_str, date, sizeof(date)) < 0)
+                               die("invalid date format: %s", date_str);
+               }
+               else
+                       strcpy(date, ident_default_date());
        }
  
-       i = copy(buffer, sizeof(buffer), 0, name);
-       i = add_raw(buffer, sizeof(buffer), i, " <");
-       i = copy(buffer, sizeof(buffer), i, email);
-       if (!name_addr_only) {
-               i = add_raw(buffer, sizeof(buffer), i,  "> ");
-               i = copy(buffer, sizeof(buffer), i, date);
-       } else {
-               i = add_raw(buffer, sizeof(buffer), i, ">");
+       strbuf_reset(&ident);
+       if (want_name) {
+               strbuf_addstr_without_crud(&ident, name);
+               strbuf_addstr(&ident, " <");
        }
-       if (i >= sizeof(buffer))
-               die("Impossibly long personal identifier");
-       buffer[i] = 0;
-       return buffer;
+       strbuf_addstr_without_crud(&ident, email);
+       if (want_name)
+                       strbuf_addch(&ident, '>');
+       if (want_date) {
+               strbuf_addch(&ident, ' ');
+               strbuf_addstr_without_crud(&ident, date);
+       }
+       return ident.buf;
  }
  
  const char *fmt_name(const char *name, const char *email)
  {
-       return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
+       return fmt_ident(name, email, NULL, IDENT_STRICT | IDENT_NO_DATE);
  }
  
  const char *git_author_info(int flag)
@@@ -385,3 -351,26 +351,26 @@@ int user_ident_sufficiently_given(void
        return (user_ident_explicitly_given == IDENT_ALL_GIVEN);
  #endif
  }
+ int git_ident_config(const char *var, const char *value, void *data)
+ {
+       if (!strcmp(var, "user.name")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               strbuf_reset(&git_default_name);
+               strbuf_addstr(&git_default_name, value);
+               user_ident_explicitly_given |= IDENT_NAME_GIVEN;
+               return 0;
+       }
+       if (!strcmp(var, "user.email")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               strbuf_reset(&git_default_email);
+               strbuf_addstr(&git_default_email, value);
+               user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
+               return 0;
+       }
+       return 0;
+ }