Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get normalized (canonical) file path on Linux "even if the filepath is not existing on the file system"? (In a C program))

I have researched a lot on this topic but could not get anything substantial. By normalize/canonicalize I mean to remove all the "..", ".", multiple slashes etc from a file path and get a simple absolute path. e.g.

"/rootdir/dir1/dir2/dir3/../././././dir4//////////" to "/rootdir/dir1/dir2/dir4"

On windows I have GetFullPathName() and I can get the canonical filepath name, but for Linux I cannot find any such API which can do the same work for me, realpath() is there, but even realpath() needs the filepath to be present on the file system to be able to output normalized path, e.g. if the path /rootdir/dir1/dir2/dir4 is not on file system - realpath() will throw error on the above specified complex filepath input. Is there any way by which one could get the normalized file path even if it is not existing on the file system?

like image 388
Yogesh Avatar asked Oct 12 '15 10:10

Yogesh


2 Answers

realpath(3) does not resolve missing filenames.
But GNU core utilities (https://www.gnu.org/software/coreutils/) have a program realpath(1) which is similar to realpath(3) function, but have option:
-m, --canonicalize-missing no components of the path need exist
And your task can be done by canonicalize_filename_mode() function from file lib/canonicalize.c of the coreutils source.

like image 153
y_ug Avatar answered Sep 27 '22 17:09

y_ug


canonicalize_filename_mode() from Gnulib is a great option but cannot be used in commercial software (GPL License)

We use the following implementation that depends on cwalk library:

#define _GNU_SOURCE

#include <unistd.h>
#include <stdlib.h>

#include "cwalk.h"

/* extended version of canonicalize_file_name(3) that can handle non existing paths*/
static char *canonicalize_file_name_missing(const char *path) {
    char *resolved_path = canonicalize_file_name(path);
    if (resolved_path != NULL) {
        return resolved_path;
    }
    /* handle missing files*/
    char *cwd = get_current_dir_name();
    if (cwd == NULL) {
        /* cannot detect current working directory */
        return NULL;
    }
    size_t resolved_path_len = cwk_path_get_absolute(cwd, path, NULL, 0);
    if (resolved_path_len == 0) {
        return NULL;
    }
    resolved_path = malloc(resolved_path_len + 1);
    cwk_path_get_absolute(cwd, path, resolved_path, resolved_path_len + 1);
    free(cwd);
    return resolved_path;
}
like image 45
Yuri Shapira Avatar answered Sep 27 '22 18:09

Yuri Shapira