diff options
Diffstat (limited to 'source/blender/blenlib/intern/path_util.c')
-rw-r--r-- | source/blender/blenlib/intern/path_util.c | 215 |
1 files changed, 144 insertions, 71 deletions
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index afe8c3cc033..2376bd82b69 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -123,7 +123,7 @@ int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort void BLI_path_sequence_encode( char *string, const char *head, const char *tail, ushort numlen, int pic) { - sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail); + BLI_sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail); } static int BLI_path_unc_prefix_len(const char *path); /* defined below in same file */ @@ -153,6 +153,19 @@ void BLI_path_normalize(const char *relabase, char *path) */ #ifdef WIN32 + + while ((start = strstr(path, "\\.\\"))) { + eind = start + strlen("\\.\\") - 1; + memmove(start, eind, strlen(eind) + 1); + } + + /* remove two consecutive backslashes, but skip the UNC prefix, + * which needs to be preserved */ + while ((start = strstr(path + BLI_path_unc_prefix_len(path), "\\\\"))) { + eind = start + strlen("\\\\") - 1; + memmove(start, eind, strlen(eind) + 1); + } + while ((start = strstr(path, "\\..\\"))) { eind = start + strlen("\\..\\") - 1; a = start - path - 1; @@ -170,18 +183,18 @@ void BLI_path_normalize(const char *relabase, char *path) } } - while ((start = strstr(path, "\\.\\"))) { - eind = start + strlen("\\.\\") - 1; +#else + + while ((start = strstr(path, "/./"))) { + eind = start + (3 - 1) /* strlen("/./") - 1 */; memmove(start, eind, strlen(eind) + 1); } - /* remove two consecutive backslashes, but skip the UNC prefix, - * which needs to be preserved */ - while ((start = strstr(path + BLI_path_unc_prefix_len(path), "\\\\"))) { - eind = start + strlen("\\\\") - 1; + while ((start = strstr(path, "//"))) { + eind = start + (2 - 1) /* strlen("//") - 1 */; memmove(start, eind, strlen(eind) + 1); } -#else + while ((start = strstr(path, "/../"))) { a = start - path - 1; if (a > 0) { @@ -206,19 +219,10 @@ void BLI_path_normalize(const char *relabase, char *path) } } - while ((start = strstr(path, "/./"))) { - eind = start + (3 - 1) /* strlen("/./") - 1 */; - memmove(start, eind, strlen(eind) + 1); - } - - while ((start = strstr(path, "//"))) { - eind = start + (2 - 1) /* strlen("//") - 1 */; - memmove(start, eind, strlen(eind) + 1); - } #endif } -void BLI_path_normalize_dir(const char *relabase, char *dir) +void BLI_path_normalize_dir(const char *relabase, char *dir, size_t dir_maxlen) { /* Would just create an unexpected "/" path, just early exit entirely. */ if (dir[0] == '\0') { @@ -226,7 +230,7 @@ void BLI_path_normalize_dir(const char *relabase, char *dir) } BLI_path_normalize(relabase, dir); - BLI_path_slash_ensure(dir); + BLI_path_slash_ensure(dir, dir_maxlen); } bool BLI_filename_make_safe_ex(char *fname, bool allow_tokens) @@ -616,24 +620,34 @@ bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char } BLI_strncpy(extension, string + a, sizeof(extension)); - sprintf(string + a, "%s%s%s", sep, suffix, extension); + BLI_sprintf(string + a, "%s%s%s", sep, suffix, extension); return true; } bool BLI_path_parent_dir(char *path) { - const char parent_dir[] = {'.', '.', SEP, '\0'}; /* "../" or "..\\" */ - char tmp[FILE_MAX + 4]; - - BLI_path_join(tmp, sizeof(tmp), path, parent_dir); - BLI_path_normalize(NULL, tmp); /* does all the work of normalizing the path for us */ - - if (!BLI_path_extension_check(tmp, parent_dir)) { - strcpy(path, tmp); /* We assume the parent directory is always shorter. */ - return true; + /* Use #BLI_path_name_at_index instead of checking if the strings ends with `parent_dir` + * to ensure the logic isn't confused by: + * - Directory names that happen to end with `..`. + * - When `path` is empty, the contents will be `../` + * which would cause checking for a tailing `/../` fail. + * Extracting the span of the final directory avoids both these issues. */ + int tail_ofs = 0, tail_len = 0; + if (!BLI_path_name_at_index(path, -1, &tail_ofs, &tail_len)) { + return false; + } + if (tail_len == 1) { + /* Last path is ".", as normalize should remove this, it's safe to assume failure. + * This happens when the input a single period (possibly with slashes before or after). */ + if (path[tail_ofs] == '.') { + return false; + } } - return false; + /* Input paths should already be normalized if `..` is part of the path. */ + BLI_assert(!((tail_len == 2) && (path[tail_ofs] == '.') && (path[tail_ofs + 1] == '.'))); + path[tail_ofs] = '\0'; + return true; } bool BLI_path_parent_dir_until_exists(char *dir) @@ -1431,21 +1445,34 @@ const char *BLI_path_extension(const char *filepath) return extension; } -void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file) +size_t BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file) { size_t dirlen = BLI_strnlen(dst, maxlen); - /* inline BLI_path_slash_ensure */ + /* Inline #BLI_path_slash_ensure. */ if ((dirlen > 0) && (dst[dirlen - 1] != SEP)) { dst[dirlen++] = SEP; dst[dirlen] = '\0'; } if (dirlen >= maxlen) { - return; /* fills the path */ + return dirlen; /* fills the path */ } - BLI_strncpy(dst + dirlen, file, maxlen - dirlen); + return dirlen + BLI_strncpy_rlen(dst + dirlen, file, maxlen - dirlen); +} + +size_t BLI_path_append_dir(char *__restrict dst, const size_t maxlen, const char *__restrict dir) +{ + size_t dirlen = BLI_path_append(dst, maxlen, dir); + if (dirlen + 1 < maxlen) { + /* Inline #BLI_path_slash_ensure. */ + if ((dirlen > 0) && (dst[dirlen - 1] != SEP)) { + dst[dirlen++] = SEP; + dst[dirlen] = '\0'; + } + } + return dirlen; } size_t BLI_path_join_array(char *__restrict dst, @@ -1469,14 +1496,36 @@ size_t BLI_path_join_array(char *__restrict dst, return ofs; } +#ifdef WIN32 + /* Special case "//" for relative paths, don't use separator #SEP + * as this has a special meaning on both WIN32 & UNIX. + * Without this check joining `"//", "path"`. results in `"//\path"`. */ + if (ofs != 0) { + size_t i; + for (i = 0; i < ofs; i++) { + if (dst[i] != '/') { + break; + } + } + if (i == ofs) { + /* All slashes, keep them as-is, and join the remaining path array. */ + return path_array_num > 1 ? + BLI_path_join_array( + dst + ofs, dst_len - ofs, &path_array[1], path_array_num - 1) : + ofs; + } + } +#endif + /* Remove trailing slashes, unless there are *only* trailing slashes * (allow `//` or `//some_path` as the first argument). */ bool has_trailing_slash = false; if (ofs != 0) { size_t len = ofs; - while ((len != 0) && ELEM(path[len - 1], SEP, ALTSEP)) { + while ((len != 0) && (path[len - 1] == SEP)) { len -= 1; } + if (len != 0) { ofs = len; } @@ -1487,18 +1536,18 @@ size_t BLI_path_join_array(char *__restrict dst, path = path_array[path_index]; has_trailing_slash = false; const char *path_init = path; - while (ELEM(path[0], SEP, ALTSEP)) { + while (path[0] == SEP) { path++; } size_t len = strlen(path); if (len != 0) { - while ((len != 0) && ELEM(path[len - 1], SEP, ALTSEP)) { + while ((len != 0) && (path[len - 1] == SEP)) { len -= 1; } if (len != 0) { /* the very first path may have a slash at the end */ - if (ofs && !ELEM(dst[ofs - 1], SEP, ALTSEP)) { + if (ofs && (dst[ofs - 1] != SEP)) { dst[ofs++] = SEP; if (ofs == dst_last) { break; @@ -1521,7 +1570,7 @@ size_t BLI_path_join_array(char *__restrict dst, } if (has_trailing_slash) { - if ((ofs != dst_last) && (ofs != 0) && (ELEM(dst[ofs - 1], SEP, ALTSEP) == 0)) { + if ((ofs != dst_last) && (ofs != 0) && (dst[ofs - 1] != SEP)) { dst[ofs++] = SEP; } } @@ -1538,53 +1587,64 @@ const char *BLI_path_basename(const char *path) return filename ? filename + 1 : path; } -bool BLI_path_name_at_index(const char *__restrict path, - const int index, - int *__restrict r_offset, - int *__restrict r_len) +static bool path_name_at_index_forward(const char *__restrict path, + const int index, + int *__restrict r_offset, + int *__restrict r_len) { - if (index >= 0) { - int index_step = 0; - int prev = -1; - int i = 0; - while (true) { - const char c = path[i]; - if (ELEM(c, SEP, ALTSEP, '\0')) { - if (prev + 1 != i) { - prev += 1; + BLI_assert(index >= 0); + int index_step = 0; + int prev = -1; + int i = 0; + while (true) { + const char c = path[i]; + if (ELEM(c, SEP, '\0')) { + if (prev + 1 != i) { + prev += 1; + /* Skip '/./' (behave as if they don't exist). */ + if (!((i - prev == 1) && (prev != 0) && (path[prev] == '.'))) { if (index_step == index) { *r_offset = prev; *r_len = i - prev; - // printf("!!! %d %d\n", start, end); return true; } index_step += 1; } - if (c == '\0') { - break; - } - prev = i; } - i += 1; + if (c == '\0') { + break; + } + prev = i; } - return false; + i += 1; } + return false; +} - /* negative number, reverse where -1 is the last element */ +static bool path_name_at_index_backward(const char *__restrict path, + const int index, + int *__restrict r_offset, + int *__restrict r_len) +{ + /* Negative number, reverse where -1 is the last element. */ + BLI_assert(index < 0); int index_step = -1; int prev = strlen(path); int i = prev - 1; while (true) { const char c = i >= 0 ? path[i] : '\0'; - if (ELEM(c, SEP, ALTSEP, '\0')) { + if (ELEM(c, SEP, '\0')) { if (prev - 1 != i) { i += 1; - if (index_step == index) { - *r_offset = i; - *r_len = prev - i; - return true; + /* Skip '/./' (behave as if they don't exist). */ + if (!((prev - i == 1) && (i != 0) && (path[i] == '.'))) { + if (index_step == index) { + *r_offset = i; + *r_len = prev - i; + return true; + } + index_step -= 1; } - index_step -= 1; } if (c == '\0') { break; @@ -1596,6 +1656,15 @@ bool BLI_path_name_at_index(const char *__restrict path, return false; } +bool BLI_path_name_at_index(const char *__restrict path, + const int index, + int *__restrict r_offset, + int *__restrict r_len) +{ + return (index >= 0) ? path_name_at_index_forward(path, index, r_offset, r_len) : + path_name_at_index_backward(path, index, r_offset, r_len); +} + bool BLI_path_contains(const char *container_path, const char *containee_path) { char container_native[PATH_MAX]; @@ -1624,7 +1693,7 @@ bool BLI_path_contains(const char *container_path, const char *containee_path) /* Add a trailing slash to prevent same-prefix directories from matching. * e.g. "/some/path" doesn't contain "/some/path_lib". */ - BLI_path_slash_ensure(container_native); + BLI_path_slash_ensure(container_native, sizeof(container_native)); return BLI_str_startswith(containee_native, container_native); } @@ -1659,13 +1728,17 @@ const char *BLI_path_slash_rfind(const char *string) return (lfslash > lbslash) ? lfslash : lbslash; } -int BLI_path_slash_ensure(char *string) +int BLI_path_slash_ensure(char *string, size_t string_maxlen) { int len = strlen(string); + BLI_assert(len < string_maxlen); if (len == 0 || string[len - 1] != SEP) { - string[len] = SEP; - string[len + 1] = '\0'; - return len + 1; + /* Avoid unlikely buffer overflow. */ + if (len + 1 < string_maxlen) { + string[len] = SEP; + string[len + 1] = '\0'; + return len + 1; + } } return len; } |