Merge branch 'jh/memihash-opt'
[git] / abspath.c
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  */
8 int is_directory(const char *path)
9 {
10         struct stat st;
11         return (!stat(path, &st) && S_ISDIR(st.st_mode));
12 }
13
14 /* removes the last path component from 'path' except if 'path' is root */
15 static void strip_last_component(struct strbuf *path)
16 {
17         size_t offset = offset_1st_component(path->buf);
18         size_t len = path->len;
19
20         /* Find start of the last component */
21         while (offset < len && !is_dir_sep(path->buf[len - 1]))
22                 len--;
23         /* Skip sequences of multiple path-separators */
24         while (offset < len && is_dir_sep(path->buf[len - 1]))
25                 len--;
26
27         strbuf_setlen(path, len);
28 }
29
30 /* get (and remove) the next component in 'remaining' and place it in 'next' */
31 static void get_next_component(struct strbuf *next, struct strbuf *remaining)
32 {
33         char *start = NULL;
34         char *end = NULL;
35
36         strbuf_reset(next);
37
38         /* look for the next component */
39         /* Skip sequences of multiple path-separators */
40         for (start = remaining->buf; is_dir_sep(*start); start++)
41                 ; /* nothing */
42         /* Find end of the path component */
43         for (end = start; *end && !is_dir_sep(*end); end++)
44                 ; /* nothing */
45
46         strbuf_add(next, start, end - start);
47         /* remove the component from 'remaining' */
48         strbuf_remove(remaining, 0, end - remaining->buf);
49 }
50
51 /* copies root part from remaining to resolved, canonicalizing it on the way */
52 static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)
53 {
54         int offset = offset_1st_component(remaining->buf);
55
56         strbuf_reset(resolved);
57         strbuf_add(resolved, remaining->buf, offset);
58 #ifdef GIT_WINDOWS_NATIVE
59         convert_slashes(resolved->buf);
60 #endif
61         strbuf_remove(remaining, 0, offset);
62 }
63
64 /* We allow "recursive" symbolic links. Only within reason, though. */
65 #ifndef MAXSYMLINKS
66 #define MAXSYMLINKS 32
67 #endif
68
69 /*
70  * Return the real path (i.e., absolute path, with symlinks resolved
71  * and extra slashes removed) equivalent to the specified path.  (If
72  * you want an absolute path but don't mind links, use
73  * absolute_path().)  Places the resolved realpath in the provided strbuf.
74  *
75  * The directory part of path (i.e., everything up to the last
76  * dir_sep) must denote a valid, existing directory, but the last
77  * component need not exist.  If die_on_error is set, then die with an
78  * informative error message if there is a problem.  Otherwise, return
79  * NULL on errors (without generating any output).
80  */
81 char *strbuf_realpath(struct strbuf *resolved, const char *path,
82                       int die_on_error)
83 {
84         struct strbuf remaining = STRBUF_INIT;
85         struct strbuf next = STRBUF_INIT;
86         struct strbuf symlink = STRBUF_INIT;
87         char *retval = NULL;
88         int num_symlinks = 0;
89         struct stat st;
90
91         if (!*path) {
92                 if (die_on_error)
93                         die("The empty string is not a valid path");
94                 else
95                         goto error_out;
96         }
97
98         strbuf_addstr(&remaining, path);
99         get_root_part(resolved, &remaining);
100
101         if (!resolved->len) {
102                 /* relative path; can use CWD as the initial resolved path */
103                 if (strbuf_getcwd(resolved)) {
104                         if (die_on_error)
105                                 die_errno("unable to get current working directory");
106                         else
107                                 goto error_out;
108                 }
109         }
110
111         /* Iterate over the remaining path components */
112         while (remaining.len > 0) {
113                 get_next_component(&next, &remaining);
114
115                 if (next.len == 0) {
116                         continue; /* empty component */
117                 } else if (next.len == 1 && !strcmp(next.buf, ".")) {
118                         continue; /* '.' component */
119                 } else if (next.len == 2 && !strcmp(next.buf, "..")) {
120                         /* '..' component; strip the last path component */
121                         strip_last_component(resolved);
122                         continue;
123                 }
124
125                 /* append the next component and resolve resultant path */
126                 if (!is_dir_sep(resolved->buf[resolved->len - 1]))
127                         strbuf_addch(resolved, '/');
128                 strbuf_addbuf(resolved, &next);
129
130                 if (lstat(resolved->buf, &st)) {
131                         /* error out unless this was the last component */
132                         if (errno != ENOENT || remaining.len) {
133                                 if (die_on_error)
134                                         die_errno("Invalid path '%s'",
135                                                   resolved->buf);
136                                 else
137                                         goto error_out;
138                         }
139                 } else if (S_ISLNK(st.st_mode)) {
140                         ssize_t len;
141                         strbuf_reset(&symlink);
142
143                         if (num_symlinks++ > MAXSYMLINKS) {
144                                 errno = ELOOP;
145
146                                 if (die_on_error)
147                                         die("More than %d nested symlinks "
148                                             "on path '%s'", MAXSYMLINKS, path);
149                                 else
150                                         goto error_out;
151                         }
152
153                         len = strbuf_readlink(&symlink, resolved->buf,
154                                               st.st_size);
155                         if (len < 0) {
156                                 if (die_on_error)
157                                         die_errno("Invalid symlink '%s'",
158                                                   resolved->buf);
159                                 else
160                                         goto error_out;
161                         }
162
163                         if (is_absolute_path(symlink.buf)) {
164                                 /* absolute symlink; set resolved to root */
165                                 get_root_part(resolved, &symlink);
166                         } else {
167                                 /*
168                                  * relative symlink
169                                  * strip off the last component since it will
170                                  * be replaced with the contents of the symlink
171                                  */
172                                 strip_last_component(resolved);
173                         }
174
175                         /*
176                          * if there are still remaining components to resolve
177                          * then append them to symlink
178                          */
179                         if (remaining.len) {
180                                 strbuf_addch(&symlink, '/');
181                                 strbuf_addbuf(&symlink, &remaining);
182                         }
183
184                         /*
185                          * use the symlink as the remaining components that
186                          * need to be resloved
187                          */
188                         strbuf_swap(&symlink, &remaining);
189                 }
190         }
191
192         retval = resolved->buf;
193
194 error_out:
195         strbuf_release(&remaining);
196         strbuf_release(&next);
197         strbuf_release(&symlink);
198
199         if (!retval)
200                 strbuf_reset(resolved);
201
202         return retval;
203 }
204
205 const char *real_path(const char *path)
206 {
207         static struct strbuf realpath = STRBUF_INIT;
208         return strbuf_realpath(&realpath, path, 1);
209 }
210
211 const char *real_path_if_valid(const char *path)
212 {
213         static struct strbuf realpath = STRBUF_INIT;
214         return strbuf_realpath(&realpath, path, 0);
215 }
216
217 char *real_pathdup(const char *path, int die_on_error)
218 {
219         struct strbuf realpath = STRBUF_INIT;
220         char *retval = NULL;
221
222         if (strbuf_realpath(&realpath, path, die_on_error))
223                 retval = strbuf_detach(&realpath, NULL);
224
225         strbuf_release(&realpath);
226
227         return retval;
228 }
229
230 /*
231  * Use this to get an absolute path from a relative one. If you want
232  * to resolve links, you should use real_path.
233  */
234 const char *absolute_path(const char *path)
235 {
236         static struct strbuf sb = STRBUF_INIT;
237         strbuf_reset(&sb);
238         strbuf_add_absolute_path(&sb, path);
239         return sb.buf;
240 }
241
242 char *absolute_pathdup(const char *path)
243 {
244         struct strbuf sb = STRBUF_INIT;
245         strbuf_add_absolute_path(&sb, path);
246         return strbuf_detach(&sb, NULL);
247 }
248
249 char *prefix_filename(const char *pfx, const char *arg)
250 {
251         struct strbuf path = STRBUF_INIT;
252         size_t pfx_len = pfx ? strlen(pfx) : 0;
253
254         if (!pfx_len)
255                 ; /* nothing to prefix */
256         else if (is_absolute_path(arg))
257                 pfx_len = 0;
258         else
259                 strbuf_add(&path, pfx, pfx_len);
260
261         strbuf_addstr(&path, arg);
262 #ifdef GIT_WINDOWS_NATIVE
263         convert_slashes(path.buf + pfx_len);
264 #endif
265         return strbuf_detach(&path, NULL);
266 }