abspath.con commit --dirstat: In case of renames, use target filename instead of source filename (2ca8671)
   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
  17const char *make_absolute_path(const char *path)
  18{
  19        static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
  20        char cwd[1024] = "";
  21        int buf_index = 1;
  22
  23        int depth = MAXDEPTH;
  24        char *last_elem = NULL;
  25        struct stat st;
  26
  27        /* We've already done it */
  28        if (path == buf || path == next_buf)
  29                return path;
  30
  31        if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
  32                die ("Too long path: %.*s", 60, path);
  33
  34        while (depth--) {
  35                if (!is_directory(buf)) {
  36                        char *last_slash = strrchr(buf, '/');
  37                        if (last_slash) {
  38                                *last_slash = '\0';
  39                                last_elem = xstrdup(last_slash + 1);
  40                        } else {
  41                                last_elem = xstrdup(buf);
  42                                *buf = '\0';
  43                        }
  44                }
  45
  46                if (*buf) {
  47                        if (!*cwd && !getcwd(cwd, sizeof(cwd)))
  48                                die_errno ("Could not get current working directory");
  49
  50                        if (chdir(buf))
  51                                die_errno ("Could not switch to '%s'", buf);
  52                }
  53                if (!getcwd(buf, PATH_MAX))
  54                        die_errno ("Could not get current working directory");
  55
  56                if (last_elem) {
  57                        size_t len = strlen(buf);
  58                        if (len + strlen(last_elem) + 2 > PATH_MAX)
  59                                die ("Too long path name: '%s/%s'",
  60                                                buf, last_elem);
  61                        if (len && buf[len-1] != '/')
  62                                buf[len++] = '/';
  63                        strcpy(buf + len, last_elem);
  64                        free(last_elem);
  65                        last_elem = NULL;
  66                }
  67
  68                if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
  69                        ssize_t len = readlink(buf, next_buf, PATH_MAX);
  70                        if (len < 0)
  71                                die_errno ("Invalid symlink '%s'", buf);
  72                        if (PATH_MAX <= len)
  73                                die("symbolic link too long: %s", buf);
  74                        next_buf[len] = '\0';
  75                        buf = next_buf;
  76                        buf_index = 1 - buf_index;
  77                        next_buf = bufs[buf_index];
  78                } else
  79                        break;
  80        }
  81
  82        if (*cwd && chdir(cwd))
  83                die_errno ("Could not change back to '%s'", cwd);
  84
  85        return buf;
  86}
  87
  88static const char *get_pwd_cwd(void)
  89{
  90        static char cwd[PATH_MAX + 1];
  91        char *pwd;
  92        struct stat cwd_stat, pwd_stat;
  93        if (getcwd(cwd, PATH_MAX) == NULL)
  94                return NULL;
  95        pwd = getenv("PWD");
  96        if (pwd && strcmp(pwd, cwd)) {
  97                stat(cwd, &cwd_stat);
  98                if (!stat(pwd, &pwd_stat) &&
  99                    pwd_stat.st_dev == cwd_stat.st_dev &&
 100                    pwd_stat.st_ino == cwd_stat.st_ino) {
 101                        strlcpy(cwd, pwd, PATH_MAX);
 102                }
 103        }
 104        return cwd;
 105}
 106
 107const char *make_nonrelative_path(const char *path)
 108{
 109        static char buf[PATH_MAX + 1];
 110
 111        if (is_absolute_path(path)) {
 112                if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
 113                        die("Too long path: %.*s", 60, path);
 114        } else {
 115                size_t len;
 116                const char *fmt;
 117                const char *cwd = get_pwd_cwd();
 118                if (!cwd)
 119                        die_errno("Cannot determine the current working directory");
 120                len = strlen(cwd);
 121                fmt = (len > 0 && is_dir_sep(cwd[len-1])) ? "%s%s" : "%s/%s";
 122                if (snprintf(buf, PATH_MAX, fmt, cwd, path) >= PATH_MAX)
 123                        die("Too long path: %.*s", 60, path);
 124        }
 125        return buf;
 126}