ident.c 8.1 KB
Newer Older
1 2 3 4 5 6 7 8 9
/*
 * ident.c
 *
 * create git identifier lines of the form "name <email> date"
 *
 * Copyright (C) 2005 Linus Torvalds
 */
#include "cache.h"

10 11
static struct strbuf git_default_name = STRBUF_INIT;
static struct strbuf git_default_email = STRBUF_INIT;
12
static char git_default_date[50];
13
int user_ident_explicitly_given;
14

15 16 17 18 19 20
#ifdef NO_GECOS_IN_PWENT
#define get_gecos(ignored) "&"
#else
#define get_gecos(struct_passwd) ((struct_passwd)->pw_gecos)
#endif

21
static void copy_gecos(const struct passwd *w, struct strbuf *name)
22
{
23
	char *src;
24 25 26 27 28

	/* Traditionally GECOS field had office phone numbers etc, separated
	 * with commas.  Also & stands for capitalized form of the login name.
	 */

29
	for (src = get_gecos(w); *src && *src != ','; src++) {
30
		int ch = *src;
31 32 33
		if (ch != '&')
			strbuf_addch(name, ch);
		else {
34
			/* Sorry, Mr. McDonald... */
35 36
			strbuf_addch(name, toupper(*w->pw_name));
			strbuf_addstr(name, w->pw_name + 1);
37 38 39 40
		}
	}
}

41
static int add_mailname_host(struct strbuf *buf)
42 43 44 45 46 47 48 49 50 51
{
	FILE *mailname;

	mailname = fopen("/etc/mailname", "r");
	if (!mailname) {
		if (errno != ENOENT)
			warning("cannot open /etc/mailname: %s",
				strerror(errno));
		return -1;
	}
52
	if (strbuf_getline(buf, mailname, '\n') == EOF) {
53 54 55 56 57 58 59 60 61 62 63
		if (ferror(mailname))
			warning("cannot read /etc/mailname: %s",
				strerror(errno));
		fclose(mailname);
		return -1;
	}
	/* success! */
	fclose(mailname);
	return 0;
}

64
static void add_domainname(struct strbuf *out)
65
{
66
	char buf[1024];
67 68
	struct hostent *he;

69
	if (gethostname(buf, sizeof(buf))) {
70
		warning("cannot get host name: %s", strerror(errno));
71
		strbuf_addstr(out, "(none)");
72 73
		return;
	}
74
	if (strchr(buf, '.'))
75 76 77
		strbuf_addstr(out, buf);
	else if ((he = gethostbyname(buf)) && strchr(he->h_name, '.'))
		strbuf_addstr(out, he->h_name);
78
	else
79
		strbuf_addf(out, "%s.(none)", buf);
80 81
}

82
static void copy_email(const struct passwd *pw, struct strbuf *email)
83
{
84 85 86 87
	/*
	 * Make up a fake email address
	 * (name + '@' + hostname [+ '.' + domainname])
	 */
88 89 90 91
	strbuf_addstr(email, pw->pw_name);
	strbuf_addch(email, '@');

	if (!add_mailname_host(email))
92
		return;	/* read from "/etc/mailname" (Debian) */
93
	add_domainname(email);
94 95
}

96
const char *ident_default_name(void)
97
{
98
	if (!git_default_name.len) {
99
		copy_gecos(xgetpwuid_self(), &git_default_name);
100 101
		strbuf_trim(&git_default_name);
	}
102
	return git_default_name.buf;
103
}
104

105 106
const char *ident_default_email(void)
{
107
	if (!git_default_email.len) {
M
Matt Kraai 已提交
108 109
		const char *email = getenv("EMAIL");

110
		if (email && email[0]) {
111
			strbuf_addstr(&git_default_email, email);
112
			user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
113 114
		} else
			copy_email(xgetpwuid_self(), &git_default_email);
115
		strbuf_trim(&git_default_email);
116
	}
117
	return git_default_email.buf;
118
}
119

120 121
const char *ident_default_date(void)
{
122 123
	if (!git_default_date[0])
		datestamp(git_default_date, sizeof(git_default_date));
124
	return git_default_date;
125 126 127 128
}

static int crud(unsigned char c)
{
A
Alex Riesen 已提交
129 130 131 132 133 134 135 136
	return  c <= 32  ||
		c == '.' ||
		c == ',' ||
		c == ':' ||
		c == ';' ||
		c == '<' ||
		c == '>' ||
		c == '"' ||
137
		c == '\\' ||
A
Alex Riesen 已提交
138
		c == '\'';
139 140 141 142 143 144
}

/*
 * Copy over a string to the destination, but avoid special
 * characters ('\n', '<' and '>') and remove crud at the end
 */
145
static void strbuf_addstr_without_crud(struct strbuf *sb, const char *src)
146
{
147
	size_t i, len;
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
	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
J
Junio C Hamano 已提交
168
	 * characters '\n' '<' and '>' that act as delimiters on
169 170
	 * an identification line. We can only remove crud, never add it,
	 * so 'len' is our maximum.
171
	 */
172
	strbuf_grow(sb, len);
173 174 175 176 177 178
	for (i = 0; i < len; i++) {
		c = *src++;
		switch (c) {
		case '\n': case '<': case '>':
			continue;
		}
179
		sb->buf[sb->len++] = c;
180
	}
181
	sb->buf[sb->len] = '\0';
182 183
}

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
/*
 * Reverse of fmt_ident(); given an ident line, split the fields
 * to allow the caller to parse it.
 * Signal a success by returning 0, but date/tz fields of the result
 * can still be NULL if the input line only has the name/email part
 * (e.g. reading from a reflog entry).
 */
int split_ident_line(struct ident_split *split, const char *line, int len)
{
	const char *cp;
	size_t span;
	int status = -1;

	memset(split, 0, sizeof(*split));

	split->name_begin = line;
	for (cp = line; *cp && cp < line + len; cp++)
		if (*cp == '<') {
			split->mail_begin = cp + 1;
			break;
		}
	if (!split->mail_begin)
		return status;

	for (cp = split->mail_begin - 2; line < cp; cp--)
		if (!isspace(*cp)) {
			split->name_end = cp + 1;
			break;
		}
	if (!split->name_end)
		return status;

	for (cp = split->mail_begin; cp < line + len; cp++)
		if (*cp == '>') {
			split->mail_end = cp;
			break;
		}
	if (!split->mail_end)
		return status;

	for (cp = split->mail_end + 1; cp < line + len && isspace(*cp); cp++)
		;
	if (line + len <= cp)
		goto person_only;
	split->date_begin = cp;
	span = strspn(cp, "0123456789");
	if (!span)
		goto person_only;
	split->date_end = split->date_begin + span;
	for (cp = split->date_end; cp < line + len && isspace(*cp); cp++)
		;
	if (line + len <= cp || (*cp != '+' && *cp != '-'))
		goto person_only;
	split->tz_begin = cp;
	span = strspn(cp + 1, "0123456789");
	if (!span)
		goto person_only;
	split->tz_end = split->tz_begin + 1 + span;
	return 0;

person_only:
	split->date_begin = NULL;
	split->date_end = NULL;
	split->tz_begin = NULL;
	split->tz_end = NULL;
	return 0;
}

252
static const char *env_hint =
253
"\n"
254
"*** Please tell me who you are.\n"
255 256 257
"\n"
"Run\n"
"\n"
258
"  git config --global user.email \"you@example.com\"\n"
259
"  git config --global user.name \"Your Name\"\n"
260
"\n"
261 262
"to set your account\'s default identity.\n"
"Omit --global to set the identity only in this repository.\n"
263
"\n";
264

265 266
const char *fmt_ident(const char *name, const char *email,
		      const char *date_str, int flag)
267
{
268
	static struct strbuf ident = STRBUF_INIT;
269
	char date[50];
270 271
	int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME);
	int name_addr_only = (flag & IDENT_NO_DATE);
272

273 274 275 276
	if (!name)
		name = ident_default_name();
	if (!email)
		email = ident_default_email();
277

278
	if (!*name) {
279 280
		struct passwd *pw;

281
		if (error_on_no_name) {
282
			if (name == git_default_name.buf)
283
				fputs(env_hint, stderr);
284
			die("empty ident name (for <%s>) not allowed", email);
285
		}
286
		pw = xgetpwuid_self();
287
		name = pw->pw_name;
288
	}
289

290
	strcpy(date, ident_default_date());
J
Jeff King 已提交
291 292 293 294
	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);
	}
295

296 297 298 299 300
	strbuf_reset(&ident);
	strbuf_addstr_without_crud(&ident, name);
	strbuf_addstr(&ident, " <");
	strbuf_addstr_without_crud(&ident, email);
	strbuf_addch(&ident, '>');
301
	if (!name_addr_only) {
302 303
		strbuf_addch(&ident, ' ');
		strbuf_addstr_without_crud(&ident, date);
304
	}
305
	return ident.buf;
306
}
307

308 309
const char *fmt_name(const char *name, const char *email)
{
310
	return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
311 312
}

313
const char *git_author_info(int flag)
314
{
315
	return fmt_ident(getenv("GIT_AUTHOR_NAME"),
316
			 getenv("GIT_AUTHOR_EMAIL"),
317
			 getenv("GIT_AUTHOR_DATE"),
318
			 flag);
319 320
}

321
const char *git_committer_info(int flag)
322
{
323 324 325 326
	if (getenv("GIT_COMMITTER_NAME"))
		user_ident_explicitly_given |= IDENT_NAME_GIVEN;
	if (getenv("GIT_COMMITTER_EMAIL"))
		user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
327
	return fmt_ident(getenv("GIT_COMMITTER_NAME"),
328
			 getenv("GIT_COMMITTER_EMAIL"),
329
			 getenv("GIT_COMMITTER_DATE"),
330
			 flag);
331
}
332 333 334 335 336 337 338 339 340

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
}
341 342 343 344 345 346

int git_ident_config(const char *var, const char *value, void *data)
{
	if (!strcmp(var, "user.name")) {
		if (!value)
			return config_error_nonbool(var);
347 348
		strbuf_reset(&git_default_name);
		strbuf_addstr(&git_default_name, value);
349 350 351 352 353 354 355
		user_ident_explicitly_given |= IDENT_NAME_GIVEN;
		return 0;
	}

	if (!strcmp(var, "user.email")) {
		if (!value)
			return config_error_nonbool(var);
356 357
		strbuf_reset(&git_default_email);
		strbuf_addstr(&git_default_email, value);
358 359 360 361 362 363
		user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
		return 0;
	}

	return 0;
}