abspath.con commit Merge branch 'jl/interrupt-clone-remove-separate-git-dir' into maint (32e820b)
   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 (!*path)
  39                die("The empty string is not a valid path");
  40
  41        if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
  42                die ("Too long path: %.*s", 60, path);
  43
  44        while (depth--) {
  45                if (!is_directory(buf)) {
  46                        char *last_slash = find_last_dir_sep(buf);
  47                        if (last_slash) {
  48                                last_elem = xstrdup(last_slash + 1);
  49                                last_slash[1] = '\0';
  50                        } else {
  51                                last_elem = xstrdup(buf);
  52                                *buf = '\0';
  53                        }
  54                }
  55
  56                if (*buf) {
  57                        if (!*cwd && !getcwd(cwd, sizeof(cwd)))
  58                                die_errno ("Could not get current working directory");
  59
  60                        if (chdir(buf))
  61                                die_errno ("Could not switch to '%s'", buf);
  62                }
  63                if (!getcwd(buf, PATH_MAX))
  64                        die_errno ("Could not get current working directory");
  65
  66                if (last_elem) {
  67                        size_t len = strlen(buf);
  68                        if (len + strlen(last_elem) + 2 > PATH_MAX)
  69                                die ("Too long path name: '%s/%s'",
  70                                                buf, last_elem);
  71                        if (len && !is_dir_sep(buf[len-1]))
  72                                buf[len++] = '/';
  73                        strcpy(buf + len, last_elem);
  74                        free(last_elem);
  75                        last_elem = NULL;
  76                }
  77
  78                if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
  79                        ssize_t len = readlink(buf, next_buf, PATH_MAX);
  80                        if (len < 0)
  81                                die_errno ("Invalid symlink '%s'", buf);
  82                        if (PATH_MAX <= len)
  83                                die("symbolic link too long: %s", buf);
  84                        next_buf[len] = '\0';
  85                        buf = next_buf;
  86                        buf_index = 1 - buf_index;
  87                        next_buf = bufs[buf_index];
  88                } else
  89                        break;
  90        }
  91
  92        if (*cwd && chdir(cwd))
  93                die_errno ("Could not change back to '%s'", cwd);
  94
  95        return buf;
  96}
  97
  98static const char *get_pwd_cwd(void)
  99{
 100        static char cwd[PATH_MAX + 1];
 101        char *pwd;
 102        struct stat cwd_stat, pwd_stat;
 103        if (getcwd(cwd, PATH_MAX) == NULL)
 104                return NULL;
 105        pwd = getenv("PWD");
 106        if (pwd && strcmp(pwd, cwd)) {
 107                stat(cwd, &cwd_stat);
 108                if ((cwd_stat.st_dev || cwd_stat.st_ino) &&
 109                    !stat(pwd, &pwd_stat) &&
 110                    pwd_stat.st_dev == cwd_stat.st_dev &&
 111                    pwd_stat.st_ino == cwd_stat.st_ino) {
 112                        strlcpy(cwd, pwd, PATH_MAX);
 113                }
 114        }
 115        return cwd;
 116}
 117
 118/*
 119 * Use this to get an absolute path from a relative one. If you want
 120 * to resolve links, you should use real_path.
 121 *
 122 * If the path is already absolute, then return path. As the user is
 123 * never meant to free the return value, we're safe.
 124 */
 125const char *absolute_path(const char *path)
 126{
 127        static char buf[PATH_MAX + 1];
 128
 129        if (!*path) {
 130                die("The empty string is not a valid path");
 131        } else if (is_absolute_path(path)) {
 132                if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
 133                        die("Too long path: %.*s", 60, path);
 134        } else {
 135                size_t len;
 136                const char *fmt;
 137                const char *cwd = get_pwd_cwd();
 138                if (!cwd)
 139                        die_errno("Cannot determine the current working directory");
 140                len = strlen(cwd);
 141                fmt = (len > 0 && is_dir_sep(cwd[len-1])) ? "%s%s" : "%s/%s";
 142                if (snprintf(buf, PATH_MAX, fmt, cwd, path) >= PATH_MAX)
 143                        die("Too long path: %.*s", 60, path);
 144        }
 145        return buf;
 146}
 147
 148/*
 149 * Unlike prefix_path, this should be used if the named file does
 150 * not have to interact with index entry; i.e. name of a random file
 151 * on the filesystem.
 152 */
 153const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 154{
 155        static char path[PATH_MAX];
 156#ifndef WIN32
 157        if (!pfx_len || is_absolute_path(arg))
 158                return arg;
 159        memcpy(path, pfx, pfx_len);
 160        strcpy(path + pfx_len, arg);
 161#else
 162        char *p;
 163        /* don't add prefix to absolute paths, but still replace '\' by '/' */
 164        if (is_absolute_path(arg))
 165                pfx_len = 0;
 166        else if (pfx_len)
 167                memcpy(path, pfx, pfx_len);
 168        strcpy(path + pfx_len, arg);
 169        for (p = path + pfx_len; *p; p++)
 170                if (*p == '\\')
 171                        *p = '/';
 172#endif
 173        return path;
 174}