mirror of
https://github.com/git/git
synced 2024-08-24 18:26:02 +00:00
![Jonathan Nieder](/assets/img/avatar_default.png)
Avoid a getpwuid() call (which contacts the network if the password database is not local), read of /etc/mailname, gethostname() call, and reverse DNS lookup if the user has already chosen a name and email through configuration, the environment, or the command line. This should slightly speed up commands like "git commit". More importantly, it improves error reporting when computation of the default ident string does not go smoothly. For example, after detecting a problem (e.g., "warning: cannot open /etc/mailname: Permission denied") in retrieving the default committer identity: touch /etc/mailname; # as root chmod -r /etc/mailname; # as root git commit -m 'test commit' you can squelch the warning while waiting for your sysadmin to fix the permissions problem. echo '[user] email = me@example.com' >>~/.gitconfig Inspired-by: Johannes Sixt <j6t@kdgb.org> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
320 lines
7 KiB
C
320 lines
7 KiB
C
/*
|
|
* ident.c
|
|
*
|
|
* create git identifier lines of the form "name <email> date"
|
|
*
|
|
* Copyright (C) 2005 Linus Torvalds
|
|
*/
|
|
#include "cache.h"
|
|
|
|
static char git_default_date[50];
|
|
|
|
#ifdef NO_GECOS_IN_PWENT
|
|
#define get_gecos(ignored) "&"
|
|
#else
|
|
#define get_gecos(struct_passwd) ((struct_passwd)->pw_gecos)
|
|
#endif
|
|
|
|
static void copy_gecos(const struct passwd *w, char *name, size_t sz)
|
|
{
|
|
char *src, *dst;
|
|
size_t len, nlen;
|
|
|
|
nlen = strlen(w->pw_name);
|
|
|
|
/* 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++) {
|
|
int ch = *src;
|
|
if (ch != '&') {
|
|
*dst++ = ch;
|
|
if (ch == 0 || ch == ',')
|
|
break;
|
|
len++;
|
|
continue;
|
|
}
|
|
if (len + nlen < sz) {
|
|
/* Sorry, Mr. McDonald... */
|
|
*dst++ = toupper(*w->pw_name);
|
|
memcpy(dst, w->pw_name + 1, nlen - 1);
|
|
dst += nlen - 1;
|
|
len += nlen;
|
|
}
|
|
}
|
|
if (len < sz)
|
|
name[len] = 0;
|
|
else
|
|
die("Your parents must have hated you!");
|
|
|
|
}
|
|
|
|
static int add_mailname_host(char *buf, size_t len)
|
|
{
|
|
FILE *mailname;
|
|
|
|
mailname = fopen("/etc/mailname", "r");
|
|
if (!mailname) {
|
|
if (errno != ENOENT)
|
|
warning("cannot open /etc/mailname: %s",
|
|
strerror(errno));
|
|
return -1;
|
|
}
|
|
if (!fgets(buf, len, mailname)) {
|
|
if (ferror(mailname))
|
|
warning("cannot read /etc/mailname: %s",
|
|
strerror(errno));
|
|
fclose(mailname);
|
|
return -1;
|
|
}
|
|
/* success! */
|
|
fclose(mailname);
|
|
return 0;
|
|
}
|
|
|
|
static void add_domainname(char *buf, size_t len)
|
|
{
|
|
struct hostent *he;
|
|
size_t namelen;
|
|
const char *domainname;
|
|
|
|
if (gethostname(buf, len)) {
|
|
warning("cannot get host name: %s", strerror(errno));
|
|
strlcpy(buf, "(none)", len);
|
|
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);
|
|
else
|
|
strlcpy(buf, "(none)", len);
|
|
}
|
|
|
|
static void copy_email(const struct passwd *pw)
|
|
{
|
|
/*
|
|
* 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))
|
|
return; /* read from "/etc/mailname" (Debian) */
|
|
add_domainname(git_default_email + len,
|
|
sizeof(git_default_email) - len);
|
|
}
|
|
|
|
static void setup_ident(const char **name, const char **emailp)
|
|
{
|
|
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 (!*name)
|
|
*name = git_default_name;
|
|
|
|
if (!*emailp && !git_default_email[0]) {
|
|
const char *email = getenv("EMAIL");
|
|
|
|
if (email && email[0]) {
|
|
strlcpy(git_default_email, email,
|
|
sizeof(git_default_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);
|
|
}
|
|
}
|
|
if (!*emailp)
|
|
*emailp = git_default_email;
|
|
|
|
/* And set the default date */
|
|
if (!git_default_date[0])
|
|
datestamp(git_default_date, sizeof(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 ||
|
|
c == '.' ||
|
|
c == ',' ||
|
|
c == ':' ||
|
|
c == ';' ||
|
|
c == '<' ||
|
|
c == '>' ||
|
|
c == '"' ||
|
|
c == '\\' ||
|
|
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)
|
|
{
|
|
size_t i, len;
|
|
unsigned char c;
|
|
|
|
/* Remove crud from the beginning.. */
|
|
while ((c = *src) != 0) {
|
|
if (!crud(c))
|
|
break;
|
|
src++;
|
|
}
|
|
|
|
/* Remove crud from the end.. */
|
|
len = strlen(src);
|
|
while (len > 0) {
|
|
c = src[len-1];
|
|
if (!crud(c))
|
|
break;
|
|
--len;
|
|
}
|
|
|
|
/*
|
|
* Copy the rest to the buffer, but avoid the special
|
|
* characters '\n' '<' and '>' that act as delimiters on
|
|
* an identification line
|
|
*/
|
|
for (i = 0; i < len; i++) {
|
|
c = *src++;
|
|
switch (c) {
|
|
case '\n': case '<': case '>':
|
|
continue;
|
|
}
|
|
if (offset >= size)
|
|
return size;
|
|
buf[offset++] = c;
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
static const char *env_hint =
|
|
"\n"
|
|
"*** Please tell me who you are.\n"
|
|
"\n"
|
|
"Run\n"
|
|
"\n"
|
|
" git config --global user.email \"you@example.com\"\n"
|
|
" git config --global user.name \"Your Name\"\n"
|
|
"\n"
|
|
"to set your account\'s default identity.\n"
|
|
"Omit --global to set the identity only in this repository.\n"
|
|
"\n";
|
|
|
|
const char *fmt_ident(const char *name, const char *email,
|
|
const char *date_str, int flag)
|
|
{
|
|
static char buffer[1000];
|
|
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);
|
|
|
|
setup_ident(&name, &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)
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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, ">");
|
|
}
|
|
if (i >= sizeof(buffer))
|
|
die("Impossibly long personal identifier");
|
|
buffer[i] = 0;
|
|
return buffer;
|
|
}
|
|
|
|
const char *fmt_name(const char *name, const char *email)
|
|
{
|
|
return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
|
|
}
|
|
|
|
const char *git_author_info(int flag)
|
|
{
|
|
return fmt_ident(getenv("GIT_AUTHOR_NAME"),
|
|
getenv("GIT_AUTHOR_EMAIL"),
|
|
getenv("GIT_AUTHOR_DATE"),
|
|
flag);
|
|
}
|
|
|
|
const char *git_committer_info(int flag)
|
|
{
|
|
if (getenv("GIT_COMMITTER_NAME"))
|
|
user_ident_explicitly_given |= IDENT_NAME_GIVEN;
|
|
if (getenv("GIT_COMMITTER_EMAIL"))
|
|
user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
|
|
return fmt_ident(getenv("GIT_COMMITTER_NAME"),
|
|
getenv("GIT_COMMITTER_EMAIL"),
|
|
getenv("GIT_COMMITTER_DATE"),
|
|
flag);
|
|
}
|
|
|
|
int user_ident_sufficiently_given(void)
|
|
{
|
|
#ifndef WINDOWS
|
|
return (user_ident_explicitly_given & IDENT_MAIL_GIVEN);
|
|
#else
|
|
return (user_ident_explicitly_given == IDENT_ALL_GIVEN);
|
|
#endif
|
|
}
|