ident: don't write fallback username into git_default_name
[git/git.git] / ident.c
CommitLineData
6aa33f40
LT
1/*
2 * ident.c
3 *
4 * create git identifier lines of the form "name <email> date"
5 *
6 * Copyright (C) 2005 Linus Torvalds
7 */
8#include "cache.h"
9
2d4b4fce
JK
10#define MAX_GITNAME (1000)
11static char git_default_name[MAX_GITNAME];
12static char git_default_email[MAX_GITNAME];
e1b10391 13static char git_default_date[50];
2d4b4fce 14int user_ident_explicitly_given;
6aa33f40 15
590e081d
RG
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
21
b0732115 22static void copy_gecos(const struct passwd *w, char *name, size_t sz)
e9bacb4f
JH
23{
24 char *src, *dst;
b0732115 25 size_t len, nlen;
e9bacb4f
JH
26
27 nlen = strlen(w->pw_name);
28
29 /* Traditionally GECOS field had office phone numbers etc, separated
30 * with commas. Also & stands for capitalized form of the login name.
31 */
32
590e081d 33 for (len = 0, dst = name, src = get_gecos(w); len < sz; src++) {
e9bacb4f
JH
34 int ch = *src;
35 if (ch != '&') {
36 *dst++ = ch;
37 if (ch == 0 || ch == ',')
38 break;
39 len++;
40 continue;
41 }
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;
c0336ff2 47 len += nlen;
e9bacb4f
JH
48 }
49 }
50 if (len < sz)
51 name[len] = 0;
52 else
53 die("Your parents must have hated you!");
54
55}
56
8a55caa8
JN
57static int add_mailname_host(char *buf, size_t len)
58{
59 FILE *mailname;
60
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;
67 }
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;
74 }
75 /* success! */
76 fclose(mailname);
132f4b6c
JK
77
78 len = strlen(buf);
79 if (len && buf[len-1] == '\n')
80 buf[len-1] = '\0';
8a55caa8
JN
81 return 0;
82}
83
84static void add_domainname(char *buf, size_t len)
85{
86 struct hostent *he;
87 size_t namelen;
88 const char *domainname;
89
90 if (gethostname(buf, len)) {
91 warning("cannot get host name: %s", strerror(errno));
92 strlcpy(buf, "(none)", len);
93 return;
94 }
95 namelen = strlen(buf);
96 if (memchr(buf, '.', namelen))
97 return;
98
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);
107}
108
0b952a98 109static void copy_email(const struct passwd *pw)
6aa33f40 110{
01754769
JH
111 /*
112 * Make up a fake email address
113 * (name + '@' + hostname [+ '.' + domainname])
114 */
b0732115 115 size_t len = strlen(pw->pw_name);
e1b10391 116 if (len > sizeof(git_default_email)/2)
7a868a84 117 die("Your sysadmin must hate you!");
e1b10391
LT
118 memcpy(git_default_email, pw->pw_name, len);
119 git_default_email[len++] = '@';
8a55caa8
JN
120
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);
01754769
JH
126}
127
bcb2b004 128const char *ident_default_name(void)
01754769 129{
bcb2b004
JK
130 if (!git_default_name[0]) {
131 struct passwd *pw = getpwuid(getuid());
01754769
JH
132 if (!pw)
133 die("You don't exist. Go away!");
134 copy_gecos(pw, git_default_name, sizeof(git_default_name));
135 }
bcb2b004
JK
136 return git_default_name;
137}
01754769 138
bcb2b004
JK
139const char *ident_default_email(void)
140{
141 if (!git_default_email[0]) {
46f74f00
MK
142 const char *email = getenv("EMAIL");
143
99178c83 144 if (email && email[0]) {
46f74f00
MK
145 strlcpy(git_default_email, email,
146 sizeof(git_default_email));
99178c83
JH
147 user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
148 } else {
bcb2b004 149 struct passwd *pw = getpwuid(getuid());
46f74f00
MK
150 if (!pw)
151 die("You don't exist. Go away!");
152 copy_email(pw);
153 }
01754769 154 }
bcb2b004
JK
155 return git_default_email;
156}
01754769 157
bcb2b004
JK
158const char *ident_default_date(void)
159{
01754769
JH
160 if (!git_default_date[0])
161 datestamp(git_default_date, sizeof(git_default_date));
bcb2b004 162 return git_default_date;
6aa33f40
LT
163}
164
b0732115 165static int add_raw(char *buf, size_t size, int offset, const char *str)
6aa33f40 166{
b0732115 167 size_t len = strlen(str);
6aa33f40
LT
168 if (offset + len > size)
169 return size;
170 memcpy(buf + offset, str, len);
171 return offset + len;
172}
173
174static int crud(unsigned char c)
175{
f64c81d4
AR
176 return c <= 32 ||
177 c == '.' ||
178 c == ',' ||
179 c == ':' ||
180 c == ';' ||
181 c == '<' ||
182 c == '>' ||
183 c == '"' ||
d404bf02 184 c == '\\' ||
f64c81d4 185 c == '\'';
6aa33f40
LT
186}
187
188/*
189 * Copy over a string to the destination, but avoid special
190 * characters ('\n', '<' and '>') and remove crud at the end
191 */
b0732115 192static int copy(char *buf, size_t size, int offset, const char *src)
6aa33f40 193{
b0732115 194 size_t i, len;
6aa33f40
LT
195 unsigned char c;
196
197 /* Remove crud from the beginning.. */
198 while ((c = *src) != 0) {
199 if (!crud(c))
200 break;
201 src++;
202 }
203
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;
211 }
212
213 /*
214 * Copy the rest to the buffer, but avoid the special
82f9d58a 215 * characters '\n' '<' and '>' that act as delimiters on
790296fd 216 * an identification line
6aa33f40
LT
217 */
218 for (i = 0; i < len; i++) {
219 c = *src++;
220 switch (c) {
221 case '\n': case '<': case '>':
222 continue;
223 }
224 if (offset >= size)
225 return size;
226 buf[offset++] = c;
227 }
228 return offset;
229}
230
4b340cfa
JH
231/*
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).
237 */
238int split_ident_line(struct ident_split *split, const char *line, int len)
239{
240 const char *cp;
241 size_t span;
242 int status = -1;
243
244 memset(split, 0, sizeof(*split));
245
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;
251 }
252 if (!split->mail_begin)
253 return status;
254
255 for (cp = split->mail_begin - 2; line < cp; cp--)
256 if (!isspace(*cp)) {
257 split->name_end = cp + 1;
258 break;
259 }
260 if (!split->name_end)
261 return status;
262
263 for (cp = split->mail_begin; cp < line + len; cp++)
264 if (*cp == '>') {
265 split->mail_end = cp;
266 break;
267 }
268 if (!split->mail_end)
269 return status;
270
271 for (cp = split->mail_end + 1; cp < line + len && isspace(*cp); cp++)
272 ;
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++)
281 ;
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;
290
291person_only:
292 split->date_begin = NULL;
293 split->date_end = NULL;
294 split->tz_begin = NULL;
295 split->tz_end = NULL;
296 return 0;
297}
298
749be728 299static const char *env_hint =
d5cc2de9 300"\n"
6c293d40 301"*** Please tell me who you are.\n"
d5cc2de9
HWN
302"\n"
303"Run\n"
304"\n"
8e7425da 305" git config --global user.email \"you@example.com\"\n"
180787c4 306" git config --global user.name \"Your Name\"\n"
d5cc2de9 307"\n"
180787c4
SP
308"to set your account\'s default identity.\n"
309"Omit --global to set the identity only in this repository.\n"
d5cc2de9 310"\n";
749be728 311
774751a8
JH
312const char *fmt_ident(const char *name, const char *email,
313 const char *date_str, int flag)
6aa33f40
LT
314{
315 static char buffer[1000];
316 char date[50];
317 int i;
774751a8 318 int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME);
774751a8 319 int name_addr_only = (flag & IDENT_NO_DATE);
6aa33f40 320
bcb2b004
JK
321 if (!name)
322 name = ident_default_name();
323 if (!email)
324 email = ident_default_email();
dfdd309e 325
749be728 326 if (!*name) {
cb280e10
JH
327 struct passwd *pw;
328
b9f0ac17
JK
329 if (error_on_no_name) {
330 if (name == git_default_name)
331 fputs(env_hint, stderr);
749be728 332 die("empty ident %s <%s> not allowed", name, email);
b9f0ac17 333 }
cb280e10
JH
334 pw = getpwuid(getuid());
335 if (!pw)
336 die("You don't exist. Go away!");
060d4bb3 337 name = pw->pw_name;
749be728 338 }
dfdd309e 339
bcb2b004 340 strcpy(date, ident_default_date());
4579bb41
JK
341 if (!name_addr_only && date_str && date_str[0]) {
342 if (parse_date(date_str, date, sizeof(date)) < 0)
343 die("invalid date format: %s", date_str);
344 }
6aa33f40
LT
345
346 i = copy(buffer, sizeof(buffer), 0, name);
347 i = add_raw(buffer, sizeof(buffer), i, " <");
348 i = copy(buffer, sizeof(buffer), i, email);
d9ccfe77
JH
349 if (!name_addr_only) {
350 i = add_raw(buffer, sizeof(buffer), i, "> ");
351 i = copy(buffer, sizeof(buffer), i, date);
352 } else {
353 i = add_raw(buffer, sizeof(buffer), i, ">");
354 }
6aa33f40
LT
355 if (i >= sizeof(buffer))
356 die("Impossibly long personal identifier");
357 buffer[i] = 0;
358 return buffer;
359}
d289d136 360
d9ccfe77
JH
361const char *fmt_name(const char *name, const char *email)
362{
774751a8 363 return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
d9ccfe77
JH
364}
365
774751a8 366const char *git_author_info(int flag)
d289d136 367{
798123af 368 return fmt_ident(getenv("GIT_AUTHOR_NAME"),
c7d77dab 369 getenv("GIT_AUTHOR_EMAIL"),
749be728 370 getenv("GIT_AUTHOR_DATE"),
774751a8 371 flag);
d289d136
EB
372}
373
774751a8 374const char *git_committer_info(int flag)
d289d136 375{
91c38a21
JH
376 if (getenv("GIT_COMMITTER_NAME"))
377 user_ident_explicitly_given |= IDENT_NAME_GIVEN;
378 if (getenv("GIT_COMMITTER_EMAIL"))
379 user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
798123af 380 return fmt_ident(getenv("GIT_COMMITTER_NAME"),
c7d77dab 381 getenv("GIT_COMMITTER_EMAIL"),
749be728 382 getenv("GIT_COMMITTER_DATE"),
774751a8 383 flag);
d289d136 384}
5aeb3a3a
JH
385
386int user_ident_sufficiently_given(void)
387{
388#ifndef WINDOWS
389 return (user_ident_explicitly_given & IDENT_MAIL_GIVEN);
390#else
391 return (user_ident_explicitly_given == IDENT_ALL_GIVEN);
392#endif
393}
9597921b
JK
394
395int git_ident_config(const char *var, const char *value, void *data)
396{
397 if (!strcmp(var, "user.name")) {
398 if (!value)
399 return config_error_nonbool(var);
400 strlcpy(git_default_name, value, sizeof(git_default_name));
401 user_ident_explicitly_given |= IDENT_NAME_GIVEN;
402 return 0;
403 }
404
405 if (!strcmp(var, "user.email")) {
406 if (!value)
407 return config_error_nonbool(var);
408 strlcpy(git_default_email, value, sizeof(git_default_email));
409 user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
410 return 0;
411 }
412
413 return 0;
414}