format-patch: use default email for generating message ids
[git.git] / ident.c
blobacb3a0843f40a10a730a80e7fcbf896660184a42
1 /*
2 * ident.c
4 * create git identifier lines of the form "name <email> date"
6 * Copyright (C) 2005 Linus Torvalds
7 */
8 #include "cache.h"
10 #define MAX_GITNAME (1000)
11 static char git_default_name[MAX_GITNAME];
12 static char git_default_email[MAX_GITNAME];
13 static char git_default_date[50];
14 int user_ident_explicitly_given;
16 #ifdef NO_GECOS_IN_PWENT
17 #define get_gecos(ignored) "&"
18 #else
19 #define get_gecos(struct_passwd) ((struct_passwd)->pw_gecos)
20 #endif
22 static void copy_gecos(const struct passwd *w, char *name, size_t sz)
24 char *src, *dst;
25 size_t len, nlen;
27 nlen = strlen(w->pw_name);
29 /* Traditionally GECOS field had office phone numbers etc, separated
30 * with commas. Also & stands for capitalized form of the login name.
33 for (len = 0, dst = name, src = get_gecos(w); len < sz; src++) {
34 int ch = *src;
35 if (ch != '&') {
36 *dst++ = ch;
37 if (ch == 0 || ch == ',')
38 break;
39 len++;
40 continue;
42 if (len + nlen < sz) {
43 /* Sorry, Mr. McDonald... */
44 *dst++ = toupper(*w->pw_name);
45 memcpy(dst, w->pw_name + 1, nlen - 1);
46 dst += nlen - 1;
47 len += nlen;
50 if (len < sz)
51 name[len] = 0;
52 else
53 die("Your parents must have hated you!");
57 static int add_mailname_host(char *buf, size_t len)
59 FILE *mailname;
61 mailname = fopen("/etc/mailname", "r");
62 if (!mailname) {
63 if (errno != ENOENT)
64 warning("cannot open /etc/mailname: %s",
65 strerror(errno));
66 return -1;
68 if (!fgets(buf, len, mailname)) {
69 if (ferror(mailname))
70 warning("cannot read /etc/mailname: %s",
71 strerror(errno));
72 fclose(mailname);
73 return -1;
75 /* success! */
76 fclose(mailname);
78 len = strlen(buf);
79 if (len && buf[len-1] == '\n')
80 buf[len-1] = '\0';
81 return 0;
84 static void add_domainname(char *buf, size_t len)
86 struct hostent *he;
87 size_t namelen;
88 const char *domainname;
90 if (gethostname(buf, len)) {
91 warning("cannot get host name: %s", strerror(errno));
92 strlcpy(buf, "(none)", len);
93 return;
95 namelen = strlen(buf);
96 if (memchr(buf, '.', namelen))
97 return;
99 he = gethostbyname(buf);
100 buf[namelen++] = '.';
101 buf += namelen;
102 len -= namelen;
103 if (he && (domainname = strchr(he->h_name, '.')))
104 strlcpy(buf, domainname + 1, len);
105 else
106 strlcpy(buf, "(none)", len);
109 static void copy_email(const struct passwd *pw)
112 * Make up a fake email address
113 * (name + '@' + hostname [+ '.' + domainname])
115 size_t len = strlen(pw->pw_name);
116 if (len > sizeof(git_default_email)/2)
117 die("Your sysadmin must hate you!");
118 memcpy(git_default_email, pw->pw_name, len);
119 git_default_email[len++] = '@';
121 if (!add_mailname_host(git_default_email + len,
122 sizeof(git_default_email) - len))
123 return; /* read from "/etc/mailname" (Debian) */
124 add_domainname(git_default_email + len,
125 sizeof(git_default_email) - len);
128 const char *ident_default_name(void)
130 if (!git_default_name[0]) {
131 struct passwd *pw = getpwuid(getuid());
132 if (!pw)
133 die("You don't exist. Go away!");
134 copy_gecos(pw, git_default_name, sizeof(git_default_name));
136 return git_default_name;
139 const char *ident_default_email(void)
141 if (!git_default_email[0]) {
142 const char *email = getenv("EMAIL");
144 if (email && email[0]) {
145 strlcpy(git_default_email, email,
146 sizeof(git_default_email));
147 user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
148 } else {
149 struct passwd *pw = getpwuid(getuid());
150 if (!pw)
151 die("You don't exist. Go away!");
152 copy_email(pw);
155 return git_default_email;
158 const char *ident_default_date(void)
160 if (!git_default_date[0])
161 datestamp(git_default_date, sizeof(git_default_date));
162 return git_default_date;
165 static int add_raw(char *buf, size_t size, int offset, const char *str)
167 size_t len = strlen(str);
168 if (offset + len > size)
169 return size;
170 memcpy(buf + offset, str, len);
171 return offset + len;
174 static int crud(unsigned char c)
176 return c <= 32 ||
177 c == '.' ||
178 c == ',' ||
179 c == ':' ||
180 c == ';' ||
181 c == '<' ||
182 c == '>' ||
183 c == '"' ||
184 c == '\\' ||
185 c == '\'';
189 * Copy over a string to the destination, but avoid special
190 * characters ('\n', '<' and '>') and remove crud at the end
192 static int copy(char *buf, size_t size, int offset, const char *src)
194 size_t i, len;
195 unsigned char c;
197 /* Remove crud from the beginning.. */
198 while ((c = *src) != 0) {
199 if (!crud(c))
200 break;
201 src++;
204 /* Remove crud from the end.. */
205 len = strlen(src);
206 while (len > 0) {
207 c = src[len-1];
208 if (!crud(c))
209 break;
210 --len;
214 * Copy the rest to the buffer, but avoid the special
215 * characters '\n' '<' and '>' that act as delimiters on
216 * an identification line
218 for (i = 0; i < len; i++) {
219 c = *src++;
220 switch (c) {
221 case '\n': case '<': case '>':
222 continue;
224 if (offset >= size)
225 return size;
226 buf[offset++] = c;
228 return offset;
232 * Reverse of fmt_ident(); given an ident line, split the fields
233 * to allow the caller to parse it.
234 * Signal a success by returning 0, but date/tz fields of the result
235 * can still be NULL if the input line only has the name/email part
236 * (e.g. reading from a reflog entry).
238 int split_ident_line(struct ident_split *split, const char *line, int len)
240 const char *cp;
241 size_t span;
242 int status = -1;
244 memset(split, 0, sizeof(*split));
246 split->name_begin = line;
247 for (cp = line; *cp && cp < line + len; cp++)
248 if (*cp == '<') {
249 split->mail_begin = cp + 1;
250 break;
252 if (!split->mail_begin)
253 return status;
255 for (cp = split->mail_begin - 2; line < cp; cp--)
256 if (!isspace(*cp)) {
257 split->name_end = cp + 1;
258 break;
260 if (!split->name_end)
261 return status;
263 for (cp = split->mail_begin; cp < line + len; cp++)
264 if (*cp == '>') {
265 split->mail_end = cp;
266 break;
268 if (!split->mail_end)
269 return status;
271 for (cp = split->mail_end + 1; cp < line + len && isspace(*cp); cp++)
273 if (line + len <= cp)
274 goto person_only;
275 split->date_begin = cp;
276 span = strspn(cp, "0123456789");
277 if (!span)
278 goto person_only;
279 split->date_end = split->date_begin + span;
280 for (cp = split->date_end; cp < line + len && isspace(*cp); cp++)
282 if (line + len <= cp || (*cp != '+' && *cp != '-'))
283 goto person_only;
284 split->tz_begin = cp;
285 span = strspn(cp + 1, "0123456789");
286 if (!span)
287 goto person_only;
288 split->tz_end = split->tz_begin + 1 + span;
289 return 0;
291 person_only:
292 split->date_begin = NULL;
293 split->date_end = NULL;
294 split->tz_begin = NULL;
295 split->tz_end = NULL;
296 return 0;
299 static const char *env_hint =
300 "\n"
301 "*** Please tell me who you are.\n"
302 "\n"
303 "Run\n"
304 "\n"
305 " git config --global user.email \"you@example.com\"\n"
306 " git config --global user.name \"Your Name\"\n"
307 "\n"
308 "to set your account\'s default identity.\n"
309 "Omit --global to set the identity only in this repository.\n"
310 "\n";
312 const char *fmt_ident(const char *name, const char *email,
313 const char *date_str, int flag)
315 static char buffer[1000];
316 char date[50];
317 int i;
318 int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME);
319 int warn_on_no_name = (flag & IDENT_WARN_ON_NO_NAME);
320 int name_addr_only = (flag & IDENT_NO_DATE);
322 if (!name)
323 name = ident_default_name();
324 if (!email)
325 email = ident_default_email();
327 if (!*name) {
328 struct passwd *pw;
330 if ((warn_on_no_name || error_on_no_name) &&
331 name == git_default_name && env_hint) {
332 fputs(env_hint, stderr);
333 env_hint = NULL; /* warn only once */
335 if (error_on_no_name)
336 die("empty ident %s <%s> not allowed", name, email);
337 pw = getpwuid(getuid());
338 if (!pw)
339 die("You don't exist. Go away!");
340 strlcpy(git_default_name, pw->pw_name,
341 sizeof(git_default_name));
342 name = git_default_name;
345 strcpy(date, ident_default_date());
346 if (!name_addr_only && date_str && date_str[0]) {
347 if (parse_date(date_str, date, sizeof(date)) < 0)
348 die("invalid date format: %s", date_str);
351 i = copy(buffer, sizeof(buffer), 0, name);
352 i = add_raw(buffer, sizeof(buffer), i, " <");
353 i = copy(buffer, sizeof(buffer), i, email);
354 if (!name_addr_only) {
355 i = add_raw(buffer, sizeof(buffer), i, "> ");
356 i = copy(buffer, sizeof(buffer), i, date);
357 } else {
358 i = add_raw(buffer, sizeof(buffer), i, ">");
360 if (i >= sizeof(buffer))
361 die("Impossibly long personal identifier");
362 buffer[i] = 0;
363 return buffer;
366 const char *fmt_name(const char *name, const char *email)
368 return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
371 const char *git_author_info(int flag)
373 return fmt_ident(getenv("GIT_AUTHOR_NAME"),
374 getenv("GIT_AUTHOR_EMAIL"),
375 getenv("GIT_AUTHOR_DATE"),
376 flag);
379 const char *git_committer_info(int flag)
381 if (getenv("GIT_COMMITTER_NAME"))
382 user_ident_explicitly_given |= IDENT_NAME_GIVEN;
383 if (getenv("GIT_COMMITTER_EMAIL"))
384 user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
385 return fmt_ident(getenv("GIT_COMMITTER_NAME"),
386 getenv("GIT_COMMITTER_EMAIL"),
387 getenv("GIT_COMMITTER_DATE"),
388 flag);
391 int user_ident_sufficiently_given(void)
393 #ifndef WINDOWS
394 return (user_ident_explicitly_given & IDENT_MAIL_GIVEN);
395 #else
396 return (user_ident_explicitly_given == IDENT_ALL_GIVEN);
397 #endif
400 int git_ident_config(const char *var, const char *value, void *data)
402 if (!strcmp(var, "user.name")) {
403 if (!value)
404 return config_error_nonbool(var);
405 strlcpy(git_default_name, value, sizeof(git_default_name));
406 user_ident_explicitly_given |= IDENT_NAME_GIVEN;
407 return 0;
410 if (!strcmp(var, "user.email")) {
411 if (!value)
412 return config_error_nonbool(var);
413 strlcpy(git_default_email, value, sizeof(git_default_email));
414 user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
415 return 0;
418 return 0;