From bcb2b0044b3baedf7857613ec069f300c364680c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:09:43 -0400 Subject: ident: split setup_ident into separate functions This function sets up the default name, email, and date, and is not publicly available. Let's split it into three public functions so that callers can get just the parts they need. While we're at it, let's change the interface to simple accessors. The original function was called only by fmt_ident, and contained logic for "if we already have some other value, don't load the default" which properly belongs in fmt_ident. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- cache.h | 3 +++ ident.c | 35 +++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/cache.h b/cache.h index e14ffcd914..0c095d4842 100644 --- a/cache.h +++ b/cache.h @@ -894,6 +894,9 @@ 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); diff --git a/ident.c b/ident.c index 87c697c2b0..0f7dcae8f8 100644 --- a/ident.c +++ b/ident.c @@ -117,21 +117,20 @@ static void copy_email(const struct passwd *pw) sizeof(git_default_email) - len); } -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 (!git_default_name[0]) { + struct passwd *pw = getpwuid(getuid()); if (!pw) die("You don't exist. Go away!"); copy_gecos(pw, git_default_name, sizeof(git_default_name)); } - if (!*name) - *name = git_default_name; + return git_default_name; +} - if (!*emailp && !git_default_email[0]) { +const char *ident_default_email(void) +{ + if (!git_default_email[0]) { const char *email = getenv("EMAIL"); if (email && email[0]) { @@ -139,19 +138,20 @@ static void setup_ident(const char **name, const char **emailp) sizeof(git_default_email)); user_ident_explicitly_given |= IDENT_MAIL_GIVEN; } else { - if (!pw) - pw = getpwuid(getuid()); + struct passwd *pw = getpwuid(getuid()); if (!pw) die("You don't exist. Go away!"); copy_email(pw); } } - if (!*emailp) - *emailp = git_default_email; + return git_default_email; +} - /* And set the default date */ +const char *ident_default_date(void) +{ if (!git_default_date[0]) datestamp(git_default_date, sizeof(git_default_date)); + return git_default_date; } static int add_raw(char *buf, size_t size, int offset, const char *str) @@ -311,7 +311,10 @@ const char *fmt_ident(const char *name, const char *email, int warn_on_no_name = (flag & IDENT_WARN_ON_NO_NAME); int name_addr_only = (flag & IDENT_NO_DATE); - setup_ident(&name, &email); + if (!name) + name = ident_default_name(); + if (!email) + email = ident_default_email(); if (!*name) { struct passwd *pw; @@ -331,7 +334,7 @@ const char *fmt_ident(const char *name, const char *email, name = git_default_name; } - strcpy(date, git_default_date); + strcpy(date, ident_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); -- cgit v1.2.1 From 5cb2194aba3c6828cec3e4ec75d78f49165e2eab Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:09:47 -0400 Subject: http-push: do not access git_default_email directly By calling the ident_default_email accessor, we can be sure that the default value is actually filled-in. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- http-push.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-push.c b/http-push.c index 1df7ab5670..a832ca77a3 100644 --- a/http-push.c +++ b/http-push.c @@ -904,7 +904,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout) ep = strchr(ep + 1, '/'); } - escaped = xml_entities(git_default_email); + escaped = xml_entities(ident_default_email()); strbuf_addf(&out_buffer.buf, LOCK_REQUEST, escaped); free(escaped); -- cgit v1.2.1 From e21ab1340a1a22f017e9c3e4fc0deb9f4de36917 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:09:51 -0400 Subject: fmt-merge-msg: don't use static buffer in record_person The record_person function just parses out the "name" field of the person line in a commit and adds it to a string_list. The only reason we need an extra buffer is that the string_list functions require a NUL-terminated string. Instead of the static buffer, we can just allocate a temporary NUL-terminated copy. In addition to removing a useless limit, this removes the only user of MAX_GITNAME outside of ident.c. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/fmt-merge-msg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index a517f1794a..4ee6a88d75 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -230,7 +230,7 @@ static void add_branch_desc(struct strbuf *out, const char *name) 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 "; @@ -243,10 +243,9 @@ static void record_person(int which, struct string_list *people, 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) { @@ -254,6 +253,7 @@ static void record_person(int which, struct string_list *people, 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_) -- cgit v1.2.1 From 9597921b6c173d90359e2cfa4c2e36de8d1554e6 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:09:54 -0400 Subject: move identity config parsing to ident.c There's no reason for this to be in config, except that once upon a time all of the config parsing was there. It makes more sense to keep the ident code together. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- cache.h | 1 + config.c | 24 +----------------------- ident.c | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/cache.h b/cache.h index 0c095d4842..86224c8198 100644 --- a/cache.h +++ b/cache.h @@ -899,6 +899,7 @@ 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; diff --git a/config.c b/config.c index eeee986022..71ef171cab 100644 --- a/config.c +++ b/config.c @@ -762,28 +762,6 @@ static int git_default_core_config(const char *var, const char *value) return 0; } -static int git_default_user_config(const char *var, const char *value) -{ - if (!strcmp(var, "user.name")) { - if (!value) - return config_error_nonbool(var); - strlcpy(git_default_name, value, sizeof(git_default_name)); - user_ident_explicitly_given |= IDENT_NAME_GIVEN; - return 0; - } - - if (!strcmp(var, "user.email")) { - if (!value) - return config_error_nonbool(var); - strlcpy(git_default_email, value, sizeof(git_default_email)); - user_ident_explicitly_given |= IDENT_MAIL_GIVEN; - return 0; - } - - /* Add other config variables here and to Documentation/config.txt. */ - return 0; -} - static int git_default_i18n_config(const char *var, const char *value) { if (!strcmp(var, "i18n.commitencoding")) @@ -870,7 +848,7 @@ int git_default_config(const char *var, const char *value, void *dummy) return git_default_core_config(var, value); if (!prefixcmp(var, "user.")) - return git_default_user_config(var, value); + return git_ident_config(var, value, dummy); if (!prefixcmp(var, "i18n.")) return git_default_i18n_config(var, value); diff --git a/ident.c b/ident.c index 0f7dcae8f8..bb1158f7d2 100644 --- a/ident.c +++ b/ident.c @@ -388,3 +388,24 @@ 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); + strlcpy(git_default_name, value, sizeof(git_default_name)); + user_ident_explicitly_given |= IDENT_NAME_GIVEN; + return 0; + } + + if (!strcmp(var, "user.email")) { + if (!value) + return config_error_nonbool(var); + strlcpy(git_default_email, value, sizeof(git_default_email)); + user_ident_explicitly_given |= IDENT_MAIL_GIVEN; + return 0; + } + + return 0; +} -- cgit v1.2.1 From 2d4b4fcebdd4fb8c8cd2664b390e3bbb82370155 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:09:57 -0400 Subject: move git_default_* variables to ident.c There's no reason anybody outside of ident.c should access these directly (they should use the new accessors which make sure the variables are initialized), so we can make them file-scope statics. While we're at it, move user_ident_explicitly_given into ident.c; while still globally visible, it makes more sense to reside with the ident code. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- cache.h | 3 --- environment.c | 3 --- ident.c | 4 ++++ 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cache.h b/cache.h index 86224c8198..f63b71ff66 100644 --- a/cache.h +++ b/cache.h @@ -1142,9 +1142,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 --git a/environment.c b/environment.c index d7e6c65763..669e498f5a 100644 --- a/environment.c +++ b/environment.c @@ -11,9 +11,6 @@ #include "refs.h" #include "fmt-merge-msg.h" -char git_default_email[MAX_GITNAME]; -char git_default_name[MAX_GITNAME]; -int user_ident_explicitly_given; int trust_executable_bit = 1; int trust_ctime = 1; int has_symlinks = 1; diff --git a/ident.c b/ident.c index bb1158f7d2..af92b2cd86 100644 --- a/ident.c +++ b/ident.c @@ -7,7 +7,11 @@ */ #include "cache.h" +#define MAX_GITNAME (1000) +static char git_default_name[MAX_GITNAME]; +static char git_default_email[MAX_GITNAME]; static char git_default_date[50]; +int user_ident_explicitly_given; #ifdef NO_GECOS_IN_PWENT #define get_gecos(ignored) "&" -- cgit v1.2.1 From 132f4b6ccb470cb209167b7806c68805ba4dc600 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:10:02 -0400 Subject: ident: trim trailing newline from /etc/mailname We use fgets to read the /etc/mailname file, which means we will typically end up with an extra newline in our git_default_email. Most of the time this doesn't matter, as fmt_ident will skip it as cruft, but there is one code path that accesses it directly (in http-push.c:lock_remote). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- ident.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ident.c b/ident.c index af92b2cd86..acb3a0843f 100644 --- a/ident.c +++ b/ident.c @@ -74,6 +74,10 @@ static int add_mailname_host(char *buf, size_t len) } /* success! */ fclose(mailname); + + len = strlen(buf); + if (len && buf[len-1] == '\n') + buf[len-1] = '\0'; return 0; } -- cgit v1.2.1 From 43ae9f47ab8a7762d914e91d6f57b79126986640 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:10:08 -0400 Subject: format-patch: use default email for generating message ids We try to generate a sane message id for cover letters and threading by appending some changing bits to the front of the user's email address. The current code parses the email out of the results of git_committer_info, but we can do this much more easily by just calling ident_default_email ourselves. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/log.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index 690caa7830..656bddfb03 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -737,15 +737,9 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha static void gen_message_id(struct rev_info *info, char *base) { - const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME); - const char *email_start = strrchr(committer, '<'); - const char *email_end = strrchr(committer, '>'); struct strbuf buf = STRBUF_INIT; - if (!email_start || !email_end || email_start > email_end - 1) - die(_("Could not extract email from committer identity.")); - strbuf_addf(&buf, "%s.%lu.git.%.*s", base, - (unsigned long) time(NULL), - (int)(email_end - email_start - 1), email_start + 1); + strbuf_addf(&buf, "%s.%lu.git.%s", base, + (unsigned long) time(NULL), ident_default_email()); info->message_id = strbuf_detach(&buf, NULL); } -- cgit v1.2.1 From b9f0ac1710ed1d2dc865ab40893720e9a242e362 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:10:11 -0400 Subject: fmt_ident: drop IDENT_WARN_ON_NO_NAME code There are no more callers who want this, so we can drop it. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- cache.h | 5 ++--- ident.c | 11 ++++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/cache.h b/cache.h index f63b71ff66..65cbab5564 100644 --- a/cache.h +++ b/cache.h @@ -887,9 +887,8 @@ unsigned long approxidate_careful(const char *, int *); 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_ERROR_ON_NO_NAME 1 +#define IDENT_NO_DATE 2 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); diff --git a/ident.c b/ident.c index acb3a0843f..3b92c44654 100644 --- a/ident.c +++ b/ident.c @@ -316,7 +316,6 @@ const char *fmt_ident(const char *name, const char *email, 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); if (!name) @@ -327,13 +326,11 @@ const char *fmt_ident(const char *name, const char *email, if (!*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 (error_on_no_name) + if (error_on_no_name) { + if (name == git_default_name) + fputs(env_hint, stderr); die("empty ident %s <%s> not allowed", name, email); + } pw = getpwuid(getuid()); if (!pw) die("You don't exist. Go away!"); -- cgit v1.2.1 From 060d4bb3d6c0f4ca3b85f089708b07447224bc91 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:10:14 -0400 Subject: ident: don't write fallback username into git_default_name The fmt_ident function gets a flag that tells us whether to die if the name field is blank. If it is blank and we don't die, then we fall back to the username from the passwd file. The current code writes the value into git_default_name. However, that's not necessarily correct, as the empty value might have come from git_default_name, or it might have been passed in. This leads to two potential problems: 1. If we are overriding an empty name in the passed-in value, then we may be overwriting a perfectly good name (from gitconfig or gecos) in the git_default_name buffer. Later calls to fmt_ident will end up using the fallback name, even though a better name was available. 2. If we override an empty gecos name, we end up with the fallback name in git_default_name. A later call that uses IDENT_ERROR_ON_NO_NAME will see the fallback name and think that it is a good name, instead of producing an error. In other words, a blank gecos name would cause an error with this code: git_committer_info(IDENT_ERROR_ON_NO_NAME); but not this: git_committer_info(0); git_committer_info(IDENT_ERROR_ON_NO_NAME); because in the latter case, the first call has polluted the name buffer. Instead, let's make the fallback a per-invocation variable. We can just use the pw->pw_name string directly, since it only needs to persist through the rest of the function (and we don't do any other getpwent calls). Note that while this solves (1) for future invocations of fmt_indent, the current invocation might use the fallback when it could in theory load a better value from git_default_name. However, by not passing IDENT_ERROR_ON_NO_NAME, the caller is indicating that it does not care too much about the name, anyway, so we don't bother; this is primarily about protecting future callers who do care. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- ident.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ident.c b/ident.c index 3b92c44654..87e3cbe589 100644 --- a/ident.c +++ b/ident.c @@ -334,9 +334,7 @@ const char *fmt_ident(const char *name, const char *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; + name = pw->pw_name; } strcpy(date, ident_default_date()); -- cgit v1.2.1 From 8587ead78ad3d2af760270b21035ffe48fde3e7b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:10:17 -0400 Subject: drop length limitations on gecos-derived names and emails When we pull the user's name from the GECOS field of the passwd file (or generate an email address based on their username and hostname), we put the result into a static buffer. While it's extremely unlikely that anybody ever hit these limits (after all, in such a case their parents must have hated them), we still had to deal with the error cases in our code. Converting these static buffers to strbufs lets us simplify the code and drop some error messages from the documentation that have confused some users. The conversion is mostly mechanical: replace string copies with strbuf equivalents, and access the strbuf.buf directly. There are a few exceptions: - copy_gecos and copy_email are the big winners in code reduction (since they no longer have to manage the string length manually) - git_ident_config wants to replace old versions of the default name (e.g., if we read the config multiple times), so it must reset+add to the strbuf instead of just adding Note that there is still one length limitation: the gethostname interface requires us to provide a static buffer, so we arbitrarily choose 1024 bytes for the hostname. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-commit-tree.txt | 4 -- Documentation/git-var.txt | 4 -- ident.c | 104 ++++++++++++++------------------------ 3 files changed, 39 insertions(+), 73 deletions(-) diff --git a/Documentation/git-commit-tree.txt b/Documentation/git-commit-tree.txt index cfb9906bb5..eb12b2dd91 100644 --- a/Documentation/git-commit-tree.txt +++ b/Documentation/git-commit-tree.txt @@ -92,10 +92,6 @@ Diagnostics ----------- You don't exist. Go away!:: The passwd(5) gecos field couldn't be read -Your parents must have hated you!:: - The passwd(5) gecos field is longer than a giant static buffer. -Your sysadmin must hate you!:: - The passwd(5) name field is longer than a giant static buffer. Discussion ---------- diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt index 988a3234f4..3f703e3775 100644 --- a/Documentation/git-var.txt +++ b/Documentation/git-var.txt @@ -63,10 +63,6 @@ Diagnostics ----------- You don't exist. Go away!:: The passwd(5) gecos field couldn't be read -Your parents must have hated you!:: - The passwd(5) gecos field is longer than a giant static buffer. -Your sysadmin must hate you!:: - The passwd(5) name field is longer than a giant static buffer. SEE ALSO -------- diff --git a/ident.c b/ident.c index 87e3cbe589..73a06a11fa 100644 --- a/ident.c +++ b/ident.c @@ -7,9 +7,8 @@ */ #include "cache.h" -#define MAX_GITNAME (1000) -static char git_default_name[MAX_GITNAME]; -static char git_default_email[MAX_GITNAME]; +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; @@ -19,42 +18,27 @@ int user_ident_explicitly_given; #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; @@ -65,7 +49,7 @@ static int add_mailname_host(char *buf, size_t len) 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)); @@ -74,85 +58,73 @@ static int add_mailname_host(char *buf, size_t len) } /* success! */ fclose(mailname); - - len = strlen(buf); - if (len && buf[len-1] == '\n') - buf[len-1] = '\0'; 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)) + strbuf_addstr(out, buf); + if (strchr(buf, '.')) return; he = gethostbyname(buf); - buf[namelen++] = '.'; - buf += namelen; - len -= namelen; + strbuf_addch(out, '.'); if (he && (domainname = strchr(he->h_name, '.'))) - strlcpy(buf, domainname + 1, len); + strbuf_addstr(out, domainname + 1); else - strlcpy(buf, "(none)", len); + strbuf_addstr(out, "(none)"); } -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); } const char *ident_default_name(void) { - if (!git_default_name[0]) { + if (!git_default_name.len) { struct passwd *pw = getpwuid(getuid()); if (!pw) die("You don't exist. Go away!"); - copy_gecos(pw, git_default_name, sizeof(git_default_name)); + copy_gecos(pw, &git_default_name); } - return git_default_name; + return git_default_name.buf; } const char *ident_default_email(void) { - if (!git_default_email[0]) { + 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 { struct passwd *pw = getpwuid(getuid()); if (!pw) die("You don't exist. Go away!"); - copy_email(pw); + copy_email(pw, &git_default_email); } } - return git_default_email; + return git_default_email.buf; } const char *ident_default_date(void) @@ -327,7 +299,7 @@ const char *fmt_ident(const char *name, const char *email, struct passwd *pw; if (error_on_no_name) { - if (name == git_default_name) + if (name == git_default_name.buf) fputs(env_hint, stderr); die("empty ident %s <%s> not allowed", name, email); } @@ -397,7 +369,8 @@ int git_ident_config(const char *var, const char *value, void *data) if (!strcmp(var, "user.name")) { if (!value) return config_error_nonbool(var); - strlcpy(git_default_name, value, sizeof(git_default_name)); + strbuf_reset(&git_default_name); + strbuf_addstr(&git_default_name, value); user_ident_explicitly_given |= IDENT_NAME_GIVEN; return 0; } @@ -405,7 +378,8 @@ int git_ident_config(const char *var, const char *value, void *data) if (!strcmp(var, "user.email")) { if (!value) return config_error_nonbool(var); - strlcpy(git_default_email, value, sizeof(git_default_email)); + strbuf_reset(&git_default_email); + strbuf_addstr(&git_default_email, value); user_ident_explicitly_given |= IDENT_MAIL_GIVEN; return 0; } -- cgit v1.2.1 From 2f70587502b6b5a8cfda5a3eff54392f48e2db8d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:10:20 -0400 Subject: ident: report passwd errors with a more friendly message When getpwuid fails, we give a cute but cryptic message. While it makes sense if you know that getpwuid or identity functions are being called, this code is triggered behind the scenes by quite a few git commands these days (e.g., receive-pack on a remote server might use it for a reflog; the current message is hard to distinguish from an authentication error). Let's switch to something that gives a little more context. While we're at it, we can factor out all of the cut-and-pastes of the "you don't exist" message into a wrapper function. Rather than provide xgetpwuid, let's make it even more specific to just getting the passwd entry for the current uid. That's the only way we use getpwuid anyway, and it lets us make an even more specific error message. The current message also fails to mention errno. While the usual cause for getpwuid failing is that the user does not exist, mentioning errno makes it easier to diagnose these problems. Note that POSIX specifies that errno remain untouched if the passwd entry does not exist (but will be set on actual errors), whereas some systems will return ENOENT or similar for a missing entry. We handle both cases in our wrapper. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-commit-tree.txt | 5 ----- Documentation/git-var.txt | 5 ----- git-compat-util.h | 3 +++ ident.c | 20 +++++--------------- wrapper.c | 12 ++++++++++++ 5 files changed, 20 insertions(+), 25 deletions(-) diff --git a/Documentation/git-commit-tree.txt b/Documentation/git-commit-tree.txt index eb12b2dd91..eb8ee9999e 100644 --- a/Documentation/git-commit-tree.txt +++ b/Documentation/git-commit-tree.txt @@ -88,11 +88,6 @@ for one to be entered and terminated with ^D. include::date-formats.txt[] -Diagnostics ------------ -You don't exist. Go away!:: - The passwd(5) gecos field couldn't be read - Discussion ---------- diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt index 3f703e3775..67edf58689 100644 --- a/Documentation/git-var.txt +++ b/Documentation/git-var.txt @@ -59,11 +59,6 @@ ifdef::git-default-pager[] The build you are using chose '{git-default-pager}' as the default. endif::git-default-pager[] -Diagnostics ------------ -You don't exist. Go away!:: - The passwd(5) gecos field couldn't be read - SEE ALSO -------- linkgit:git-commit-tree[1] diff --git a/git-compat-util.h b/git-compat-util.h index ed11ad8119..5bd9ad7d2a 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -595,4 +595,7 @@ int rmdir_or_warn(const char *path); */ int remove_or_warn(unsigned int mode, const char *path); +/* Get the passwd entry for the UID of the current process. */ +struct passwd *xgetpwuid_self(void); + #endif diff --git a/ident.c b/ident.c index 73a06a11fa..5aec073b96 100644 --- a/ident.c +++ b/ident.c @@ -100,12 +100,8 @@ static void copy_email(const struct passwd *pw, struct strbuf *email) const char *ident_default_name(void) { - if (!git_default_name.len) { - struct passwd *pw = getpwuid(getuid()); - if (!pw) - die("You don't exist. Go away!"); - copy_gecos(pw, &git_default_name); - } + if (!git_default_name.len) + copy_gecos(xgetpwuid_self(), &git_default_name); return git_default_name.buf; } @@ -117,12 +113,8 @@ const char *ident_default_email(void) if (email && email[0]) { strbuf_addstr(&git_default_email, email); user_ident_explicitly_given |= IDENT_MAIL_GIVEN; - } else { - struct passwd *pw = getpwuid(getuid()); - if (!pw) - die("You don't exist. Go away!"); - copy_email(pw, &git_default_email); - } + } else + copy_email(xgetpwuid_self(), &git_default_email); } return git_default_email.buf; } @@ -303,9 +295,7 @@ const char *fmt_ident(const char *name, const char *email, fputs(env_hint, stderr); die("empty ident %s <%s> not allowed", name, email); } - pw = getpwuid(getuid()); - if (!pw) - die("You don't exist. Go away!"); + pw = xgetpwuid_self(); name = pw->pw_name; } diff --git a/wrapper.c b/wrapper.c index 6ccd0595f4..b5e33e49c7 100644 --- a/wrapper.c +++ b/wrapper.c @@ -402,3 +402,15 @@ int remove_or_warn(unsigned int mode, const char *file) { return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file); } + +struct passwd *xgetpwuid_self(void) +{ + struct passwd *pw; + + errno = 0; + pw = getpwuid(getuid()); + if (!pw) + die(_("unable to look up current user in the passwd file: %s"), + errno ? strerror(errno) : _("no such user")); + return pw; +} -- cgit v1.2.1 From f8254d321cc42a6d47abf79b0f93d16e28432df3 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:10:23 -0400 Subject: ident: use full dns names to generate email addresses When we construct an email address from the username and hostname, we generate the host part of the email with this procedure: 1. add the result of gethostname 2. if it has a dot, ok, it's fully qualified 3. if not, then look up the unqualified hostname via gethostbyname; take the domain name of the result and append it to the hostname Step 3 can actually produce a bogus result, as the name returned by gethostbyname may not be related to the hostname we fed it (e.g., consider a machine "foo" with names "foo.one.example.com" and "bar.two.example.com"; we may have the latter returned and generate the bogus name "foo.two.example.com"). This patch simply uses the full hostname returned by gethostbyname. In the common case that the first part is the same as the unqualified hostname, the behavior is identical. And in the case that it is not the same, we are much more likely to be generating a valid name. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- ident.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/ident.c b/ident.c index 5aec073b96..b111e34140 100644 --- a/ident.c +++ b/ident.c @@ -65,23 +65,18 @@ static void add_domainname(struct strbuf *out) { char buf[1024]; struct hostent *he; - const char *domainname; if (gethostname(buf, sizeof(buf))) { warning("cannot get host name: %s", strerror(errno)); strbuf_addstr(out, "(none)"); return; } - strbuf_addstr(out, buf); if (strchr(buf, '.')) - return; - - he = gethostbyname(buf); - strbuf_addch(out, '.'); - if (he && (domainname = strchr(he->h_name, '.'))) - strbuf_addstr(out, domainname + 1); + strbuf_addstr(out, buf); + else if ((he = gethostbyname(buf)) && strchr(he->h_name, '.')) + strbuf_addstr(out, he->h_name); else - strbuf_addstr(out, "(none)"); + strbuf_addf(out, "%s.(none)", buf); } static void copy_email(const struct passwd *pw, struct strbuf *email) -- cgit v1.2.1 From c96f0c8d0a9f1eeabb2ea49cb7ede954a64bd540 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:10:26 -0400 Subject: ident: use a dynamic strbuf in fmt_ident Now that we accept arbitrary-sized names and email addresses, the only remaining limit is in the actual formatting of the names into a buffer. The current limit is 1000 characters, which is not likely to be reached, but using a strbuf is one less error condition we have to worry about. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- ident.c | 43 +++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/ident.c b/ident.c index b111e34140..cefb8294c3 100644 --- a/ident.c +++ b/ident.c @@ -121,15 +121,6 @@ const char *ident_default_date(void) return git_default_date; } -static int add_raw(char *buf, size_t size, int offset, const char *str) -{ - size_t len = strlen(str); - if (offset + len > size) - return size; - memcpy(buf + offset, str, len); - return offset + len; -} - static int crud(unsigned char c) { return c <= 32 || @@ -148,7 +139,7 @@ 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; @@ -172,19 +163,19 @@ static int copy(char *buf, size_t size, int offset, const char *src) /* * 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'; } /* @@ -271,9 +262,8 @@ 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 name_addr_only = (flag & IDENT_NO_DATE); @@ -300,19 +290,16 @@ const char *fmt_ident(const char *name, const char *email, die("invalid date format: %s", date_str); } - i = copy(buffer, sizeof(buffer), 0, name); - i = add_raw(buffer, sizeof(buffer), i, " <"); - i = copy(buffer, sizeof(buffer), i, email); + strbuf_reset(&ident); + strbuf_addstr_without_crud(&ident, name); + strbuf_addstr(&ident, " <"); + strbuf_addstr_without_crud(&ident, email); + strbuf_addch(&ident, '>'); 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_addch(&ident, ' '); + strbuf_addstr_without_crud(&ident, date); } - if (i >= sizeof(buffer)) - die("Impossibly long personal identifier"); - buffer[i] = 0; - return buffer; + return ident.buf; } const char *fmt_name(const char *name, const char *email) -- cgit v1.2.1 From be641abdb544f00adb7ae6fdab41f9bd5453e206 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:10:29 -0400 Subject: ident: trim whitespace from default name/email Usually these values get fed to fmt_ident, which will trim any cruft anyway, but there are a few code paths which use them directly. Let's clean them up for the benefit of those callers. Furthermore, fmt_ident will look at the pre-trimmed value and decide whether to invoke ERROR_ON_NO_NAME; this check can be fooled by a name consisting only of spaces. Note that we only bother to clean up when we are pulling the information from gecos or from system files. Any other value comes from a config file, where we will have cleaned up accidental whitespace already. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- ident.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ident.c b/ident.c index cefb8294c3..e279039ac5 100644 --- a/ident.c +++ b/ident.c @@ -95,8 +95,10 @@ static void copy_email(const struct passwd *pw, struct strbuf *email) const char *ident_default_name(void) { - if (!git_default_name.len) + if (!git_default_name.len) { copy_gecos(xgetpwuid_self(), &git_default_name); + strbuf_trim(&git_default_name); + } return git_default_name.buf; } @@ -110,6 +112,7 @@ const char *ident_default_email(void) user_ident_explicitly_given |= IDENT_MAIL_GIVEN; } else copy_email(xgetpwuid_self(), &git_default_email); + strbuf_trim(&git_default_email); } return git_default_email.buf; } -- cgit v1.2.1 From a21c2f94fb6e74ebddb5c78cf6b5f68983646530 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 21 May 2012 19:10:32 -0400 Subject: format-patch: refactor get_patch_filename The get_patch_filename function expects a commit argument and uses it to get the sanitized subject line when making a patch filename. However, we also want to use this same function for the cover letter, which does not have a commit object. The current solution is to create a fake commit with the subject "cover letter". Instead, let's make the get_patch_filename interface more flexibile, and allow passing a direct subject. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/log.c | 35 +++++++---------------------------- log-tree.c | 19 +++++++++++-------- log-tree.h | 4 ++-- 3 files changed, 20 insertions(+), 38 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index 656bddfb03..8010a4045e 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -663,7 +663,8 @@ static FILE *realstdout = NULL; static const char *output_directory = NULL; static int outdir_offset; -static int reopen_stdout(struct commit *commit, struct rev_info *rev, int quiet) +static int reopen_stdout(struct commit *commit, const char *subject, + struct rev_info *rev, int quiet) { struct strbuf filename = STRBUF_INIT; int suffix_len = strlen(fmt_patch_suffix) + 1; @@ -677,7 +678,7 @@ static int reopen_stdout(struct commit *commit, struct rev_info *rev, int quiet) strbuf_addch(&filename, '/'); } - get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename); + get_patch_filename(commit, subject, rev->nr, fmt_patch_suffix, &filename); if (!quiet) fprintf(realstdout, "%s\n", filename.buf + outdir_offset); @@ -778,7 +779,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, const char *encoding = "UTF-8"; struct diff_options opts; int need_8bit_cte = 0; - struct commit *commit = NULL; struct pretty_print_context pp = {0}; if (rev->commit_format != CMIT_FMT_EMAIL) @@ -786,31 +786,10 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, committer = git_committer_info(0); - if (!numbered_files) { - /* - * We fake a commit for the cover letter so we get the filename - * desired. - */ - commit = xcalloc(1, sizeof(*commit)); - commit->buffer = xmalloc(400); - snprintf(commit->buffer, 400, - "tree 0000000000000000000000000000000000000000\n" - "parent %s\n" - "author %s\n" - "committer %s\n\n" - "cover letter\n", - sha1_to_hex(head->object.sha1), committer, committer); - } - - if (!use_stdout && reopen_stdout(commit, rev, quiet)) + if (!use_stdout && + reopen_stdout(NULL, numbered_files ? NULL : "cover-letter", rev, quiet)) return; - if (commit) { - - free(commit->buffer); - free(commit); - } - log_write_email_headers(rev, head, &pp.subject, &pp.after_subject, &need_8bit_cte); @@ -1405,8 +1384,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } - if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit, - &rev, quiet)) + if (!use_stdout && + reopen_stdout(numbered_files ? NULL : commit, NULL, &rev, quiet)) die(_("Failed to create output files")); shown = log_tree_commit(&rev, commit); free(commit->buffer); diff --git a/log-tree.c b/log-tree.c index 376d973176..c894930c18 100644 --- a/log-tree.c +++ b/log-tree.c @@ -299,19 +299,22 @@ static unsigned int digits_in_number(unsigned int number) return result; } -void get_patch_filename(struct commit *commit, int nr, const char *suffix, - struct strbuf *buf) +void get_patch_filename(struct commit *commit, const char *subject, int nr, + const char *suffix, struct strbuf *buf) { int suffix_len = strlen(suffix) + 1; int start_len = buf->len; - strbuf_addf(buf, commit ? "%04d-" : "%d", nr); - if (commit) { + strbuf_addf(buf, commit || subject ? "%04d-" : "%d", nr); + if (commit || subject) { int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len; struct pretty_print_context ctx = {0}; - ctx.date_mode = DATE_NORMAL; - format_commit_message(commit, "%f", buf, &ctx); + if (subject) + strbuf_addstr(buf, subject); + else if (commit) + format_commit_message(commit, "%f", buf, &ctx); + if (max_len < buf->len) strbuf_setlen(buf, max_len); strbuf_addstr(buf, suffix); @@ -384,8 +387,8 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit, mime_boundary_leader, opt->mime_boundary); extra_headers = subject_buffer; - get_patch_filename(opt->numbered_files ? NULL : commit, opt->nr, - opt->patch_suffix, &filename); + get_patch_filename(opt->numbered_files ? NULL : commit, NULL, + opt->nr, opt->patch_suffix, &filename); snprintf(buffer, sizeof(buffer) - 1, "\n--%s%s\n" "Content-Type: text/x-patch;" diff --git a/log-tree.h b/log-tree.h index 5c4cf7cac3..f5ac238bba 100644 --- a/log-tree.h +++ b/log-tree.h @@ -21,7 +21,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit, void load_ref_decorations(int flags); #define FORMAT_PATCH_NAME_MAX 64 -void get_patch_filename(struct commit *commit, int nr, const char *suffix, - struct strbuf *buf); +void get_patch_filename(struct commit *commit, const char *subject, int nr, + const char *suffix, struct strbuf *buf); #endif -- cgit v1.2.1 From b00f6cfcd7232d90c4625c42eb9694d4ed2dc615 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 May 2012 19:26:32 -0400 Subject: ident: reword empty ident error message There's on point in printing the name, since it is by definition the empty string if we have reached this code path. Instead, let's be more clear that we are complaining about the empty name, but still show the email address that it is attached to (since that may provide some context to the user). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- ident.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ident.c b/ident.c index e279039ac5..f5160e1f43 100644 --- a/ident.c +++ b/ident.c @@ -281,7 +281,7 @@ const char *fmt_ident(const char *name, const char *email, if (error_on_no_name) { if (name == git_default_name.buf) fputs(env_hint, stderr); - die("empty ident %s <%s> not allowed", name, email); + die("empty ident name (for <%s>) not allowed", email); } pw = xgetpwuid_self(); name = pw->pw_name; -- cgit v1.2.1 From 359b27add341878a13c6a8b85849b75b78246c7e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 May 2012 19:26:50 -0400 Subject: ident: refactor NO_DATE flag in fmt_ident As a short-hand, we extract this flag into the local variable "name_addr_only". It's more accurate to simply negate this and refer to it as "want_date", which will be less confusing when we add more NO_* flags. While we're touching this part of the code, let's move the call to ident_default_date() only when we are actually going to use it, not when we have NO_DATE set, or when we get a date from the environment. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- ident.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ident.c b/ident.c index f5160e1f43..59beef2f35 100644 --- a/ident.c +++ b/ident.c @@ -268,7 +268,7 @@ const char *fmt_ident(const char *name, const char *email, static struct strbuf ident = STRBUF_INIT; char date[50]; int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME); - int name_addr_only = (flag & IDENT_NO_DATE); + int want_date = !(flag & IDENT_NO_DATE); if (!name) name = ident_default_name(); @@ -287,10 +287,13 @@ const char *fmt_ident(const char *name, const char *email, name = pw->pw_name; } - strcpy(date, ident_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()); } strbuf_reset(&ident); @@ -298,7 +301,7 @@ const char *fmt_ident(const char *name, const char *email, strbuf_addstr(&ident, " <"); strbuf_addstr_without_crud(&ident, email); strbuf_addch(&ident, '>'); - if (!name_addr_only) { + if (want_date) { strbuf_addch(&ident, ' '); strbuf_addstr_without_crud(&ident, date); } -- cgit v1.2.1 From c15e1987aec562107bd8e4a9fdeebf6d27d0e53a Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 May 2012 19:27:24 -0400 Subject: ident: let callers omit name with fmt_indent Most callers want to see all of "$name <$email> $date", but a few want only limited parts, omitting the date, or even the name. We already have IDENT_NO_DATE to handle the date part, but there's not a good option for getting just the email. Callers have to done one of: 1. Call ident_default_email; this does not respect environment variables, nor does it promise to trim whitespace or other crud from the result. 2. Call git_{committer,author}_info; this returns the name and email, leaving the caller to parse out the wanted bits. This patch adds IDENT_NO_NAME; it stops short of adding IDENT_NO_EMAIL, as no callers want it (nor are likely to), and it complicates the error handling of the function. When no name is requested, the angle brackets (<>) around the email address are also omitted. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- cache.h | 1 + ident.c | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/cache.h b/cache.h index 65cbab5564..713cd04e1e 100644 --- a/cache.h +++ b/cache.h @@ -889,6 +889,7 @@ enum date_mode parse_date_format(const char *format); #define IDENT_ERROR_ON_NO_NAME 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); diff --git a/ident.c b/ident.c index 59beef2f35..8b5080dfe6 100644 --- a/ident.c +++ b/ident.c @@ -269,13 +269,14 @@ const char *fmt_ident(const char *name, const char *email, char date[50]; int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME); int want_date = !(flag & IDENT_NO_DATE); + int want_name = !(flag & IDENT_NO_NAME); - if (!name) + if (want_name && !name) name = ident_default_name(); if (!email) email = ident_default_email(); - if (!*name) { + if (want_name && !*name) { struct passwd *pw; if (error_on_no_name) { @@ -297,10 +298,13 @@ const char *fmt_ident(const char *name, const char *email, } strbuf_reset(&ident); - strbuf_addstr_without_crud(&ident, name); - strbuf_addstr(&ident, " <"); + if (want_name) { + strbuf_addstr_without_crud(&ident, name); + strbuf_addstr(&ident, " <"); + } strbuf_addstr_without_crud(&ident, email); - strbuf_addch(&ident, '>'); + if (want_name) + strbuf_addch(&ident, '>'); if (want_date) { strbuf_addch(&ident, ' '); strbuf_addstr_without_crud(&ident, date); -- cgit v1.2.1 From c73f384f92dcb0d7111533ed21f6c2dc9fcc323b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 May 2012 19:28:25 -0400 Subject: format-patch: use GIT_COMMITTER_EMAIL in message ids Before commit 43ae9f4, we generated the tail of a message id by calling git_committer_info and parsing the email out of the result. 43ae9f4 changed to use ident_default_email directly, so we didn't have to bother with parsing. As a side effect, it meant we no longer used GIT_COMMITTER_EMAIL at all. In general, this is probably reasonable behavior. Either the default email is sane on your system, or you are using user.email to provide something sane. The exception is if you rely on GIT_COMMITTER_EMAIL being set all the time to override the bogus generated email. This is unlikely to match anybody's real-life setup, but we do use it in the test environment. And furthermore, it's what we have always done, and the change in 43ae9f4 was about cleaning up, not fixing any bug; we should be conservative and keep the behavior identical. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/log.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin/log.c b/builtin/log.c index 8010a4045e..4538309d02 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -740,7 +740,8 @@ static void gen_message_id(struct rev_info *info, char *base) { struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "%s.%lu.git.%s", base, - (unsigned long) time(NULL), ident_default_email()); + (unsigned long) time(NULL), + git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE)); info->message_id = strbuf_detach(&buf, NULL); } -- cgit v1.2.1 From f9bc573fdaeaf8621008f3f49aaaa64869791691 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 May 2012 19:28:40 -0400 Subject: ident: rename IDENT_ERROR_ON_NO_NAME to IDENT_STRICT Callers who ask for ERROR_ON_NO_NAME are not so much concerned that the name will be blank (because, after all, we will fall back to using the username), but rather it is a check to make sure that low-quality identities do not end up in things like commit messages or emails (whereas it is OK for them to end up in things like reflogs). When future commits add more quality checks on the identity, each of these callers would want to use those checks, too. Rather than modify each of them later to add a new flag, let's refactor the flag. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/commit.c | 3 +-- builtin/log.c | 2 +- builtin/merge.c | 4 ++-- builtin/tag.c | 2 +- builtin/var.c | 4 ++-- cache.h | 2 +- commit.c | 4 ++-- gpg-interface.c | 2 +- ident.c | 6 +++--- 9 files changed, 14 insertions(+), 15 deletions(-) diff --git a/builtin/commit.c b/builtin/commit.c index a2ec73d738..f43eaafb3b 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -526,8 +526,7 @@ static void determine_author_info(struct strbuf *author_ident) if (force_date) date = force_date; - strbuf_addstr(author_ident, fmt_ident(name, email, date, - IDENT_ERROR_ON_NO_NAME)); + strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT)); if (!split_ident_line(&author, author_ident->buf, author_ident->len)) { export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0); export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0); diff --git a/builtin/log.c b/builtin/log.c index 4538309d02..d86bca34dd 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1147,7 +1147,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (do_signoff) { const char *committer; const char *endpos; - committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); + committer = git_committer_info(IDENT_STRICT); endpos = strchr(committer, '>'); if (!endpos) die(_("bogus committer info %s"), committer); diff --git a/builtin/merge.c b/builtin/merge.c index 470fc57c5d..dd50a0c57b 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -1447,7 +1447,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) refresh_cache(REFRESH_QUIET); if (allow_trivial && !fast_forward_only) { /* See if it is really trivial. */ - git_committer_info(IDENT_ERROR_ON_NO_NAME); + git_committer_info(IDENT_STRICT); printf(_("Trying really trivial in-index merge...\n")); if (!read_tree_trivial(common->item->object.sha1, head_commit->object.sha1, @@ -1490,7 +1490,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) die(_("Not possible to fast-forward, aborting.")); /* We are going to make a new commit. */ - git_committer_info(IDENT_ERROR_ON_NO_NAME); + git_committer_info(IDENT_STRICT); /* * At this point, we need a real merge. No matter what strategy diff --git a/builtin/tag.c b/builtin/tag.c index 4fb6bd7b3d..7b1be85e48 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -332,7 +332,7 @@ static void create_tag(const unsigned char *object, const char *tag, sha1_to_hex(object), typename(type), tag, - git_committer_info(IDENT_ERROR_ON_NO_NAME)); + git_committer_info(IDENT_STRICT)); if (header_len > sizeof(header_buf) - 1) die(_("tag header too big.")); diff --git a/builtin/var.c b/builtin/var.c index 99d068a532..aedbb53a2d 100644 --- a/builtin/var.c +++ b/builtin/var.c @@ -11,7 +11,7 @@ static const char *editor(int flag) { const char *pgm = git_editor(); - if (!pgm && flag & IDENT_ERROR_ON_NO_NAME) + if (!pgm && flag & IDENT_STRICT) die("Terminal is dumb, but EDITOR unset"); return pgm; @@ -55,7 +55,7 @@ static const char *read_var(const char *var) val = NULL; for (ptr = git_vars; ptr->read; ptr++) { if (strcmp(var, ptr->name) == 0) { - val = ptr->read(IDENT_ERROR_ON_NO_NAME); + val = ptr->read(IDENT_STRICT); break; } } diff --git a/cache.h b/cache.h index 713cd04e1e..f89f22db70 100644 --- a/cache.h +++ b/cache.h @@ -887,7 +887,7 @@ unsigned long approxidate_careful(const char *, int *); unsigned long approxidate_relative(const char *date, const struct timeval *now); enum date_mode parse_date_format(const char *format); -#define IDENT_ERROR_ON_NO_NAME 1 +#define IDENT_STRICT 1 #define IDENT_NO_DATE 2 #define IDENT_NO_NAME 4 extern const char *git_author_info(int); diff --git a/commit.c b/commit.c index 9ed36c7db5..8248a994a5 100644 --- a/commit.c +++ b/commit.c @@ -1154,9 +1154,9 @@ int commit_tree_extended(const struct strbuf *msg, unsigned char *tree, /* Person/date information */ if (!author) - author = git_author_info(IDENT_ERROR_ON_NO_NAME); + author = git_author_info(IDENT_STRICT); strbuf_addf(&buffer, "author %s\n", author); - strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME)); + strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_STRICT)); if (!encoding_is_utf8) strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding); diff --git a/gpg-interface.c b/gpg-interface.c index 09ab64aa24..0863c61800 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -30,7 +30,7 @@ const char *get_signing_key(void) { if (configured_signing_key) return configured_signing_key; - return git_committer_info(IDENT_ERROR_ON_NO_NAME|IDENT_NO_DATE); + return git_committer_info(IDENT_STRICT|IDENT_NO_DATE); } /* diff --git a/ident.c b/ident.c index 8b5080dfe6..c42258f4ea 100644 --- a/ident.c +++ b/ident.c @@ -267,7 +267,7 @@ const char *fmt_ident(const char *name, const char *email, { static struct strbuf ident = STRBUF_INIT; char date[50]; - int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME); + int strict = (flag & IDENT_STRICT); int want_date = !(flag & IDENT_NO_DATE); int want_name = !(flag & IDENT_NO_NAME); @@ -279,7 +279,7 @@ const char *fmt_ident(const char *name, const char *email, if (want_name && !*name) { struct passwd *pw; - if (error_on_no_name) { + if (strict) { if (name == git_default_name.buf) fputs(env_hint, stderr); die("empty ident name (for <%s>) not allowed", email); @@ -314,7 +314,7 @@ const char *fmt_ident(const char *name, const char *email, 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) -- cgit v1.2.1 From 8c5b1ae1b26a7512eb29e75391b8b24c0d0439e7 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 May 2012 19:32:37 -0400 Subject: ident: reject bogus email addresses with IDENT_STRICT If we come up with a hostname like "foo.(none)" because the user's machine is not fully qualified, we should reject this in strict mode (e.g., when we are making a commit object), just as we reject an empty gecos username. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- ident.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ident.c b/ident.c index c42258f4ea..98852c7cc9 100644 --- a/ident.c +++ b/ident.c @@ -288,6 +288,12 @@ const char *fmt_ident(const char *name, const char *email, 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); + } + if (want_date) { if (date_str && date_str[0]) { if (parse_date(date_str, date, sizeof(date)) < 0) -- cgit v1.2.1 From 59f9b8a9a95cbcf4f6a5123cac04dc5073a8d0cc Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 May 2012 19:32:52 -0400 Subject: format-patch: do not use bogus email addresses in message ids We can ask git_committer_info to be strict about coming up with an email, which will die automatically on a poorly configured machine. This is better than letting invalid message-ids into the wild. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/log.c b/builtin/log.c index d86bca34dd..906dca475a 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -741,7 +741,7 @@ static void gen_message_id(struct rev_info *info, char *base) struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "%s.%lu.git.%s", base, (unsigned long) time(NULL), - git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE)); + git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT)); info->message_id = strbuf_detach(&buf, NULL); } -- cgit v1.2.1