Skip to content

Commit be6e0da

Browse files
bk2204gitster
authored andcommitted
abspath: add a function to resolve paths with missing components
Currently, we have a function to resolve paths, strbuf_realpath. This function canonicalizes paths like realpath(3), but permits a trailing component to be absent from the file system. In other words, this is the behavior of the GNU realpath(1) without any arguments. In the future, we'll need this same behavior, except that we want to allow for any number of missing trailing components, which is the behavior of GNU realpath(1) with the -m option. This is useful because we'll want to canonicalize a path that may point to a not yet present path under the .git directory. For example, a user may want to know where an arbitrary ref would be stored if it existed in the file system. Let's refactor strbuf_realpath to move most of the code to an internal function and then pass it two flags to control its behavior. We'll add a strbuf_realpath_forgiving function that has our new behavior, and leave strbuf_realpath with the older, stricter behavior. Signed-off-by: brian m. carlson <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3cf5978 commit be6e0da

File tree

2 files changed

+48
-18
lines changed

2 files changed

+48
-18
lines changed

abspath.c

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,15 @@ static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)
6767
#endif
6868

6969
/*
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).
70+
* If set, any number of trailing components may be missing; otherwise, only one
71+
* may be.
8072
*/
81-
char *strbuf_realpath(struct strbuf *resolved, const char *path,
82-
int die_on_error)
73+
#define REALPATH_MANY_MISSING (1 << 0)
74+
/* Should we die if there's an error? */
75+
#define REALPATH_DIE_ON_ERROR (1 << 1)
76+
77+
static char *strbuf_realpath_1(struct strbuf *resolved, const char *path,
78+
int flags)
8379
{
8480
struct strbuf remaining = STRBUF_INIT;
8581
struct strbuf next = STRBUF_INIT;
@@ -89,7 +85,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,
8985
struct stat st;
9086

9187
if (!*path) {
92-
if (die_on_error)
88+
if (flags & REALPATH_DIE_ON_ERROR)
9389
die("The empty string is not a valid path");
9490
else
9591
goto error_out;
@@ -101,7 +97,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,
10197
if (!resolved->len) {
10298
/* relative path; can use CWD as the initial resolved path */
10399
if (strbuf_getcwd(resolved)) {
104-
if (die_on_error)
100+
if (flags & REALPATH_DIE_ON_ERROR)
105101
die_errno("unable to get current working directory");
106102
else
107103
goto error_out;
@@ -129,8 +125,9 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,
129125

130126
if (lstat(resolved->buf, &st)) {
131127
/* error out unless this was the last component */
132-
if (errno != ENOENT || remaining.len) {
133-
if (die_on_error)
128+
if (errno != ENOENT ||
129+
(!(flags & REALPATH_MANY_MISSING) && remaining.len)) {
130+
if (flags & REALPATH_DIE_ON_ERROR)
134131
die_errno("Invalid path '%s'",
135132
resolved->buf);
136133
else
@@ -143,7 +140,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,
143140
if (num_symlinks++ > MAXSYMLINKS) {
144141
errno = ELOOP;
145142

146-
if (die_on_error)
143+
if (flags & REALPATH_DIE_ON_ERROR)
147144
die("More than %d nested symlinks "
148145
"on path '%s'", MAXSYMLINKS, path);
149146
else
@@ -153,7 +150,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,
153150
len = strbuf_readlink(&symlink, resolved->buf,
154151
st.st_size);
155152
if (len < 0) {
156-
if (die_on_error)
153+
if (flags & REALPATH_DIE_ON_ERROR)
157154
die_errno("Invalid symlink '%s'",
158155
resolved->buf);
159156
else
@@ -202,6 +199,37 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,
202199
return retval;
203200
}
204201

202+
/*
203+
* Return the real path (i.e., absolute path, with symlinks resolved
204+
* and extra slashes removed) equivalent to the specified path. (If
205+
* you want an absolute path but don't mind links, use
206+
* absolute_path().) Places the resolved realpath in the provided strbuf.
207+
*
208+
* The directory part of path (i.e., everything up to the last
209+
* dir_sep) must denote a valid, existing directory, but the last
210+
* component need not exist. If die_on_error is set, then die with an
211+
* informative error message if there is a problem. Otherwise, return
212+
* NULL on errors (without generating any output).
213+
*/
214+
char *strbuf_realpath(struct strbuf *resolved, const char *path,
215+
int die_on_error)
216+
{
217+
return strbuf_realpath_1(resolved, path,
218+
die_on_error ? REALPATH_DIE_ON_ERROR : 0);
219+
}
220+
221+
/*
222+
* Just like strbuf_realpath, but allows an arbitrary number of path
223+
* components to be missing.
224+
*/
225+
char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path,
226+
int die_on_error)
227+
{
228+
return strbuf_realpath_1(resolved, path,
229+
((die_on_error ? REALPATH_DIE_ON_ERROR : 0) |
230+
REALPATH_MANY_MISSING));
231+
}
232+
205233
char *real_pathdup(const char *path, int die_on_error)
206234
{
207235
struct strbuf realpath = STRBUF_INIT;

cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,8 @@ static inline int is_absolute_path(const char *path)
13251325
int is_directory(const char *);
13261326
char *strbuf_realpath(struct strbuf *resolved, const char *path,
13271327
int die_on_error);
1328+
char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path,
1329+
int die_on_error);
13281330
char *real_pathdup(const char *path, int die_on_error);
13291331
const char *absolute_path(const char *path);
13301332
char *absolute_pathdup(const char *path);

0 commit comments

Comments
 (0)