Reputation: 10546
In unistd.h
, I have the C function readlink
, which follows a link down one target. The coreutil readlink
has the great -f
option to follow every sym link recursively. How can I get this behavior in C++? A boost library, a posix function I don't know about, etc?
** Edit ** was just looking at the man page for realpath
. Is this giving the same 'canonical' expansion of readlink -f
?
Upvotes: 4
Views: 1734
Reputation: 170745
In C++17 you can use std::filesystem::canonical
(or weakly_canonical
if the file doesn't have to exist). It is also cross-platform. If you can't use C++17 yet, you may still be able to use the std::filesystem::experimental
version.
Upvotes: 0
Reputation: 336
I needed to do exactly this today and the suggestion to use realpath() failed because the symlink was relative! If you are using glibc, consider canonicalize_file_name (https://man7.org/linux/man-pages/man3/canonicalize_file_name.3.html).
Of course, I found that after I wrote this code. I don't know how cross-platform this is. I wouldn't recommend it if canonicalize_file_name is available for you, but it might be a good starting point :)
char *readlink_f(const char *path) {
struct stat sb;
char *linkname = NULL;
ssize_t r;
int ret = -1;
char *ptmp = NULL;
char *dir; // not allocated
char *relpath = NULL;
char *abspath = NULL;
/* create enough space to read the link into */
if (lstat(path, &sb) == -1) {
fprintf(stderr, "failed to lstat the path\n");
goto error;
}
linkname = malloc(sb.st_size + 1);
if (linkname == NULL) {
fprintf(stderr, "insufficient memory\n");
goto error;
}
r = readlink(path, linkname, sb.st_size + 1);
if (r < 0) {
fprintf(stderr, "failed to readlink the path\n");
goto error;
}
if (r > sb.st_size) {
fprintf(stderr, "symlink increased in size between lstat() and readlink()\n");
goto error;
}
linkname[sb.st_size] = '\0';
if (linkname[0] != '/') {
/* realpath fails with relative symlinks */
ptmp = strdup(path); /* dirname modifies its argument */
dir = dirname(ptmp);
if (asprintf(&relpath, "%s/%s", dir, linkname) == -1) {
fprintf(stderr, "failed to get generate absolute path\n");
goto error;
}
} else {
relpath = strdup(linkname);
}
/* canonicalize the path */
abspath = realpath(relpath, NULL);
if (!abspath) {
fprintf(stderr, "failed to get canonical path\n");
goto error;
}
goto cleanup;
error:
free(abspath);
abspath = NULL;
cleanup:
free(linkname);
free(ptmp);
free(relpath);
return abspath;
}
Upvotes: 1
Reputation: 3455
Yes, realpath
is the equivalent of readlink -f
.
See the man page for more information
Upvotes: 3