pythonic metaphor
pythonic metaphor

Reputation: 10546

C++ equivalent of 'readlink -f'

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

Answers (3)

Alexey Romanov
Alexey Romanov

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

a1291762
a1291762

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

Robby75
Robby75

Reputation: 3455

Yes, realpath is the equivalent of readlink -f.

See the man page for more information

Upvotes: 3

Related Questions