Reputation: 910
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:
this question has been asked various times for other languages (though most answers take advantage of function PathRelativePathTo):
Upvotes: 1
Views: 730
Reputation: 910
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) :
when we come across a difference
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
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
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