| 77 | #define REALPATH_DIE_ON_ERROR (1 << 1) |
| 78 | |
| 79 | static char *strbuf_realpath_1(struct strbuf *resolved, const char *path, |
| 80 | int flags) |
| 81 | { |
| 82 | struct strbuf remaining = STRBUF_INIT; |
| 83 | struct strbuf next = STRBUF_INIT; |
| 84 | struct strbuf symlink = STRBUF_INIT; |
| 85 | char *retval = NULL; |
| 86 | int num_symlinks = 0; |
| 87 | struct stat st; |
| 88 | |
| 89 | if (!*path) { |
| 90 | if (flags & REALPATH_DIE_ON_ERROR) |
| 91 | die("The empty string is not a valid path"); |
| 92 | else |
| 93 | goto error_out; |
| 94 | } |
| 95 | |
| 96 | strbuf_addstr(&remaining, path); |
| 97 | get_root_part(resolved, &remaining); |
| 98 | |
| 99 | if (!resolved->len) { |
| 100 | /* relative path; can use CWD as the initial resolved path */ |
| 101 | if (strbuf_getcwd(resolved)) { |
| 102 | if (flags & REALPATH_DIE_ON_ERROR) |
| 103 | die_errno("unable to get current working directory"); |
| 104 | else |
| 105 | goto error_out; |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | /* Iterate over the remaining path components */ |
| 110 | while (remaining.len > 0) { |
| 111 | get_next_component(&next, &remaining); |
| 112 | |
| 113 | if (next.len == 0) { |
| 114 | continue; /* empty component */ |
| 115 | } else if (next.len == 1 && !strcmp(next.buf, ".")) { |
| 116 | continue; /* '.' component */ |
| 117 | } else if (next.len == 2 && !strcmp(next.buf, "..")) { |
| 118 | /* '..' component; strip the last path component */ |
| 119 | strip_last_component(resolved); |
| 120 | continue; |
| 121 | } |
| 122 | |
| 123 | /* append the next component and resolve resultant path */ |
| 124 | if (!is_dir_sep(resolved->buf[resolved->len - 1])) |
| 125 | strbuf_addch(resolved, '/'); |
| 126 | strbuf_addbuf(resolved, &next); |
| 127 | |
| 128 | if (lstat(resolved->buf, &st)) { |
| 129 | /* error out unless this was the last component */ |
| 130 | if (errno != ENOENT || |
| 131 | (!(flags & REALPATH_MANY_MISSING) && remaining.len)) { |
| 132 | if (flags & REALPATH_DIE_ON_ERROR) |
| 133 | die_errno("Invalid path '%s'", |
| 134 | resolved->buf); |
| 135 | else |
| 136 | goto error_out; |
no test coverage detected