]>
Commit | Line | Data |
---|---|---|
1 | #include "git-compat-util.h" | |
2 | #include "abspath.h" | |
3 | #include "strbuf.h" | |
4 | ||
5 | /* | |
6 | * Do not use this for inspecting *tracked* content. When path is a | |
7 | * symlink to a directory, we do not want to say it is a directory when | |
8 | * dealing with tracked content in the working tree. | |
9 | */ | |
10 | int is_directory(const char *path) | |
11 | { | |
12 | struct stat st; | |
13 | return (!stat(path, &st) && S_ISDIR(st.st_mode)); | |
14 | } | |
15 | ||
16 | /* removes the last path component from 'path' except if 'path' is root */ | |
17 | static void strip_last_component(struct strbuf *path) | |
18 | { | |
19 | size_t offset = offset_1st_component(path->buf); | |
20 | size_t len = path->len; | |
21 | ||
22 | /* Find start of the last component */ | |
23 | while (offset < len && !is_dir_sep(path->buf[len - 1])) | |
24 | len--; | |
25 | /* Skip sequences of multiple path-separators */ | |
26 | while (offset < len && is_dir_sep(path->buf[len - 1])) | |
27 | len--; | |
28 | ||
29 | strbuf_setlen(path, len); | |
30 | } | |
31 | ||
32 | /* get (and remove) the next component in 'remaining' and place it in 'next' */ | |
33 | static void get_next_component(struct strbuf *next, struct strbuf *remaining) | |
34 | { | |
35 | char *start = NULL; | |
36 | char *end = NULL; | |
37 | ||
38 | strbuf_reset(next); | |
39 | ||
40 | /* look for the next component */ | |
41 | /* Skip sequences of multiple path-separators */ | |
42 | for (start = remaining->buf; is_dir_sep(*start); start++) | |
43 | ; /* nothing */ | |
44 | /* Find end of the path component */ | |
45 | for (end = start; *end && !is_dir_sep(*end); end++) | |
46 | ; /* nothing */ | |
47 | ||
48 | strbuf_add(next, start, end - start); | |
49 | /* remove the component from 'remaining' */ | |
50 | strbuf_remove(remaining, 0, end - remaining->buf); | |
51 | } | |
52 | ||
53 | /* copies root part from remaining to resolved, canonicalizing it on the way */ | |
54 | static void get_root_part(struct strbuf *resolved, struct strbuf *remaining) | |
55 | { | |
56 | int offset = offset_1st_component(remaining->buf); | |
57 | ||
58 | strbuf_reset(resolved); | |
59 | strbuf_add(resolved, remaining->buf, offset); | |
60 | #ifdef GIT_WINDOWS_NATIVE | |
61 | convert_slashes(resolved->buf); | |
62 | #endif | |
63 | strbuf_remove(remaining, 0, offset); | |
64 | } | |
65 | ||
66 | /* We allow "recursive" symbolic links. Only within reason, though. */ | |
67 | #ifndef MAXSYMLINKS | |
68 | #define MAXSYMLINKS 32 | |
69 | #endif | |
70 | ||
71 | /* | |
72 | * If set, any number of trailing components may be missing; otherwise, only one | |
73 | * may be. | |
74 | */ | |
75 | #define REALPATH_MANY_MISSING (1 << 0) | |
76 | /* Should we die if there's an error? */ | |
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; | |
137 | } | |
138 | } else if (S_ISLNK(st.st_mode)) { | |
139 | ssize_t len; | |
140 | strbuf_reset(&symlink); | |
141 | ||
142 | if (num_symlinks++ > MAXSYMLINKS) { | |
143 | errno = ELOOP; | |
144 | ||
145 | if (flags & REALPATH_DIE_ON_ERROR) | |
146 | die("More than %d nested symlinks " | |
147 | "on path '%s'", MAXSYMLINKS, path); | |
148 | else | |
149 | goto error_out; | |
150 | } | |
151 | ||
152 | len = strbuf_readlink(&symlink, resolved->buf, | |
153 | st.st_size); | |
154 | if (len < 0) { | |
155 | if (flags & REALPATH_DIE_ON_ERROR) | |
156 | die_errno("Invalid symlink '%s'", | |
157 | resolved->buf); | |
158 | else | |
159 | goto error_out; | |
160 | } | |
161 | ||
162 | if (is_absolute_path(symlink.buf)) { | |
163 | /* absolute symlink; set resolved to root */ | |
164 | get_root_part(resolved, &symlink); | |
165 | } else { | |
166 | /* | |
167 | * relative symlink | |
168 | * strip off the last component since it will | |
169 | * be replaced with the contents of the symlink | |
170 | */ | |
171 | strip_last_component(resolved); | |
172 | } | |
173 | ||
174 | /* | |
175 | * if there are still remaining components to resolve | |
176 | * then append them to symlink | |
177 | */ | |
178 | if (remaining.len) { | |
179 | strbuf_addch(&symlink, '/'); | |
180 | strbuf_addbuf(&symlink, &remaining); | |
181 | } | |
182 | ||
183 | /* | |
184 | * use the symlink as the remaining components that | |
185 | * need to be resolved | |
186 | */ | |
187 | strbuf_swap(&symlink, &remaining); | |
188 | } | |
189 | } | |
190 | ||
191 | retval = resolved->buf; | |
192 | ||
193 | error_out: | |
194 | strbuf_release(&remaining); | |
195 | strbuf_release(&next); | |
196 | strbuf_release(&symlink); | |
197 | ||
198 | if (!retval) | |
199 | strbuf_reset(resolved); | |
200 | ||
201 | return retval; | |
202 | } | |
203 | ||
204 | /* | |
205 | * Return the real path (i.e., absolute path, with symlinks resolved | |
206 | * and extra slashes removed) equivalent to the specified path. (If | |
207 | * you want an absolute path but don't mind links, use | |
208 | * absolute_path().) Places the resolved realpath in the provided strbuf. | |
209 | * | |
210 | * The directory part of path (i.e., everything up to the last | |
211 | * dir_sep) must denote a valid, existing directory, but the last | |
212 | * component need not exist. If die_on_error is set, then die with an | |
213 | * informative error message if there is a problem. Otherwise, return | |
214 | * NULL on errors (without generating any output). | |
215 | */ | |
216 | char *strbuf_realpath(struct strbuf *resolved, const char *path, | |
217 | int die_on_error) | |
218 | { | |
219 | return strbuf_realpath_1(resolved, path, | |
220 | die_on_error ? REALPATH_DIE_ON_ERROR : 0); | |
221 | } | |
222 | ||
223 | /* | |
224 | * Just like strbuf_realpath, but allows an arbitrary number of path | |
225 | * components to be missing. | |
226 | */ | |
227 | char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path, | |
228 | int die_on_error) | |
229 | { | |
230 | return strbuf_realpath_1(resolved, path, | |
231 | ((die_on_error ? REALPATH_DIE_ON_ERROR : 0) | | |
232 | REALPATH_MANY_MISSING)); | |
233 | } | |
234 | ||
235 | char *real_pathdup(const char *path, int die_on_error) | |
236 | { | |
237 | struct strbuf realpath = STRBUF_INIT; | |
238 | char *retval = NULL; | |
239 | ||
240 | if (strbuf_realpath(&realpath, path, die_on_error)) | |
241 | retval = strbuf_detach(&realpath, NULL); | |
242 | ||
243 | strbuf_release(&realpath); | |
244 | ||
245 | return retval; | |
246 | } | |
247 | ||
248 | /* | |
249 | * Use this to get an absolute path from a relative one. If you want | |
250 | * to resolve links, you should use strbuf_realpath. | |
251 | */ | |
252 | const char *absolute_path(const char *path) | |
253 | { | |
254 | static struct strbuf sb = STRBUF_INIT; | |
255 | strbuf_reset(&sb); | |
256 | strbuf_add_absolute_path(&sb, path); | |
257 | return sb.buf; | |
258 | } | |
259 | ||
260 | char *absolute_pathdup(const char *path) | |
261 | { | |
262 | struct strbuf sb = STRBUF_INIT; | |
263 | strbuf_add_absolute_path(&sb, path); | |
264 | return strbuf_detach(&sb, NULL); | |
265 | } | |
266 | ||
267 | char *prefix_filename(const char *pfx, const char *arg) | |
268 | { | |
269 | struct strbuf path = STRBUF_INIT; | |
270 | size_t pfx_len = pfx ? strlen(pfx) : 0; | |
271 | ||
272 | if (!pfx_len) | |
273 | ; /* nothing to prefix */ | |
274 | else if (is_absolute_path(arg)) | |
275 | pfx_len = 0; | |
276 | else | |
277 | strbuf_add(&path, pfx, pfx_len); | |
278 | ||
279 | strbuf_addstr(&path, arg); | |
280 | #ifdef GIT_WINDOWS_NATIVE | |
281 | convert_slashes(path.buf + pfx_len); | |
282 | #endif | |
283 | return strbuf_detach(&path, NULL); | |
284 | } | |
285 | ||
286 | char *prefix_filename_except_for_dash(const char *pfx, const char *arg) | |
287 | { | |
288 | if (!strcmp(arg, "-")) | |
289 | return xstrdup(arg); | |
290 | return prefix_filename(pfx, arg); | |
291 | } | |
292 | ||
293 | void strbuf_add_absolute_path(struct strbuf *sb, const char *path) | |
294 | { | |
295 | if (!*path) | |
296 | die("The empty string is not a valid path"); | |
297 | if (!is_absolute_path(path)) { | |
298 | struct stat cwd_stat, pwd_stat; | |
299 | size_t orig_len = sb->len; | |
300 | char *cwd = xgetcwd(); | |
301 | char *pwd = getenv("PWD"); | |
302 | if (pwd && strcmp(pwd, cwd) && | |
303 | !stat(cwd, &cwd_stat) && | |
304 | (cwd_stat.st_dev || cwd_stat.st_ino) && | |
305 | !stat(pwd, &pwd_stat) && | |
306 | pwd_stat.st_dev == cwd_stat.st_dev && | |
307 | pwd_stat.st_ino == cwd_stat.st_ino) | |
308 | strbuf_addstr(sb, pwd); | |
309 | else | |
310 | strbuf_addstr(sb, cwd); | |
311 | if (sb->len > orig_len && !is_dir_sep(sb->buf[sb->len - 1])) | |
312 | strbuf_addch(sb, '/'); | |
313 | free(cwd); | |
314 | } | |
315 | strbuf_addstr(sb, path); | |
316 | } | |
317 | ||
318 | void strbuf_add_real_path(struct strbuf *sb, const char *path) | |
319 | { | |
320 | if (sb->len) { | |
321 | struct strbuf resolved = STRBUF_INIT; | |
322 | strbuf_realpath(&resolved, path, 1); | |
323 | strbuf_addbuf(sb, &resolved); | |
324 | strbuf_release(&resolved); | |
325 | } else | |
326 | strbuf_realpath(sb, path, 1); | |
327 | } |