ident.c 5.8 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
static char git_default_date[50];
11

12
static void copy_gecos(const struct passwd *w, char *name, size_t sz)
13 14
{
	char *src, *dst;
15
	size_t len, nlen;
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

	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 = w->pw_gecos; 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;
		}
	}
	if (len < sz)
		name[len] = 0;
	else
		die("Your parents must have hated you!");

}

46
static void copy_email(const struct passwd *pw)
47
{
48 49 50 51
	/*
	 * Make up a fake email address
	 * (name + '@' + hostname [+ '.' + domainname])
	 */
52
	size_t len = strlen(pw->pw_name);
53
	if (len > sizeof(git_default_email)/2)
54
		die("Your sysadmin must hate you!");
55 56 57 58
	memcpy(git_default_email, pw->pw_name, len);
	git_default_email[len++] = '@';
	gethostname(git_default_email + len, sizeof(git_default_email) - len);
	if (!strchr(git_default_email+len, '.')) {
59 60 61
		struct hostent *he = gethostbyname(git_default_email + len);
		char *domainname;

62 63
		len = strlen(git_default_email);
		git_default_email[len++] = '.';
64
		if (he && (domainname = strchr(he->h_name, '.')))
65 66
			strlcpy(git_default_email + len, domainname + 1,
				sizeof(git_default_email) - len);
67
		else
68 69
			strlcpy(git_default_email + len, "(none)",
				sizeof(git_default_email) - len);
70
	}
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
}

static void setup_ident(void)
{
	struct passwd *pw = NULL;

	/* Get the name ("gecos") */
	if (!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_email[0]) {
M
Matt Kraai 已提交
86 87 88 89 90 91 92 93 94 95 96 97
		const char *email = getenv("EMAIL");

		if (email && email[0])
			strlcpy(git_default_email, email,
				sizeof(git_default_email));
		else {
			if (!pw)
				pw = getpwuid(getuid());
			if (!pw)
				die("You don't exist. Go away!");
			copy_email(pw);
		}
98 99
	}

100
	/* And set the default date */
101 102
	if (!git_default_date[0])
		datestamp(git_default_date, sizeof(git_default_date));
103 104
}

105
static int add_raw(char *buf, size_t size, int offset, const char *str)
106
{
107
	size_t len = strlen(str);
108 109 110 111 112 113 114 115
	if (offset + len > size)
		return size;
	memcpy(buf + offset, str, len);
	return offset + len;
}

static int crud(unsigned char c)
{
A
Alex Riesen 已提交
116 117 118 119 120 121 122 123 124
	return  c <= 32  ||
		c == '.' ||
		c == ',' ||
		c == ':' ||
		c == ';' ||
		c == '<' ||
		c == '>' ||
		c == '"' ||
		c == '\'';
125 126 127 128 129 130
}

/*
 * Copy over a string to the destination, but avoid special
 * characters ('\n', '<' and '>') and remove crud at the end
 */
131
static int copy(char *buf, size_t size, int offset, const char *src)
132
{
133
	size_t i, len;
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
	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 已提交
154
	 * characters '\n' '<' and '>' that act as delimiters on
155
	 * an identification line
156 157 158 159 160 161 162 163 164 165 166 167 168 169
	 */
	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;
}

170 171 172
static const char au_env[] = "GIT_AUTHOR_NAME";
static const char co_env[] = "GIT_COMMITTER_NAME";
static const char *env_hint =
173
"\n"
174
"*** Your name cannot be determined from your system services (gecos).\n"
175 176 177
"\n"
"Run\n"
"\n"
178
"  git config --global user.email \"you@example.com\"\n"
179
"  git config --global user.name \"Your Name\"\n"
180
"\n"
181 182
"to set your account\'s default identity.\n"
"Omit --global to set the identity only in this repository.\n"
183
"\n";
184

185 186
const char *fmt_ident(const char *name, const char *email,
		      const char *date_str, int flag)
187 188 189 190
{
	static char buffer[1000];
	char date[50];
	int i;
191 192 193
	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);
194

195
	setup_ident();
196
	if (!name)
197
		name = git_default_name;
198
	if (!email)
199
		email = git_default_email;
200

201
	if (!*name) {
202 203
		struct passwd *pw;

204
		if ((warn_on_no_name || error_on_no_name) &&
205
		    name == git_default_name && env_hint) {
206 207 208
			fprintf(stderr, env_hint, au_env, co_env);
			env_hint = NULL; /* warn only once, for "git-var -l" */
		}
209
		if (error_on_no_name)
210
			die("empty ident %s <%s> not allowed", name, email);
211 212 213 214 215 216
		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;
217
	}
218

219
	strcpy(date, git_default_date);
220
	if (!name_addr_only && date_str)
221 222 223 224 225
		parse_date(date_str, date, sizeof(date));

	i = copy(buffer, sizeof(buffer), 0, name);
	i = add_raw(buffer, sizeof(buffer), i, " <");
	i = copy(buffer, sizeof(buffer), i, email);
226 227 228 229 230 231
	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, ">");
	}
232 233 234 235 236
	if (i >= sizeof(buffer))
		die("Impossibly long personal identifier");
	buffer[i] = 0;
	return buffer;
}
237

238 239
const char *fmt_name(const char *name, const char *email)
{
240
	return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
241 242
}

243
const char *git_author_info(int flag)
244
{
245
	return fmt_ident(getenv("GIT_AUTHOR_NAME"),
246
			 getenv("GIT_AUTHOR_EMAIL"),
247
			 getenv("GIT_AUTHOR_DATE"),
248
			 flag);
249 250
}

251
const char *git_committer_info(int flag)
252
{
253
	return fmt_ident(getenv("GIT_COMMITTER_NAME"),
254
			 getenv("GIT_COMMITTER_EMAIL"),
255
			 getenv("GIT_COMMITTER_DATE"),
256
			 flag);
257
}