How to retrieve filepath relatively to a given directory in C

I'm looking for an efficient way to convert absolute filepath to a path relative to a specific directory.

Let's say we have to following structure:

D:\main\test1\blah.txt  
D:\test2\foo.txt

With "D:\main" being the reference directory, then result would be:

Any clue ?


Notes for the record:

It seems that:

Upvotes: 1

Views: 730

Answers (3)

Here is the shortest solution I could figure out.

Algorithm is actually quite simple:

Given 1) a reference path (path to which result path will be relative to); and 2) an absolute path (full path of a file) :

  • while path parts are equals : skip them
  • when we come across a difference

    1. add a ".." for each remaining part of reference path
    2. add remaining parts from absolute path

The only limitation under windows is in case of distinct volume (drive letters differ), in which situation we have no choice but to return the original absolute path.

Cross-platform C source :

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
    const char* path_separator = "\\";
#else
    const char* path_separator = "/";
#endif  

#define FILENAME_MAX 1024


char* get_relative_path(char* reference_path, char* absolute_path) {
    static char relative_path[FILENAME_MAX];
    // init result string
    relative_path[0] = '\0';    
    // check first char (under windows, if differs, we return absolute path)
    if(absolute_path[0] != reference_path[0]) {
        return absolute_path;
    }    
    // make copies to prevent altering original strings
    char* path_a = strdup(absolute_path);
    char* path_r = strdup(reference_path);

    int inc;
    int size_a = strlen(path_a)+1;
    int size_r = strlen(path_r)+1;

    for(inc = 0; inc < size_a && inc < size_r; inc += strlen(path_a+inc)+1) {
        char* token_a = strchr(path_a+inc, path_separator[0]);
        char* token_r = strchr(path_r+inc, path_separator[0]);        
        if(token_a) token_a[0] = '\0';
        if(token_r) token_r[0] = '\0';        
        if(strcmp(path_a+inc, path_r+inc) != 0) break;
    }

    for(int inc_r = inc; inc_r < size_r; inc_r += strlen(path_r+inc_r)+1) {
        strcat(relative_path, "..");
        strcat(relative_path, path_separator);        
        if( !strchr(reference_path+inc_r, path_separator[0]) ) break;
    }

    if(inc < size_a) strcat(relative_path, absolute_path+inc);

    return relative_path;
}

Upvotes: 0

jdarthenay
jdarthenay

Reputation: 3157

First thing you need to identify the file path separator as stated here.

const char kPathSeparator =
#ifdef _WIN32
                            '\\';
#else
                            '/';
#endif

Then, you need to write a function to compute a canonical absolute file path. You will have to use #ifdef _WIN32 again because there are specific windows treatment required (add current disk at the begining of path if none is present). After that, remove all . in the path, and remove all .. with their previous directory.

Once this function is written, you need to use it twice to get your origin and target canonical absolute paths, and then as explains @Weather Vane you need to identify the common part in the two paths and to add the number of .. concatenated to the end of the target canonical path.

Upvotes: -1

jdarthenay
jdarthenay

Reputation: 3157

You are giving windows paths in your example. So, if it is acceptable for you to use the WinAPI functions, you can use PathRelativePathTo.

Upvotes: 2

Related Questions