abspath.con commit remote-hg: improve branch listing (c2f7a82)
   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 * Return the real path (i.e., absolute path, with symlinks resolved
  19 * and extra slashes removed) equivalent to the specified path.  (If
  20 * you want an absolute path but don't mind links, use
  21 * absolute_path().)  The return value is a pointer to a static
  22 * buffer.
  23 *
  24 * The input and all intermediate paths must be shorter than MAX_PATH.
  25 * The directory part of path (i.e., everything up to the last
  26 * dir_sep) must denote a valid, existing directory, but the last
  27 * component need not exist.  If die_on_error is set, then die with an
  28 * informative error message if there is a problem.  Otherwise, return
  29 * NULL on errors (without generating any output).
  30 *
  31 * If path is our buffer, then return path, as it's already what the
  32 * user wants.
  33 */
  34static const char *real_path_internal(const char *path, int die_on_error)
  35{
  36        static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
  37        char *retval = NULL;
  38
  39        /*
  40         * If we have to temporarily chdir(), store the original CWD
  41         * here so that we can chdir() back to it at the end of the
  42         * function:
  43         */
  44        char cwd[1024] = "";
  45
  46        int buf_index = 1;
  47
  48        int depth = MAXDEPTH;
  49        char *last_elem = NULL;
  50        struct stat st;
  51
  52        /* We've already done it */
  53        if (path == buf || path == next_buf)
  54                return path;
  55
  56        if (!*path) {
  57                if (die_on_error)
  58                        die("The empty string is not a valid path");
  59                else
  60                        goto error_out;
  61        }
  62
  63        if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) {
  64                if (die_on_error)
  65                        die("Too long path: %.*s", 60, path);
  66                else
  67                        goto error_out;
  68        }
  69
  70        while (depth--) {
  71                if (!is_directory(buf)) {
  72                        char *last_slash = find_last_dir_sep(buf);
  73                        if (last_slash) {
  74                                last_elem = xstrdup(last_slash + 1);
  75                                last_slash[1] = '\0';
  76                        } else {
  77                                last_elem = xstrdup(buf);
  78                                *buf = '\0';
  79                        }
  80                }
  81
  82                if (*buf) {
  83                        if (!*cwd && !getcwd(cwd, sizeof(cwd))) {
  84                                if (die_on_error)
  85                                        die_errno("Could not get current working directory");
  86                                else
  87                                        goto error_out;
  88                        }
  89
  90                        if (chdir(buf)) {
  91                                if (die_on_error)
  92                                        die_errno("Could not switch to '%s'", buf);
  93                                else
  94                                        goto error_out;
  95                        }
  96                }
  97                if (!getcwd(buf, PATH_MAX)) {
  98                        if (die_on_error)
  99                                die_errno("Could not get current working directory");
 100                        else
 101                                goto error_out;
 102                }
 103
 104                if (last_elem) {
 105                        size_t len = strlen(buf);
 106                        if (len + strlen(last_elem) + 2 > PATH_MAX) {
 107                                if (die_on_error)
 108                                        die("Too long path name: '%s/%s'",
 109                                            buf, last_elem);
 110                                else
 111                                        goto error_out;
 112                        }
 113                        if (len && !is_dir_sep(buf[len-1]))
 114                                buf[len++] = '/';
 115                        strcpy(buf + len, last_elem);
 116                        free(last_elem);
 117                        last_elem = NULL;
 118                }
 119
 120                if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
 121                        ssize_t len = readlink(buf, next_buf, PATH_MAX);
 122                        if (len < 0) {
 123                                if (die_on_error)
 124                                        die_errno("Invalid symlink '%s'", buf);
 125                                else
 126                                        goto error_out;
 127                        }
 128                        if (PATH_MAX <= len) {
 129                                if (die_on_error)
 130                                        die("symbolic link too long: %s", buf);
 131                                else
 132                                        goto error_out;
 133                        }
 134                        next_buf[len] = '\0';
 135                        buf = next_buf;
 136                        buf_index = 1 - buf_index;
 137                        next_buf = bufs[buf_index];
 138                } else
 139                        break;
 140        }
 141
 142        retval = buf;
 143error_out:
 144        free(last_elem);
 145        if (*cwd && chdir(cwd))
 146                die_errno ("Could not change back to '%s'", cwd);
 147
 148        return retval;
 149}
 150
 151const char *real_path(const char *path)
 152{
 153        return real_path_internal(path, 1);
 154}
 155
 156const char *real_path_if_valid(const char *path)
 157{
 158        return real_path_internal(path, 0);
 159}
 160
 161static const char *get_pwd_cwd(void)
 162{
 163        static char cwd[PATH_MAX + 1];
 164        char *pwd;
 165        struct stat cwd_stat, pwd_stat;
 166        if (getcwd(cwd, PATH_MAX) == NULL)
 167                return NULL;
 168        pwd = getenv("PWD");
 169        if (pwd && strcmp(pwd, cwd)) {
 170                stat(cwd, &cwd_stat);
 171                if ((cwd_stat.st_dev || cwd_stat.st_ino) &&
 172                    !stat(pwd, &pwd_stat) &&
 173                    pwd_stat.st_dev == cwd_stat.st_dev &&
 174                    pwd_stat.st_ino == cwd_stat.st_ino) {
 175                        strlcpy(cwd, pwd, PATH_MAX);
 176                }
 177        }
 178        return cwd;
 179}
 180
 181/*
 182 * Use this to get an absolute path from a relative one. If you want
 183 * to resolve links, you should use real_path.
 184 *
 185 * If the path is already absolute, then return path. As the user is
 186 * never meant to free the return value, we're safe.
 187 */
 188const char *absolute_path(const char *path)
 189{
 190        static char buf[PATH_MAX + 1];
 191
 192        if (!*path) {
 193                die("The empty string is not a valid path");
 194        } else if (is_absolute_path(path)) {
 195                if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
 196                        die("Too long path: %.*s", 60, path);
 197        } else {
 198                size_t len;
 199                const char *fmt;
 200                const char *cwd = get_pwd_cwd();
 201                if (!cwd)
 202                        die_errno("Cannot determine the current working directory");
 203                len = strlen(cwd);
 204                fmt = (len > 0 && is_dir_sep(cwd[len-1])) ? "%s%s" : "%s/%s";
 205                if (snprintf(buf, PATH_MAX, fmt, cwd, path) >= PATH_MAX)
 206                        die("Too long path: %.*s", 60, path);
 207        }
 208        return buf;
 209}
 210
 211/*
 212 * Unlike prefix_path, this should be used if the named file does
 213 * not have to interact with index entry; i.e. name of a random file
 214 * on the filesystem.
 215 */
 216const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 217{
 218        static char path[PATH_MAX];
 219#ifndef WIN32
 220        if (!pfx_len || is_absolute_path(arg))
 221                return arg;
 222        memcpy(path, pfx, pfx_len);
 223        strcpy(path + pfx_len, arg);
 224#else
 225        char *p;
 226        /* don't add prefix to absolute paths, but still replace '\' by '/' */
 227        if (is_absolute_path(arg))
 228                pfx_len = 0;
 229        else if (pfx_len)
 230                memcpy(path, pfx, pfx_len);
 231        strcpy(path + pfx_len, arg);
 232        for (p = path + pfx_len; *p; p++)
 233                if (*p == '\\')
 234                        *p = '/';
 235#endif
 236        return path;
 237}