Fix crash when reading the empty tree
[gitweb.git] / ident.c
diff --git a/ident.c b/ident.c
index 0df2d979242bd6ee246bd280ffebdf8299408a8c..7c81fe8d8bfc4c76324277dc7daa39c76ed12cd3 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -8,12 +8,43 @@
 #include "cache.h"
 
 #include <pwd.h>
-#include <time.h>
-#include <ctype.h>
+#include <netdb.h>
 
-static char real_email[1000];
-static char real_name[1000];
-static char real_date[50];
+static char git_default_date[50];
+
+static void copy_gecos(struct passwd *w, char *name, int sz)
+{
+       char *src, *dst;
+       int len, nlen;
+
+       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!");
+
+}
 
 int setup_ident(void)
 {
@@ -24,27 +55,29 @@ int setup_ident(void)
                die("You don't exist. Go away!");
 
        /* Get the name ("gecos") */
-       len = strlen(pw->pw_gecos);
-       if (len >= sizeof(real_name))
-               die("Your parents must have hated you!");
-       memcpy(real_name, pw->pw_gecos, len+1);
+       copy_gecos(pw, git_default_name, sizeof(git_default_name));
 
        /* Make up a fake email address (name + '@' + hostname [+ '.' + domainname]) */
        len = strlen(pw->pw_name);
-       if (len > sizeof(real_email)/2)
+       if (len > sizeof(git_default_email)/2)
                die("Your sysadmin must hate you!");
-       memcpy(real_email, pw->pw_name, len);
-       real_email[len++] = '@';
-       gethostname(real_email + len, sizeof(real_email) - len);
-#ifndef NO_GETDOMAINNAME
-       if (!strchr(real_email+len, '.')) {
-               len = strlen(real_email);
-               real_email[len++] = '.';
-               getdomainname(real_email+len, sizeof(real_email)-len);
+       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, '.')) {
+               struct hostent *he = gethostbyname(git_default_email + len);
+               char *domainname;
+
+               len = strlen(git_default_email);
+               git_default_email[len++] = '.';
+               if (he && (domainname = strchr(he->h_name, '.')))
+                       strncpy(git_default_email + len, domainname + 1, sizeof(git_default_email) - len);
+               else
+                       strncpy(git_default_email + len, "(none)", sizeof(git_default_email) - len);
+               git_default_email[sizeof(git_default_email) - 1] = 0;
        }
-#endif
        /* And set the default date */
-       datestamp(real_date, sizeof(real_date));
+       datestamp(git_default_date, sizeof(git_default_date));
        return 0;
 }
 
@@ -107,7 +140,7 @@ static int copy(char *buf, int size, int offset, const char *src)
 
        /*
         * Copy the rest to the buffer, but avoid the special
-        * characters '\n' '<' and '>' that act as delimeters on
+        * characters '\n' '<' and '>' that act as delimiters on
         * a identification line
         */
        for (i = 0; i < len; i++) {
@@ -123,17 +156,38 @@ static int copy(char *buf, int size, int offset, const char *src)
        return offset;
 }
 
-char *get_ident(const char *name, const char *email, const char *date_str)
+static const char au_env[] = "GIT_AUTHOR_NAME";
+static const char co_env[] = "GIT_COMMITTER_NAME";
+static const char *env_hint =
+"\n*** Environment problem:\n"
+"*** Your name cannot be determined from your system services (gecos).\n"
+"*** You would need to set %s and %s\n"
+"*** environment variables; otherwise you won't be able to perform\n"
+"*** certain operations because of \"empty ident\" errors.\n"
+"*** Alternatively, you can use user.name configuration variable.\n\n";
+
+static const char *get_ident(const char *name, const char *email,
+                            const char *date_str, int error_on_no_name)
 {
        static char buffer[1000];
        char date[50];
        int i;
 
        if (!name)
-               name = real_name;
+               name = git_default_name;
        if (!email)
-               email = real_email;
-       strcpy(date, real_date);
+               email = git_default_email;
+
+       if (!*name) {
+               if (name == git_default_name && env_hint) {
+                       fprintf(stderr, env_hint, au_env, co_env);
+                       env_hint = NULL; /* warn only once, for "git-var -l" */
+               }
+               if (error_on_no_name)
+                       die("empty ident %s <%s> not allowed", name, email);
+       }
+
+       strcpy(date, git_default_date);
        if (date_str)
                parse_date(date_str, date, sizeof(date));
 
@@ -148,12 +202,18 @@ char *get_ident(const char *name, const char *email, const char *date_str)
        return buffer;
 }
 
-char *git_author_info(void)
+const char *git_author_info(int error_on_no_name)
 {
-       return get_ident(getenv("GIT_AUTHOR_NAME"), getenv("GIT_AUTHOR_EMAIL"), getenv("GIT_AUTHOR_DATE"));
+       return get_ident(getenv("GIT_AUTHOR_NAME"),
+                        getenv("GIT_AUTHOR_EMAIL"),
+                        getenv("GIT_AUTHOR_DATE"),
+                        error_on_no_name);
 }
 
-char *git_committer_info(void)
+const char *git_committer_info(int error_on_no_name)
 {
-       return get_ident(getenv("GIT_COMMITTER_NAME"), getenv("GIT_COMMITTER_EMAIL"), getenv("GIT_COMMITTER_DATE"));
+       return get_ident(getenv("GIT_COMMITTER_NAME"),
+                        getenv("GIT_COMMITTER_EMAIL"),
+                        getenv("GIT_COMMITTER_DATE"),
+                        error_on_no_name);
 }