abspath.con commit cherry-pick: handle root commits with external strategies (1618073)
   1#include "cache.h"
   2
   3/*
   4 * Do not use this for inspecting *tracked* content.  When path is a
   5 * symlink to a directory, we do not want to say it is a directory when
   6 * dealing with tracked content in the working tree.
   7 */
   8int is_directory(const char *path)
   9{
  10        struct stat st;
  11        return (!stat(path, &st) && S_ISDIR(st.st_mode));
  12}
  13
  14/* We allow "recursive" symbolic links. Only within reason, though. */
  15#define MAXDEPTH 5
  16
  17/*
  18 * Use this to get the real path, i.e. resolve links. If you want an
  19 * absolute path but don't mind links, use absolute_path.
  20 *
  21 * If path is our buffer, then return path, as it's already what the
  22 * user wants.
  23 */
  24const char *real_path(const char *path)
  25{
  26        static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
  27        char cwd[1024] = "";
  28        int buf_index = 1;
  29
  30        int depth = MAXDEPTH;
  31        char *last_elem = NULL;
  32        struct stat st;
  33
  34        /* We've already done it */
  35        if (path == buf || path == next_buf)
  36                return path;
  37
  38        if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
  39                die ("Too long path: %.*s", 60, path);
  40
  41        while (depth--) {
  42                if (!is_directory(buf)) {
  43                        char *last_slash = strrchr(buf, '/');
  44                        if (last_slash) {
  45                                *last_slash = '\0';
  46                                last_elem = xstrdup(last_slash + 1);
  47                        } else {
  48                                last_elem = xstrdup(buf);
  49                                *buf = '\0';
  50                        }
  51                }
  52
  53                if (*buf) {
  54                        if (!*cwd && !getcwd(cwd, sizeof(cwd)))
  55                                die_errno ("Could not get current working directory");
  56
  57                        if (chdir(buf))
  58                                die_errno ("Could not switch to '%s'", buf);
  59                }
  60                if (!getcwd(buf, PATH_MAX))
  61                        die_errno ("Could not get current working directory");
  62
  63                if (last_elem) {
  64                        size_t len = strlen(buf);
  65                        if (len + strlen(last_elem) + 2 > PATH_MAX)
  66                                die ("Too long path name: '%s/%s'",
  67                                                buf, last_elem);
  68                        if (len && buf[len-1] != '/')
  69                                buf[len++] = '/';
  70                        strcpy(buf + len, last_elem);
  71                        free(last_elem);
  72                        last_elem = NULL;
  73                }
  74
  75                if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
  76                        ssize_t len = readlink(buf, next_buf, PATH_MAX);
  77                        if (len < 0)
  78                                die_errno ("Invalid symlink '%s'", buf);
  79                        if (PATH_MAX <= len)
  80                                die("symbolic link too long: %s", buf);
  81                        next_buf[len] = '\0';
  82                        buf = next_buf;
  83                        buf_index = 1 - buf_index;
  84                        next_buf = bufs[buf_index];
  85                } else
  86                        break;
  87        }
  88
  89        if (*cwd && chdir(cwd))
  90                die_errno ("Could not change back to '%s'", cwd);
  91
  92        return buf;
  93}
  94
  95static const char *get_pwd_cwd(void)
  96{
  97        static char cwd[PATH_MAX + 1];
  98        char *pwd;
  99        struct stat cwd_stat, pwd_stat;
 100        if (getcwd(cwd, PATH_MAX) == NULL)
 101                return NULL;
 102        pwd = getenv("PWD");
 103        if (pwd && strcmp(pwd, cwd)) {
 104                stat(cwd, &cwd_stat);
 105                if (!stat(pwd, &pwd_stat) &&
 106                    pwd_stat.st_dev == cwd_stat.st_dev &&
 107                    pwd_stat.st_ino == cwd_stat.st_ino) {
 108                        strlcpy(cwd, pwd, PATH_MAX);
 109                }
 110        }
 111        return cwd;
 112}
 113
 114/*
 115 * Use this to get an absolute path from a relative one. If you want
 116 * to resolve links, you should use real_path.
 117 *
 118 * If the path is already absolute, then return path. As the user is
 119 * never meant to free the return value, we're safe.
 120 */
 121const char *absolute_path(const char *path)
 122{
 123        static char buf[PATH_MAX + 1];
 124
 125        if (is_absolute_path(path)) {
 126                if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
 127                        die("Too long path: %.*s", 60, path);
 128        } else {
 129                size_t len;
 130                const char *fmt;
 131                const char *cwd = get_pwd_cwd();
 132                if (!cwd)
 133                        die_errno("Cannot determine the current working directory");
 134                len = strlen(cwd);
 135                fmt = (len > 0 && is_dir_sep(cwd[len-1])) ? "%s%s" : "%s/%s";
 136                if (snprintf(buf, PATH_MAX, fmt, cwd, path) >= PATH_MAX)
 137                        die("Too long path: %.*s", 60, path);
 138        }
 139        return buf;
 140}