abspath.con commit Merge branch 'jc/diff-stat-scaler' into maint (a67c235)
   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 = find_last_dir_sep(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 && !is_dir_sep(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 ((cwd_stat.st_dev || cwd_stat.st_ino) &&
 106                    !stat(pwd, &pwd_stat) &&
 107                    pwd_stat.st_dev == cwd_stat.st_dev &&
 108                    pwd_stat.st_ino == cwd_stat.st_ino) {
 109                        strlcpy(cwd, pwd, PATH_MAX);
 110                }
 111        }
 112        return cwd;
 113}
 114
 115/*
 116 * Use this to get an absolute path from a relative one. If you want
 117 * to resolve links, you should use real_path.
 118 *
 119 * If the path is already absolute, then return path. As the user is
 120 * never meant to free the return value, we're safe.
 121 */
 122const char *absolute_path(const char *path)
 123{
 124        static char buf[PATH_MAX + 1];
 125
 126        if (is_absolute_path(path)) {
 127                if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
 128                        die("Too long path: %.*s", 60, path);
 129        } else {
 130                size_t len;
 131                const char *fmt;
 132                const char *cwd = get_pwd_cwd();
 133                if (!cwd)
 134                        die_errno("Cannot determine the current working directory");
 135                len = strlen(cwd);
 136                fmt = (len > 0 && is_dir_sep(cwd[len-1])) ? "%s%s" : "%s/%s";
 137                if (snprintf(buf, PATH_MAX, fmt, cwd, path) >= PATH_MAX)
 138                        die("Too long path: %.*s", 60, path);
 139        }
 140        return buf;
 141}
 142
 143/*
 144 * Unlike prefix_path, this should be used if the named file does
 145 * not have to interact with index entry; i.e. name of a random file
 146 * on the filesystem.
 147 */
 148const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 149{
 150        static char path[PATH_MAX];
 151#ifndef WIN32
 152        if (!pfx_len || is_absolute_path(arg))
 153                return arg;
 154        memcpy(path, pfx, pfx_len);
 155        strcpy(path + pfx_len, arg);
 156#else
 157        char *p;
 158        /* don't add prefix to absolute paths, but still replace '\' by '/' */
 159        if (is_absolute_path(arg))
 160                pfx_len = 0;
 161        else if (pfx_len)
 162                memcpy(path, pfx, pfx_len);
 163        strcpy(path + pfx_len, arg);
 164        for (p = path + pfx_len; *p; p++)
 165                if (*p == '\\')
 166                        *p = '/';
 167#endif
 168        return path;
 169}