]> Pileus Git - ~andy/git/blob - ident.c
move git_default_* variables to ident.c
[~andy/git] / ident.c
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
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;
15
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
22 static void copy_gecos(const struct passwd *w, char *name, size_t sz)
23 {
24         char *src, *dst;
25         size_t len, nlen;
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
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;
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;
47                         len += nlen;
48                 }
49         }
50         if (len < sz)
51                 name[len] = 0;
52         else
53                 die("Your parents must have hated you!");
54
55 }
56
57 static 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);
77         return 0;
78 }
79
80 static void add_domainname(char *buf, size_t len)
81 {
82         struct hostent *he;
83         size_t namelen;
84         const char *domainname;
85
86         if (gethostname(buf, len)) {
87                 warning("cannot get host name: %s", strerror(errno));
88                 strlcpy(buf, "(none)", len);
89                 return;
90         }
91         namelen = strlen(buf);
92         if (memchr(buf, '.', namelen))
93                 return;
94
95         he = gethostbyname(buf);
96         buf[namelen++] = '.';
97         buf += namelen;
98         len -= namelen;
99         if (he && (domainname = strchr(he->h_name, '.')))
100                 strlcpy(buf, domainname + 1, len);
101         else
102                 strlcpy(buf, "(none)", len);
103 }
104
105 static void copy_email(const struct passwd *pw)
106 {
107         /*
108          * Make up a fake email address
109          * (name + '@' + hostname [+ '.' + domainname])
110          */
111         size_t len = strlen(pw->pw_name);
112         if (len > sizeof(git_default_email)/2)
113                 die("Your sysadmin must hate you!");
114         memcpy(git_default_email, pw->pw_name, len);
115         git_default_email[len++] = '@';
116
117         if (!add_mailname_host(git_default_email + len,
118                                 sizeof(git_default_email) - len))
119                 return; /* read from "/etc/mailname" (Debian) */
120         add_domainname(git_default_email + len,
121                         sizeof(git_default_email) - len);
122 }
123
124 const char *ident_default_name(void)
125 {
126         if (!git_default_name[0]) {
127                 struct passwd *pw = getpwuid(getuid());
128                 if (!pw)
129                         die("You don't exist. Go away!");
130                 copy_gecos(pw, git_default_name, sizeof(git_default_name));
131         }
132         return git_default_name;
133 }
134
135 const char *ident_default_email(void)
136 {
137         if (!git_default_email[0]) {
138                 const char *email = getenv("EMAIL");
139
140                 if (email && email[0]) {
141                         strlcpy(git_default_email, email,
142                                 sizeof(git_default_email));
143                         user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
144                 } else {
145                         struct passwd *pw = getpwuid(getuid());
146                         if (!pw)
147                                 die("You don't exist. Go away!");
148                         copy_email(pw);
149                 }
150         }
151         return git_default_email;
152 }
153
154 const char *ident_default_date(void)
155 {
156         if (!git_default_date[0])
157                 datestamp(git_default_date, sizeof(git_default_date));
158         return git_default_date;
159 }
160
161 static int add_raw(char *buf, size_t size, int offset, const char *str)
162 {
163         size_t len = strlen(str);
164         if (offset + len > size)
165                 return size;
166         memcpy(buf + offset, str, len);
167         return offset + len;
168 }
169
170 static int crud(unsigned char c)
171 {
172         return  c <= 32  ||
173                 c == '.' ||
174                 c == ',' ||
175                 c == ':' ||
176                 c == ';' ||
177                 c == '<' ||
178                 c == '>' ||
179                 c == '"' ||
180                 c == '\\' ||
181                 c == '\'';
182 }
183
184 /*
185  * Copy over a string to the destination, but avoid special
186  * characters ('\n', '<' and '>') and remove crud at the end
187  */
188 static int copy(char *buf, size_t size, int offset, const char *src)
189 {
190         size_t i, len;
191         unsigned char c;
192
193         /* Remove crud from the beginning.. */
194         while ((c = *src) != 0) {
195                 if (!crud(c))
196                         break;
197                 src++;
198         }
199
200         /* Remove crud from the end.. */
201         len = strlen(src);
202         while (len > 0) {
203                 c = src[len-1];
204                 if (!crud(c))
205                         break;
206                 --len;
207         }
208
209         /*
210          * Copy the rest to the buffer, but avoid the special
211          * characters '\n' '<' and '>' that act as delimiters on
212          * an identification line
213          */
214         for (i = 0; i < len; i++) {
215                 c = *src++;
216                 switch (c) {
217                 case '\n': case '<': case '>':
218                         continue;
219                 }
220                 if (offset >= size)
221                         return size;
222                 buf[offset++] = c;
223         }
224         return offset;
225 }
226
227 /*
228  * Reverse of fmt_ident(); given an ident line, split the fields
229  * to allow the caller to parse it.
230  * Signal a success by returning 0, but date/tz fields of the result
231  * can still be NULL if the input line only has the name/email part
232  * (e.g. reading from a reflog entry).
233  */
234 int split_ident_line(struct ident_split *split, const char *line, int len)
235 {
236         const char *cp;
237         size_t span;
238         int status = -1;
239
240         memset(split, 0, sizeof(*split));
241
242         split->name_begin = line;
243         for (cp = line; *cp && cp < line + len; cp++)
244                 if (*cp == '<') {
245                         split->mail_begin = cp + 1;
246                         break;
247                 }
248         if (!split->mail_begin)
249                 return status;
250
251         for (cp = split->mail_begin - 2; line < cp; cp--)
252                 if (!isspace(*cp)) {
253                         split->name_end = cp + 1;
254                         break;
255                 }
256         if (!split->name_end)
257                 return status;
258
259         for (cp = split->mail_begin; cp < line + len; cp++)
260                 if (*cp == '>') {
261                         split->mail_end = cp;
262                         break;
263                 }
264         if (!split->mail_end)
265                 return status;
266
267         for (cp = split->mail_end + 1; cp < line + len && isspace(*cp); cp++)
268                 ;
269         if (line + len <= cp)
270                 goto person_only;
271         split->date_begin = cp;
272         span = strspn(cp, "0123456789");
273         if (!span)
274                 goto person_only;
275         split->date_end = split->date_begin + span;
276         for (cp = split->date_end; cp < line + len && isspace(*cp); cp++)
277                 ;
278         if (line + len <= cp || (*cp != '+' && *cp != '-'))
279                 goto person_only;
280         split->tz_begin = cp;
281         span = strspn(cp + 1, "0123456789");
282         if (!span)
283                 goto person_only;
284         split->tz_end = split->tz_begin + 1 + span;
285         return 0;
286
287 person_only:
288         split->date_begin = NULL;
289         split->date_end = NULL;
290         split->tz_begin = NULL;
291         split->tz_end = NULL;
292         return 0;
293 }
294
295 static const char *env_hint =
296 "\n"
297 "*** Please tell me who you are.\n"
298 "\n"
299 "Run\n"
300 "\n"
301 "  git config --global user.email \"you@example.com\"\n"
302 "  git config --global user.name \"Your Name\"\n"
303 "\n"
304 "to set your account\'s default identity.\n"
305 "Omit --global to set the identity only in this repository.\n"
306 "\n";
307
308 const char *fmt_ident(const char *name, const char *email,
309                       const char *date_str, int flag)
310 {
311         static char buffer[1000];
312         char date[50];
313         int i;
314         int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME);
315         int warn_on_no_name = (flag & IDENT_WARN_ON_NO_NAME);
316         int name_addr_only = (flag & IDENT_NO_DATE);
317
318         if (!name)
319                 name = ident_default_name();
320         if (!email)
321                 email = ident_default_email();
322
323         if (!*name) {
324                 struct passwd *pw;
325
326                 if ((warn_on_no_name || error_on_no_name) &&
327                     name == git_default_name && env_hint) {
328                         fputs(env_hint, stderr);
329                         env_hint = NULL; /* warn only once */
330                 }
331                 if (error_on_no_name)
332                         die("empty ident %s <%s> not allowed", name, email);
333                 pw = getpwuid(getuid());
334                 if (!pw)
335                         die("You don't exist. Go away!");
336                 strlcpy(git_default_name, pw->pw_name,
337                         sizeof(git_default_name));
338                 name = git_default_name;
339         }
340
341         strcpy(date, ident_default_date());
342         if (!name_addr_only && date_str && date_str[0]) {
343                 if (parse_date(date_str, date, sizeof(date)) < 0)
344                         die("invalid date format: %s", date_str);
345         }
346
347         i = copy(buffer, sizeof(buffer), 0, name);
348         i = add_raw(buffer, sizeof(buffer), i, " <");
349         i = copy(buffer, sizeof(buffer), i, email);
350         if (!name_addr_only) {
351                 i = add_raw(buffer, sizeof(buffer), i,  "> ");
352                 i = copy(buffer, sizeof(buffer), i, date);
353         } else {
354                 i = add_raw(buffer, sizeof(buffer), i, ">");
355         }
356         if (i >= sizeof(buffer))
357                 die("Impossibly long personal identifier");
358         buffer[i] = 0;
359         return buffer;
360 }
361
362 const char *fmt_name(const char *name, const char *email)
363 {
364         return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
365 }
366
367 const char *git_author_info(int flag)
368 {
369         return fmt_ident(getenv("GIT_AUTHOR_NAME"),
370                          getenv("GIT_AUTHOR_EMAIL"),
371                          getenv("GIT_AUTHOR_DATE"),
372                          flag);
373 }
374
375 const char *git_committer_info(int flag)
376 {
377         if (getenv("GIT_COMMITTER_NAME"))
378                 user_ident_explicitly_given |= IDENT_NAME_GIVEN;
379         if (getenv("GIT_COMMITTER_EMAIL"))
380                 user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
381         return fmt_ident(getenv("GIT_COMMITTER_NAME"),
382                          getenv("GIT_COMMITTER_EMAIL"),
383                          getenv("GIT_COMMITTER_DATE"),
384                          flag);
385 }
386
387 int user_ident_sufficiently_given(void)
388 {
389 #ifndef WINDOWS
390         return (user_ident_explicitly_given & IDENT_MAIL_GIVEN);
391 #else
392         return (user_ident_explicitly_given == IDENT_ALL_GIVEN);
393 #endif
394 }
395
396 int git_ident_config(const char *var, const char *value, void *data)
397 {
398         if (!strcmp(var, "user.name")) {
399                 if (!value)
400                         return config_error_nonbool(var);
401                 strlcpy(git_default_name, value, sizeof(git_default_name));
402                 user_ident_explicitly_given |= IDENT_NAME_GIVEN;
403                 return 0;
404         }
405
406         if (!strcmp(var, "user.email")) {
407                 if (!value)
408                         return config_error_nonbool(var);
409                 strlcpy(git_default_email, value, sizeof(git_default_email));
410                 user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
411                 return 0;
412         }
413
414         return 0;
415 }