Blender V5.0
path_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9
10#include <algorithm> /* For `min/max`. */
11#include <cctype>
12#include <cstdlib>
13#include <cstring>
14
15#include "BLI_fileops.h"
16#include "BLI_fnmatch.h"
17#include "BLI_path_utils.hh"
18#include "BLI_string.h"
19#include "BLI_string_utf8.h"
20#include "BLI_string_utils.hh"
21#include "BLI_utildefines.h"
22
23#ifdef WIN32
24# include "utf_winfunc.hh"
25# include "utfconv.hh"
26# include <io.h>
27# ifdef _WIN32_IE
28# undef _WIN32_IE
29# endif
30# define _WIN32_IE 0x0501
31# include "BLI_alloca.h"
32# include "BLI_winstuff.h"
33# include <shlobj.h>
34# include <windows.h>
35#else
36# include <unistd.h>
37#endif /* WIN32 */
38
39#include "MEM_guardedalloc.h"
40
41/* Declarations. */
42
43static int BLI_path_unc_prefix_len(const char *path);
44
45#ifdef WIN32
46static bool BLI_path_is_abs_win32(const char *path);
47static int BLI_path_win32_prefix_len(const char *path);
48#endif /* WIN32 */
49
56#define FILENAME_FRAME_CHARS_MAX FILE_MAX
57
58int BLI_path_sequence_decode(const char *path,
59 char *head,
60 const size_t head_maxncpy,
61 char *tail,
62 const size_t tail_maxncpy,
63 ushort *r_digits_len)
64{
65 if (head) {
66 BLI_string_debug_size(head, head_maxncpy);
67 }
68 if (tail) {
69 BLI_string_debug_size(tail, tail_maxncpy);
70 }
71
72 uint nums = 0, nume = 0;
73 int i;
74 bool found_digit = false;
75 const char *const lslash = BLI_path_slash_rfind(path);
76 const char *const extension = BLI_path_extension_or_end(lslash ? lslash : path);
77 const uint lslash_len = lslash != nullptr ? int(lslash - path) : 0;
78 const uint name_end = uint(extension - path);
79
80 for (i = name_end - 1; i >= int(lslash_len); i--) {
81 if (isdigit(path[i])) {
82 if (found_digit) {
83 nums = i;
84 }
85 else {
86 nume = i;
87 nums = i;
88 found_digit = true;
89 }
90 }
91 else {
92 if (found_digit) {
93 break;
94 }
95 }
96 }
97
98 if (found_digit) {
99 const long long int ret = strtoll(&(path[nums]), nullptr, 10);
100 if (ret >= INT_MIN && ret <= INT_MAX) {
101 if (tail) {
102 BLI_strncpy(tail, &path[nume + 1], tail_maxncpy);
103 }
104 if (head) {
105 BLI_strncpy(head, path, std::min<size_t>(head_maxncpy, nums + 1));
106 }
107 if (r_digits_len) {
108 *r_digits_len = nume - nums + 1;
109 }
110 return int(ret);
111 }
112 }
113
114 if (tail) {
115 BLI_strncpy(tail, path + name_end, tail_maxncpy);
116 }
117 if (head) {
118 /* Name_end points to last character of head,
119 * make it +1 so null-terminator is nicely placed. */
120 BLI_strncpy(head, path, std::min<size_t>(head_maxncpy, name_end + 1));
121 }
122 if (r_digits_len) {
123 *r_digits_len = 0;
124 }
125 return 0;
126}
127
129 const size_t path_maxncpy,
130 const char *head,
131 const char *tail,
132 ushort numlen,
133 int pic)
134{
135 BLI_string_debug_size(path, path_maxncpy);
136
137 /* FIXME: MAX_ID_NAME & FILE_MAXFILE
138 *
139 * As this function directly works on a full file path (typically a FILE_MAX long char buffer),
140 * and does not perform any check on the filename part of the path, it can easily generate final
141 * paths containing a filename longer than the max supported length (FILE_MAXFILE).
142 */
143 BLI_snprintf(path, path_maxncpy, "%s%.*d%s", head, numlen, std::max(0, pic), tail);
144}
145
150static int path_normalize_impl(char *path, bool check_blend_relative_prefix)
151{
152 const char *path_orig = path;
153 int path_len = strlen(path);
154
155 /*
156 * Skip absolute prefix.
157 * ---------------------
158 */
159 if (check_blend_relative_prefix && (path[0] == '/' && path[1] == '/')) {
160 path = path + 2; /* Leave the initial `//` untouched. */
161 path_len -= 2;
162
163 /* Strip leading slashes, as they will interfere with the absolute/relative check
164 * (besides being redundant). */
165 int i = 0;
166 while (path[i] == SEP) {
167 i++;
168 }
169
170 if (i != 0) {
171 memmove(path, path + i, (path_len - i) + 1);
172 path_len -= i;
173 }
174 BLI_assert(path_len == strlen(path));
175 }
176
177#ifdef WIN32
178 /* Skip to the first slash of the drive or UNC path,
179 * so additional slashes are treated as doubles. */
180 if (path_orig == path) {
181 int path_unc_len = BLI_path_unc_prefix_len(path);
182 if (path_unc_len) {
183 path_unc_len -= 1;
184 BLI_assert(path_unc_len > 0 && path[path_unc_len] == SEP);
185 path += path_unc_len;
186 path_len -= path_unc_len;
187 }
188 else if (BLI_path_is_win32_drive(path)) { /* Check for `C:` (2 characters only). */
189 path += 2;
190 path_len -= 2;
191 }
192 }
193#endif /* WIN32 */
194 /* Works on WIN32 as well, because the drive component is skipped. */
195 const bool is_relative = path[0] && (path[0] != SEP);
196
197 /*
198 * Strip redundant path components.
199 * --------------------------------
200 */
201
202 /* NOTE(@ideasman42):
203 * `memmove(start, eind, strlen(eind) + 1);`
204 * is the same as
205 * `BLI_strncpy(start, eind, ...);`
206 * except string-copy should not be used because there is overlap,
207 * so use `memmove` 's slightly more obscure syntax. */
208
209 /* Inline replacement:
210 * - `/./` -> `/`.
211 * - `//` -> `/`.
212 * Performed until no more replacements can be made. */
213 if (path_len > 1) {
214 for (int i = path_len - 1; i > 0; i--) {
215 /* Calculate the redundant slash span (if any). */
216 if (path[i] == SEP) {
217 const int i_end = i;
218 do {
219 /* Stepping over elements assumes 'i' references a separator. */
220 BLI_assert(path[i] == SEP);
221 if (path[i - 1] == SEP) {
222 i -= 1; /* Found `//`, replace with `/`. */
223 }
224 else if (i >= 2 && path[i - 1] == '.' && path[i - 2] == SEP) {
225 i -= 2; /* Found `/./`, replace with `/`. */
226 }
227 else {
228 break;
229 }
230 } while (i > 0);
231
232 if (i < i_end) {
233 memmove(path + i, path + i_end, (path_len - i_end) + 1);
234 path_len -= i_end - i;
235 BLI_assert(strlen(path) == path_len);
236 }
237 }
238 }
239 }
240 /* Remove redundant `./` prefix as it's redundant & complicates collapsing directories. */
241 if (is_relative) {
242 if ((path_len > 2) && (path[0] == '.') && (path[1] == SEP)) {
243 memmove(path, path + 2, (path_len - 2) + 1);
244 path_len -= 2;
245 }
246 }
247
248 /*
249 * Collapse Parent Directories.
250 * ----------------------------
251 *
252 * Example: `<parent>/<child>/../` -> `<parent>/`
253 *
254 * Notes:
255 * - Leading `../` are skipped as they cannot be collapsed (see `start_base`).
256 * - Multiple parent directories are handled at once to reduce number of `memmove` calls.
257 */
258
259#define IS_PARENT_DIR(p) ((p)[0] == '.' && (p)[1] == '.' && ELEM((p)[2], SEP, '\0'))
260
261 /* First non prefix path component. */
262 char *path_first_non_slash_part = path;
263 while (*path_first_non_slash_part && *path_first_non_slash_part == SEP) {
264 path_first_non_slash_part++;
265 }
266
267 /* Maintain a pointer to the end of leading `..` component.
268 * Skip leading parent directories because logically they cannot be collapsed. */
269 char *start_base = path_first_non_slash_part;
270 while (IS_PARENT_DIR(start_base)) {
271 start_base += 3;
272 }
273
274 /* It's possible the entire path is made of up `../`,
275 * in this case there is nothing to do. */
276 if (start_base < path + path_len) {
277 /* Step over directories, always starting out on the character after the slash. */
278 char *start = start_base;
279 char *start_temp;
280 while ((start_temp = strstr(start, SEP_STR ".." SEP_STR)) ||
281 /* Check if the string ends with `/..` & assign when found, else nullptr. */
282 (start_temp = ((start <= &path[path_len - 3]) &&
283 STREQ(&path[path_len - 3], SEP_STR "..")) ?
284 &path[path_len - 3] :
285 nullptr))
286 {
287 start = start_temp + 1; /* Skip the `/`. */
288 BLI_assert(start_base != start);
289
290 /* Step `end_all` forwards (over all `..`). */
291 char *end_all = start;
292 do {
293 BLI_assert(IS_PARENT_DIR(end_all));
294 end_all += 3;
295 BLI_assert(end_all <= path + path_len + 1);
296 } while (IS_PARENT_DIR(end_all));
297
298 /* Step `start` backwards (until `end` meets `end_all` or `start` meets `start_base`). */
299 char *end = start;
300 do {
301 BLI_assert(start_base < start);
302 BLI_assert(*(start - 1) == SEP);
303 /* Step `start` backwards one. */
304 do {
305 start--;
306 } while (start_base < start && *(start - 1) != SEP);
307 BLI_assert(*start != SEP); /* Ensure the loop ran at least once. */
308 BLI_assert(!IS_PARENT_DIR(start)); /* Clamping by `start_base` prevents this. */
309 end += 3;
310 } while ((start != start_base) && (end < end_all));
311
312 if (end > path + path_len) {
313 BLI_assert(*(end - 1) == '\0');
314 end--;
315 end_all--;
316 }
317 BLI_assert(start < end && start >= start_base);
318 const size_t start_len = path_len - (end - path);
319 memmove(start, end, start_len + 1);
320 path_len -= end - start;
321 BLI_assert(strlen(path) == path_len);
322 /* Other `..` directories may have been moved to the front, step `start_base` past them. */
323 if (UNLIKELY(start == start_base && (end != end_all))) {
324 start_base += (end_all - end);
325 start = (start_base < path + path_len) ? start_base : start_base - 1;
326 }
327 }
328 }
329
330 BLI_assert(strlen(path) == path_len);
331 /* Characters before the `start_base` must *only* be `../../../` (multiples of 3). */
332 BLI_assert((start_base - path_first_non_slash_part) % 3 == 0);
333 /* All `..` ahead of `start_base` were collapsed (including trailing `/..`). */
334 BLI_assert(!(start_base < path + path_len) ||
335 (!strstr(start_base, SEP_STR ".." SEP_STR) &&
336 !(path_len >= 3 && STREQ(&path[path_len - 3], SEP_STR ".."))));
337
338 /*
339 * Final Prefix Cleanup.
340 * ---------------------
341 */
342 if (is_relative) {
343 if (path_len == 0 && (path == path_orig)) {
344 path[0] = '.';
345 path[1] = '\0';
346 path_len = 1;
347 }
348 }
349 else {
350 /* Support for odd paths: eg `/../home/me` --> `/home/me`
351 * this is a valid path in blender but we can't handle this the usual way below
352 * simply strip this prefix then evaluate the path as usual.
353 * Python's `os.path.normpath()` does this. */
354 if (start_base != path_first_non_slash_part) {
355 char *start = start_base > path + path_len ? start_base - 1 : start_base;
356 /* As long as `start` is set correctly, it should never begin with `../`
357 * as these directories are expected to be skipped. */
358 BLI_assert(!IS_PARENT_DIR(start));
359 const size_t start_len = path_len - (start - path);
360 BLI_assert(strlen(start) == start_len);
361 memmove(path_first_non_slash_part, start, start_len + 1);
362 path_len -= start - path_first_non_slash_part;
363 BLI_assert(strlen(path) == path_len);
364 }
365 }
366
367 BLI_assert(strlen(path) == path_len);
368
369#undef IS_PARENT_DIR
370
371 return (path - path_orig) + path_len;
372}
373
374int BLI_path_normalize(char *path)
375{
376 return path_normalize_impl(path, true);
377}
378
380{
381 return path_normalize_impl(path, false);
382}
383
384int BLI_path_normalize_dir(char *dir, size_t dir_maxncpy)
385{
386 /* Would just create an unexpected "/" path, just early exit entirely. */
387 if (dir[0] == '\0') {
388 return 0;
389 }
390
391 int dir_len = BLI_path_normalize(dir);
392 return BLI_path_slash_ensure_ex(dir, dir_maxncpy, dir_len);
393}
394
395int BLI_path_canonicalize_native(char *path, int path_maxncpy)
396{
397 BLI_path_abs_from_cwd(path, path_maxncpy);
398 /* As these are system level paths, only convert slashes
399 * if the alternate direction is accepted as a slash. */
402 }
403 int path_len = BLI_path_normalize_native(path);
404 /* Strip trailing slash but don't strip `/` away to nothing. */
405 if (path_len > 1 && path[path_len - 1] == SEP) {
406#ifdef WIN32
407 /* Don't strip `C:\` -> `C:` as this is no longer a valid directory. */
408 if (BLI_path_win32_prefix_len(path) + 1 < path_len)
409#endif
410 {
411 path_len -= 1;
412 path[path_len] = '\0';
413 }
414 }
415 return path_len;
416}
417
418bool BLI_path_make_safe_filename_ex(char *filename, bool allow_tokens)
419{
420#define INVALID_CHARS \
421 "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
422 "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
423 "/\\?*:|\""
424#define INVALID_TOKENS "<>"
425
426 const char *invalid = allow_tokens ? INVALID_CHARS : INVALID_CHARS INVALID_TOKENS;
427
428#undef INVALID_CHARS
429#undef INVALID_TOKENS
430
431 char *fn;
432 bool changed = false;
433
434 if (*filename == '\0') {
435 return changed;
436 }
437
438 for (fn = filename; *fn && (fn = strpbrk(fn, invalid)); fn++) {
439 *fn = '_';
440 changed = true;
441 }
442
443 /* Forbid only dots. */
444 for (fn = filename; *fn == '.'; fn++) {
445 /* Pass. */
446 }
447 if (*fn == '\0') {
448 *filename = '_';
449 changed = true;
450 }
451
452#ifdef WIN32
453 {
454 const char *invalid_names[] = {
455 "con", "prn", "aux", "null", "com1", "com2", "com3", "com4",
456 "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3",
457 "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", nullptr,
458 };
459 const size_t len = strlen(filename);
460 char *filename_lower = BLI_strdupn(filename, len);
461 const char **iname;
462
463 /* Forbid trailing dot (trailing space has already been replaced above). */
464 if (filename[len - 1] == '.') {
465 filename[len - 1] = '_';
466 changed = true;
467 }
468
469 /* Check for forbidden names - not we have to check all combination
470 * of upper and lower cases, hence the usage of filename_lower
471 * (more efficient than using #BLI_strcasestr repeatedly). */
472 BLI_str_tolower_ascii(filename_lower, len);
473 for (iname = invalid_names; *iname; iname++) {
474 if (strstr(filename_lower, *iname) == filename_lower) {
475 const size_t iname_len = strlen(*iname);
476 /* Only invalid if the whole name is made of the invalid chunk, or it has an
477 * (assumed extension) dot just after. This means it will also catch *valid*
478 * names like `aux.foo.bar`, but should be good enough for us! */
479 if ((iname_len == len) || (filename_lower[iname_len] == '.')) {
480 *filename = '_';
481 changed = true;
482 break;
483 }
484 }
485 }
486
487 MEM_freeN(filename_lower);
488 }
489#endif
490
491 return changed;
492}
493
494bool BLI_path_make_safe_filename(char *filename)
495{
496 return BLI_path_make_safe_filename_ex(filename, false);
497}
498
499bool BLI_path_make_safe(char *path)
500{
501 /* Simply apply #BLI_path_make_safe_filename() over each component of the path.
502 * Luckily enough, same *safe* rules applies to file & directory names. */
503 char *curr_slash, *curr_path = path;
504 bool changed = false;
505 bool skip_first = false;
506
507#ifdef WIN32
508 if (BLI_path_is_abs_win32(path)) {
509 /* Do not make safe `C:` in `C:\foo\bar`. */
510 skip_first = true;
511 }
512#endif
513
514 for (curr_slash = (char *)BLI_path_slash_find(curr_path); curr_slash;
515 curr_slash = (char *)BLI_path_slash_find(curr_path))
516 {
517 const char backup = *curr_slash;
518 *curr_slash = '\0';
519 if (!skip_first && (*curr_path != '\0') && BLI_path_make_safe_filename(curr_path)) {
520 changed = true;
521 }
522 skip_first = false;
523 curr_path = curr_slash + 1;
524 *curr_slash = backup;
525 }
526 if (BLI_path_make_safe_filename(curr_path)) {
527 changed = true;
528 }
529
530 return changed;
531}
532
533bool BLI_path_is_rel(const char *path)
534{
535 return path[0] == '/' && path[1] == '/';
536}
537
538bool BLI_path_is_unc(const char *path)
539{
540 return path[0] == '\\' && path[1] == '\\';
541}
542
549static int BLI_path_unc_prefix_len(const char *path)
550{
551 if (BLI_path_is_unc(path)) {
552 if ((path[2] == '?') && (path[3] == '\\')) {
553 /* We assume long UNC path like `\\?\server\share\folder` etc. */
554 return 4;
555 }
556
557 return 2;
558 }
559
560 return 0;
561}
562
563#ifdef WIN32
564static int BLI_path_win32_prefix_len(const char *path)
565{
566 if (BLI_path_is_win32_drive(path)) {
567 return 2;
568 }
569 return BLI_path_unc_prefix_len(path);
570}
571#endif
572
573bool BLI_path_is_win32_drive(const char *path)
574{
575 return isalpha(path[0]) && (path[1] == ':');
576}
577
578bool BLI_path_is_win32_drive_only(const char *path)
579{
580 return isalpha(path[0]) && (path[1] == ':') && (path[2] == '\0');
581}
582
584{
585 return isalpha(path[0]) && (path[1] == ':') && ELEM(path[2], '\\', '/');
586}
587
588#if defined(WIN32)
589
598static bool BLI_path_is_abs_win32(const char *path)
599{
600 /* Don't use the `BLI_path_is_win32_drive_with_slash`
601 * since paths such as `C:` are valid on their own. */
602 return BLI_path_is_win32_drive(path) || BLI_path_is_unc(path);
603}
604
605static wchar_t *next_slash(wchar_t *path)
606{
607 wchar_t *slash = path;
608 while (*slash && *slash != L'\\') {
609 slash++;
610 }
611 return slash;
612}
613
614/* Adds a slash if the UNC path points to a share. */
615static void BLI_path_add_slash_to_share(wchar_t *uncpath)
616{
617 wchar_t *slash_after_server = next_slash(uncpath + 2);
618 if (*slash_after_server) {
619 wchar_t *slash_after_share = next_slash(slash_after_server + 1);
620 if (!(*slash_after_share)) {
621 slash_after_share[0] = L'\\';
622 slash_after_share[1] = L'\0';
623 }
624 }
625}
626
627static void BLI_path_unc_to_short(wchar_t *unc)
628{
629 wchar_t tmp[PATH_MAX];
630
631 int len = wcslen(unc);
632 /* Convert:
633 * - `\\?\UNC\server\share\folder\...` to `\\server\share\folder\...`
634 * - `\\?\C:\` to `C:\`
635 * - `\\?\C:\folder\...` to `C:\folder\...`
636 */
637 if ((len > 3) && (unc[0] == L'\\') && (unc[1] == L'\\') && (unc[2] == L'?') &&
638 ELEM(unc[3], L'\\', L'/'))
639 {
640 if ((len > 5) && (unc[5] == L':')) {
641 wcsncpy(tmp, unc + 4, len - 4);
642 tmp[len - 4] = L'\0';
643 wcscpy(unc, tmp);
644 }
645 else if ((len > 7) && (wcsncmp(&unc[4], L"UNC", 3) == 0) && ELEM(unc[7], L'\\', L'/')) {
646 tmp[0] = L'\\';
647 tmp[1] = L'\\';
648 wcsncpy(tmp + 2, unc + 8, len - 8);
649 tmp[len - 6] = L'\0';
650 wcscpy(unc, tmp);
651 }
652 }
653}
654
655void BLI_path_normalize_unc(char *path, int path_maxncpy)
656{
657 wchar_t *tmp_16 = alloc_utf16_from_8(path, 1);
658 BLI_path_normalize_unc_16(tmp_16);
659 conv_utf_16_to_8(tmp_16, path, path_maxncpy);
660}
661
662void BLI_path_normalize_unc_16(wchar_t *path_16)
663{
664 BLI_path_unc_to_short(path_16);
665 BLI_path_add_slash_to_share(path_16);
666}
667#endif
668
669void BLI_path_rel(char path[FILE_MAX], const char *basepath)
670{
672 /* A `basepath` starting with `//` will be made relative multiple times. */
673 BLI_assert_msg(!BLI_path_is_rel(basepath), "The 'basepath' cannot start with '//'!");
674
675 const char *lslash;
676 char temp[FILE_MAX];
677
678 /* If path is already relative, bail out. */
679 if (BLI_path_is_rel(path)) {
680 return;
681 }
682
683 /* Also bail out if relative path is not set. */
684 if (basepath[0] == '\0') {
685 return;
686 }
687
688#ifdef WIN32
689 if (BLI_strnlen(basepath, 3) > 2 && !BLI_path_is_abs_win32(basepath)) {
690 char *ptemp;
691 /* Fix missing volume name in relative base,
692 * can happen with old `recent-files.txt` files. */
694 ptemp = &temp[2];
695 if (!ELEM(basepath[0], '\\', '/')) {
696 ptemp++;
697 }
698 BLI_strncpy(ptemp, basepath, FILE_MAX - 3);
699 }
700 else {
701 BLI_strncpy(temp, basepath, FILE_MAX);
702 }
703
704 if (BLI_strnlen(path, 3) > 2) {
705 bool is_unc = BLI_path_is_unc(path);
706
707 /* Ensure paths are both UNC paths or are both drives. */
708 if (BLI_path_is_unc(temp) != is_unc) {
709 return;
710 }
711
712 /* Ensure both UNC paths are on the same share. */
713 if (is_unc) {
714 int off;
715 int slash = 0;
716 for (off = 0; temp[off] && slash < 4; off++) {
717 if (temp[off] != path[off]) {
718 return;
719 }
720
721 if (temp[off] == '\\') {
722 slash++;
723 }
724 }
725 }
726 else if ((temp[1] == ':' && path[1] == ':') && (tolower(temp[0]) != tolower(path[0]))) {
727 return;
728 }
729 }
730#else
731 STRNCPY(temp, basepath);
732#endif
733
734 BLI_string_replace_char(temp + BLI_path_unc_prefix_len(temp), '\\', '/');
735 BLI_string_replace_char(path + BLI_path_unc_prefix_len(path), '\\', '/');
736
737 /* Remove `/./` which confuse the following slash counting. */
738 BLI_path_normalize(path);
739 BLI_path_normalize(temp);
740
741 /* The last slash in the path indicates where the path part ends. */
742 lslash = BLI_path_slash_rfind(temp);
743
744 if (lslash) {
745 /* Find the prefix of the filename that is equal for both filenames.
746 * This is replaced by the two slashes at the beginning. */
747 const char *p = temp;
748 const char *q = path;
749
750#ifdef WIN32
751 while (tolower(*p) == tolower(*q))
752#else
753 while (*p == *q)
754#endif
755 {
756 p++;
757 q++;
758
759 /* Don't search beyond the end of the string in the rare case they match. */
760 if ((*p == '\0') || (*q == '\0')) {
761 break;
762 }
763 }
764
765 /* We might have passed the slash when the beginning of a dir matches
766 * so we rewind. Only check on the actual filename. */
767 if (*q != '/') {
768 while ((q >= path) && (*q != '/')) {
769 q--;
770 p--;
771 }
772 }
773 else if (*p != '/') {
774 while ((p >= temp) && (*p != '/')) {
775 p--;
776 q--;
777 }
778 }
779
780 char res[FILE_MAX] = "//";
781 char *r = res + 2;
782
783 /* `p` now points to the slash that is at the beginning of the part
784 * where the path is different from the relative path.
785 * We count the number of directories we need to go up in the
786 * hierarchy to arrive at the common prefix of the path. */
787 if (p < temp) {
788 p = temp;
789 }
790 while (p && p < lslash) {
791 if (*p == '/') {
792 r += BLI_strncpy_rlen(r, "../", sizeof(res) - (r - res));
793 }
794 p++;
795 }
796
797 /* Don't copy the slash at the beginning. */
798 r += BLI_strncpy_rlen(r, q + 1, sizeof(res) - (r - res));
799 UNUSED_VARS(r);
800
801#ifdef WIN32
802 BLI_string_replace_char(res + 2, '/', '\\');
803#endif
804 BLI_strncpy(path, res, FILE_MAX);
805 }
806}
807
808bool BLI_path_suffix(char *path, size_t path_maxncpy, const char *suffix, const char *sep)
809{
810 BLI_string_debug_size_after_nil(path, path_maxncpy);
811
812 const size_t suffix_len = strlen(suffix);
813 const size_t sep_len = strlen(sep);
814 char *extension = (char *)BLI_path_extension_or_end(path);
815 const size_t extension_len = strlen(extension);
816 const size_t path_end = extension - path;
817 const size_t path_len = path_end + extension_len;
818 if (path_len + sep_len + suffix_len >= path_maxncpy) {
819 return false;
820 }
821
822 if (extension_len) {
823 memmove(extension + (sep_len + suffix_len), extension, extension_len);
824 }
825 char *c = path + path_end;
826 if (sep_len) {
827 memcpy(c, sep, sep_len);
828 c += sep_len;
829 }
830 if (suffix_len) {
831 memcpy(c, suffix, suffix_len);
832 c += suffix_len;
833 }
834 c += extension_len;
835 *c = '\0';
836 return true;
837}
838
839const char *BLI_path_parent_dir_end(const char *path, size_t path_len)
840{
841 const char *path_end = path + path_len - 1;
842 const char *p = path_end;
843 while (p >= path) {
845 break;
846 }
847 p--;
848 }
849 while (p > path) {
850 if (BLI_path_slash_is_native_compat(*(p - 1))) {
851 p -= 1; /* Skip `/`. */
852 }
853 else if ((p + 1 > path) && (*(p - 1) == '.') && BLI_path_slash_is_native_compat(*p - 2)) {
854 p -= 2; /* Skip `/.` (actually `/./` but the last slash was already skipped) */
855 }
856 else {
857 break;
858 }
859 }
860 if ((p > path) && (p != path_end)) {
861 return p;
862 }
863 return nullptr;
864}
865
866bool BLI_path_parent_dir(char *path)
867{
868 /* Use #BLI_path_name_at_index instead of checking if the strings ends with `parent_dir`
869 * to ensure the logic isn't confused by:
870 * - Directory names that happen to end with `..`.
871 * - When `path` is empty, the contents will be `../`
872 * which would cause checking for a tailing `/../` fail.
873 * Extracting the span of the final directory avoids both these issues. */
874 int tail_ofs = 0, tail_len = 0;
875 if (!BLI_path_name_at_index(path, -1, &tail_ofs, &tail_len)) {
876 return false;
877 }
878 if (tail_len == 1) {
879 /* Last path is `.`, as normalize should remove this, it's safe to assume failure.
880 * This happens when the input a single period (possibly with slashes before or after). */
881 if (path[tail_ofs] == '.') {
882 return false;
883 }
884 }
885
886 /* Input paths should already be normalized if `..` is part of the path. */
887 BLI_assert(!((tail_len == 2) && (path[tail_ofs] == '.') && (path[tail_ofs + 1] == '.')));
888 path[tail_ofs] = '\0';
889 return true;
890}
891
893{
894 bool valid_path = true;
895
896 /* Loop as long as cur path is not a dir, and we can get a parent path. */
897 while ((BLI_access(path, R_OK) != 0) && (valid_path = BLI_path_parent_dir(path))) {
898 /* Pass. */
899 }
900 return (valid_path && path[0]);
901}
902
913static bool path_frame_chars_find_range(const char *path, int *r_char_start, int *r_char_end)
914{
915 uint ch_sta, ch_end, i;
916 /* Insert current frame: `file###` -> `file001`. */
917 ch_sta = ch_end = 0;
918 for (i = 0; path[i] != '\0'; i++) {
919 if (ELEM(path[i], '\\', '/')) {
920 ch_end = 0; /* This is a directory name, don't use any hashes we found. */
921 }
922 else if (path[i] == '#') {
923 ch_sta = i;
924 ch_end = ch_sta + 1;
925 while (path[ch_end] == '#') {
926 ch_end++;
927 }
928 i = ch_end - 1; /* Keep searching. */
929
930 /* Don't break, there may be a slash after this that invalidates the previous #'s. */
931 }
932 }
933
934 if (ch_end) {
935 *r_char_start = ch_sta;
936 *r_char_end = ch_end;
937 return true;
938 }
939
940 *r_char_start = -1;
941 *r_char_end = -1;
942 return false;
943}
944
949static void ensure_digits(char *path, int digits)
950{
951 char *file = (char *)BLI_path_basename(path);
952 if (strrchr(file, '#') == nullptr) {
953 int len = strlen(file);
954
955 while (digits--) {
956 file[len++] = '#';
957 }
958 file[len] = '\0';
959 }
960}
961
962bool BLI_path_frame(char *path, size_t path_maxncpy, int frame, int digits)
963{
964 BLI_string_debug_size_after_nil(path, path_maxncpy);
965
966 int ch_sta, ch_end;
967
968 if (digits) {
969 ensure_digits(path, digits);
970 }
971
972 if (path_frame_chars_find_range(path, &ch_sta, &ch_end)) {
973 char frame_str[FILENAME_FRAME_CHARS_MAX + 1]; /* One for null. */
974 const int ch_span = std::min(ch_end - ch_sta, FILENAME_FRAME_CHARS_MAX);
975 SNPRINTF(frame_str, "%.*d", ch_span, frame);
976 BLI_string_replace_range(path, path_maxncpy, ch_sta, ch_end, frame_str);
977 return true;
978 }
979 return false;
980}
981
982bool BLI_path_frame_range(char *path, size_t path_maxncpy, int sta, int end, int digits)
983{
984 BLI_string_debug_size_after_nil(path, path_maxncpy);
985
986 int ch_sta, ch_end;
987
988 if (digits) {
989 ensure_digits(path, digits);
990 }
991
992 if (path_frame_chars_find_range(path, &ch_sta, &ch_end)) {
993 char frame_str[(FILENAME_FRAME_CHARS_MAX * 2) + 1 + 1]; /* One for null, one for the '-' */
994 const int ch_span = std::min(ch_end - ch_sta, FILENAME_FRAME_CHARS_MAX);
995 SNPRINTF(frame_str, "%.*d-%.*d", ch_span, sta, ch_span, end);
996 BLI_string_replace_range(path, path_maxncpy, ch_sta, ch_end, frame_str);
997 return true;
998 }
999 return false;
1000}
1001
1002bool BLI_path_frame_get(const char *path, int *r_frame, int *r_digits_len)
1003{
1004 if (*path == '\0') {
1005 return false;
1006 }
1007
1008 *r_digits_len = 0;
1009
1010 const char *file = BLI_path_basename(path);
1011 const char *file_ext = BLI_path_extension_or_end(file);
1012 const char *c = file_ext;
1013
1014 /* Find start of number (if there is one). */
1015 int digits_len = 0;
1016 while (c-- != file && isdigit(*c)) {
1017 digits_len++;
1018 }
1019 c++;
1020
1021 if (digits_len == 0) {
1022 return false;
1023 }
1024
1025 /* No need to trim the string, `atio` ignores non-digits. */
1026 *r_frame = atoi(c);
1027 *r_digits_len = digits_len;
1028 return true;
1029}
1030
1031void BLI_path_frame_strip(char *path, char *r_ext, const size_t ext_maxncpy)
1032{
1033 BLI_string_debug_size(r_ext, ext_maxncpy);
1034 *r_ext = '\0';
1035 if (*path == '\0') {
1036 return;
1037 }
1038
1039 char *file = (char *)BLI_path_basename(path);
1040 char *file_ext = (char *)BLI_path_extension_or_end(file);
1041 char *c = file_ext;
1042
1043 /* Find start of number (if there is one). */
1044 int digits_len = 0;
1045 while (c-- != file && isdigit(*c)) {
1046 digits_len++;
1047 }
1048 c++;
1049
1050 BLI_strncpy(r_ext, file_ext, ext_maxncpy);
1051
1052 /* Replace the number with the suffix and terminate the string. */
1053 while (digits_len--) {
1054 *c++ = '#';
1055 }
1056 *c = '\0';
1057}
1058
1059bool BLI_path_frame_check_chars(const char *path)
1060{
1061 int ch_sta_dummy, ch_end_dummy;
1062 return path_frame_chars_find_range(path, &ch_sta_dummy, &ch_end_dummy);
1063}
1064
1065void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, const char *name)
1066{
1067 BLI_string_debug_size(display_name, display_name_maxncpy);
1068
1069 /* Strip leading underscores and spaces. */
1070 int strip_offset = 0;
1071 while (ELEM(name[strip_offset], '_', ' ')) {
1072 strip_offset++;
1073 }
1074
1075 int display_name_len = BLI_strncpy_rlen(display_name, name + strip_offset, display_name_maxncpy);
1076
1077 /* Replace underscores with spaces. */
1078 BLI_string_replace_char(display_name, '_', ' ');
1079
1080 /* In rare cases file paths may not contain valid UTF8 sequences.
1081 * In this case show *something*, since it's possible stripping would remove all text.
1082 * Use a dummy character as a hint the character could not be shown. */
1083 BLI_str_utf8_invalid_substitute(display_name, display_name_len, '_');
1084
1085 BLI_path_extension_strip(display_name);
1086
1087 /* Test if string has any upper case characters. */
1088 bool all_lower = true;
1089 for (int i = 0; display_name[i]; i++) {
1090 if (isupper(display_name[i])) {
1091 all_lower = false;
1092 break;
1093 }
1094 }
1095
1096 if (all_lower) {
1097 /* For full lowercase string, use title case. */
1098 bool prevspace = true;
1099 for (int i = 0; display_name[i]; i++) {
1100 if (prevspace) {
1101 display_name[i] = toupper(display_name[i]);
1102 }
1103
1104 prevspace = isspace(display_name[i]);
1105 }
1106 }
1107}
1108
1109bool BLI_path_abs(char path[FILE_MAX], const char *basepath)
1110{
1112 /* A `basepath` starting with `//` will be made absolute multiple times. */
1113 BLI_assert_msg(!BLI_path_is_rel(basepath), "The 'basepath' cannot start with '//'!");
1114
1115 const bool wasrelative = BLI_path_is_rel(path);
1116 char tmp[FILE_MAX];
1117 char base[FILE_MAX];
1118#ifdef WIN32
1119
1120 /* Without this, an empty string converts to: `C:\` */
1121 if (*path == '\0') {
1122 return wasrelative;
1123 }
1124
1125 /* We are checking here if we have an absolute path that is not in the current `.blend` file
1126 * as a lib main - we are basically checking for the case that a UNIX root `/` is passed. */
1127 if (!wasrelative && !BLI_path_is_abs_win32(path)) {
1128 const size_t root_dir_len = 3;
1129 char *p = path;
1131 BLI_assert(strlen(tmp) == root_dir_len);
1132
1133 /* Step over the slashes at the beginning of the path. */
1134 p = (char *)BLI_path_slash_skip(p);
1135 BLI_strncpy(tmp + root_dir_len, p, sizeof(tmp) - root_dir_len);
1136 }
1137 else {
1138 STRNCPY(tmp, path);
1139 }
1140#else
1141 STRNCPY(tmp, path);
1142
1143 /* Check for loading a MS-Windows path on a POSIX system
1144 * in this case, there is no use in trying `C:/` since it
1145 * will never exist on a Unix system.
1146 *
1147 * Add a `/` prefix and lowercase the drive-letter, remove the `:`.
1148 * `C:\foo.JPG` -> `/c/foo.JPG` */
1149
1151 tmp[1] = tolower(tmp[0]); /* Replace `:` with drive-letter. */
1152 tmp[0] = '/';
1153 /* `\` the slash will be converted later. */
1154 }
1155
1156#endif
1157
1158 /* NOTE(@jesterKing): push slashes into unix mode - strings entering this part are
1159 * potentially messed up: having both back- and forward slashes.
1160 * Here we push into one conform direction, and at the end we
1161 * push them into the system specific dir. This ensures uniformity
1162 * of paths and solving some problems (and prevent potential future ones).
1163 *
1164 * NOTE(@elubie): For UNC paths the first characters containing the UNC prefix
1165 * shouldn't be switched as we need to distinguish them from
1166 * paths relative to the `.blend` file. */
1167 BLI_string_replace_char(tmp + BLI_path_unc_prefix_len(tmp), '\\', '/');
1168
1169 /* Paths starting with `//` will get the blend file as their base,
1170 * this isn't standard in any OS but is used in blender all over the place. */
1171 if (wasrelative) {
1172 const char *lslash;
1173 STRNCPY(base, basepath);
1174
1175 /* File component is ignored, so don't bother with the trailing slash. */
1176 BLI_path_normalize(base);
1177 lslash = BLI_path_slash_rfind(base);
1178 BLI_string_replace_char(base + BLI_path_unc_prefix_len(base), '\\', '/');
1179
1180 if (lslash) {
1181 /* Length up to and including last `/`. */
1182 const int baselen = int(lslash - base) + 1;
1183 /* Use path for temp storage here, we copy back over it right away. */
1184 BLI_strncpy(path, tmp + 2, FILE_MAX); /* Strip `//` prefix. */
1185
1186 memcpy(tmp, base, baselen); /* Prefix with base up to last `/`. */
1187 BLI_strncpy(tmp + baselen, path, sizeof(tmp) - baselen); /* Append path after `//`. */
1188 BLI_strncpy(path, tmp, FILE_MAX); /* Return as result. */
1189 }
1190 else {
1191 /* Base doesn't seem to be a directory, ignore it and just strip `//` prefix on path. */
1192 BLI_strncpy(path, tmp + 2, FILE_MAX);
1193 }
1194 }
1195 else {
1196 /* Base ignored. */
1197 BLI_strncpy(path, tmp, FILE_MAX);
1198 }
1199
1200#ifdef WIN32
1201 /* NOTE(@jesterking): Skip first two chars, which in case of absolute path will
1202 * be `drive:/blabla` and in case of `relpath` `//blabla/`.
1203 * So `relpath` `//` will be retained, rest will be nice and shiny WIN32 backward slashes. */
1204 BLI_string_replace_char(path + 2, '/', '\\');
1205#endif
1206
1207 /* Ensure this is after correcting for path switch. */
1208 BLI_path_normalize(path);
1209
1210 return wasrelative;
1211}
1212
1213bool BLI_path_is_abs_from_cwd(const char *path)
1214{
1215 bool is_abs = false;
1216
1217#ifdef WIN32
1218 if (BLI_path_is_abs_win32(path)) {
1219 is_abs = true;
1220 }
1221#else
1222 if (path[0] == '/') {
1223 is_abs = true;
1224 }
1225#endif
1226 return is_abs;
1227}
1228
1229bool BLI_path_abs_from_cwd(char *path, const size_t path_maxncpy)
1230{
1231 BLI_string_debug_size_after_nil(path, path_maxncpy);
1232
1233 if (!BLI_path_is_abs_from_cwd(path)) {
1234 char cwd[PATH_MAX];
1235 /* In case the full path to the blend isn't used. */
1236 if (BLI_current_working_dir(cwd, sizeof(cwd))) {
1237 char origpath[PATH_MAX];
1238 STRNCPY(origpath, path);
1239 BLI_path_join(path, path_maxncpy, cwd, origpath);
1240 }
1241 else {
1242 printf("Could not get the current working directory - $PWD for an unknown reason.\n");
1243 }
1244 return true;
1245 }
1246
1247 return false;
1248}
1249
1250#ifdef _WIN32
1256bool BLI_path_program_extensions_add_win32(char *program_name, const size_t program_name_maxncpy)
1257{
1258 bool retval = false;
1259 int type;
1260
1261 type = BLI_exists(program_name);
1262 if ((type == 0) || S_ISDIR(type)) {
1263 /* Typically 3-5, ".EXE", ".BAT"... etc. */
1264 const int ext_max = 12;
1265 const char *ext = BLI_getenv("PATHEXT");
1266 if (ext) {
1267 const int program_name_len = strlen(program_name);
1268 char *filename = static_cast<char *>(alloca(program_name_len + ext_max));
1269 char *filename_ext;
1270 const char *ext_next;
1271
1272 /* Null terminated in the loop. */
1273 memcpy(filename, program_name, program_name_len);
1274 filename_ext = filename + program_name_len;
1275
1276 do {
1277 int ext_len;
1278 ext_next = strchr(ext, ';');
1279 ext_len = ext_next ? ((ext_next++) - ext) : strlen(ext);
1280
1281 if (LIKELY(ext_len < ext_max)) {
1282 memcpy(filename_ext, ext, ext_len);
1283 filename_ext[ext_len] = '\0';
1284
1285 type = BLI_exists(filename);
1286 if (type && (!S_ISDIR(type))) {
1287 retval = true;
1288 BLI_strncpy(program_name, filename, program_name_maxncpy);
1289 break;
1290 }
1291 }
1292 } while ((ext = ext_next));
1293 }
1294 }
1295 else {
1296 retval = true;
1297 }
1298
1299 return retval;
1300}
1301#endif /* WIN32 */
1302
1304 const size_t program_filepath_maxncpy,
1305 const char *program_name)
1306{
1307 BLI_string_debug_size(program_filepath, program_filepath_maxncpy);
1308
1309 const char *path;
1310 bool retval = false;
1311
1312#ifdef _WIN32
1313 const char separator = ';';
1314#else
1315 const char separator = ':';
1316#endif
1317
1318 path = BLI_getenv("PATH");
1319 if (path) {
1320 char filepath_test[PATH_MAX];
1321 const char *temp;
1322
1323 do {
1324 temp = strchr(path, separator);
1325 if (temp) {
1326 memcpy(filepath_test, path, temp - path);
1327 filepath_test[temp - path] = 0;
1328 path = temp + 1;
1329 }
1330 else {
1331 STRNCPY(filepath_test, path);
1332 }
1333
1334 BLI_path_append(filepath_test, program_filepath_maxncpy, program_name);
1335 if (
1336#ifdef _WIN32
1337 BLI_path_program_extensions_add_win32(filepath_test, sizeof(filepath_test))
1338#else
1339 BLI_exists(filepath_test)
1340#endif
1341 )
1342 {
1343 BLI_strncpy(program_filepath, filepath_test, program_filepath_maxncpy);
1344 retval = true;
1345 break;
1346 }
1347 } while (temp);
1348 }
1349
1350 if (retval == false) {
1351 *program_filepath = '\0';
1352 }
1353
1354 return retval;
1355}
1356
1357void BLI_setenv(const char *env, const char *val)
1358{
1359#if (defined(_WIN32) || defined(_WIN64))
1360 /* MS-Windows. */
1361 uputenv(env, val);
1362#else
1363 /* Linux/macOS/BSD */
1364 if (val) {
1365 setenv(env, val, 1);
1366 }
1367 else {
1368 unsetenv(env);
1369 }
1370#endif
1371}
1372
1373void BLI_setenv_if_new(const char *env, const char *val)
1374{
1375 if (BLI_getenv(env) == nullptr) {
1376 BLI_setenv(env, val);
1377 }
1378}
1379
1380const char *BLI_getenv(const char *env)
1381{
1382#ifdef _MSC_VER
1383 const char *result = nullptr;
1384 /* 32767 is the maximum size of the environment variable on windows,
1385 * reserve one more character for the zero terminator. */
1386 static wchar_t buffer[32768];
1387 wchar_t *env_16 = alloc_utf16_from_8(env, 0);
1388 if (env_16) {
1389 if (GetEnvironmentVariableW(env_16, buffer, ARRAY_SIZE(buffer))) {
1390 char *res_utf8 = alloc_utf_8_from_16(buffer, 0);
1391 /* Make sure the result is valid, and will fit into our temporary storage buffer. */
1392 if (res_utf8) {
1393 if (strlen(res_utf8) + 1 < sizeof(buffer)) {
1394 /* We are re-using the utf16 buffer here, since allocating a second static buffer to
1395 * contain the UTF8 version to return would be wasteful. */
1396 memcpy(buffer, res_utf8, strlen(res_utf8) + 1);
1397 result = (const char *)buffer;
1398 }
1399 free(res_utf8);
1400 }
1401 }
1402 }
1403 return result;
1404#else
1405 return getenv(env);
1406#endif
1407}
1408
1409static bool path_extension_check_ex(const char *path,
1410 const size_t path_len,
1411 const char *ext,
1412 const size_t ext_len)
1413{
1414 BLI_assert(strlen(path) == path_len);
1415 BLI_assert(strlen(ext) == ext_len);
1416
1417 return (((path_len == 0 || ext_len == 0 || ext_len >= path_len) == 0) &&
1418 (BLI_strcasecmp(ext, path + path_len - ext_len) == 0));
1419}
1420
1421bool BLI_path_extension_check(const char *path, const char *ext)
1422{
1423 return path_extension_check_ex(path, strlen(path), ext, strlen(ext));
1424}
1425
1426bool BLI_path_extension_check_n(const char *path, ...)
1427{
1428 const size_t path_len = strlen(path);
1429
1430 va_list args;
1431 const char *ext;
1432 bool ret = false;
1433
1434 va_start(args, path);
1435
1436 while ((ext = (const char *)va_arg(args, void *))) {
1437 if (path_extension_check_ex(path, path_len, ext, strlen(ext))) {
1438 ret = true;
1439 break;
1440 }
1441 }
1442
1443 va_end(args);
1444
1445 return ret;
1446}
1447
1448bool BLI_path_extension_check_array(const char *path, const char **ext_array)
1449{
1450 const size_t path_len = strlen(path);
1451 int i = 0;
1452
1453 while (ext_array[i]) {
1454 if (path_extension_check_ex(path, path_len, ext_array[i], strlen(ext_array[i]))) {
1455 return true;
1456 }
1457
1458 i++;
1459 }
1460 return false;
1461}
1462
1463bool BLI_path_extension_check_glob(const char *path, const char *ext_fnmatch)
1464{
1465 const char *ext_step = ext_fnmatch;
1466 char pattern[16];
1467
1468 while (ext_step[0]) {
1469 const char *ext_next;
1470 size_t len_ext;
1471
1472 if ((ext_next = strchr(ext_step, ';'))) {
1473 len_ext = ext_next - ext_step + 1;
1474 BLI_strncpy(pattern, ext_step, (len_ext > sizeof(pattern)) ? sizeof(pattern) : len_ext);
1475 }
1476 else {
1477 len_ext = STRNCPY_RLEN(pattern, ext_step);
1478 }
1479
1480 if (fnmatch(pattern, path, FNM_CASEFOLD) == 0) {
1481 return true;
1482 }
1483 ext_step += len_ext;
1484 }
1485
1486 return false;
1487}
1488
1490{
1491 bool only_wildcards = false;
1492
1493 for (size_t i = strlen(ext_fnmatch); i-- > 0;) {
1494 if (ext_fnmatch[i] == ';') {
1495 /* Group separator, we truncate here if we only had wildcards so far.
1496 * Otherwise, all is sound and fine. */
1497 if (only_wildcards) {
1498 ext_fnmatch[i] = '\0';
1499 return true;
1500 }
1501 return false;
1502 }
1503 if (!ELEM(ext_fnmatch[i], '?', '*')) {
1504 /* Non-wildcard char, we can break here and consider the pattern valid. */
1505 return false;
1506 }
1507 /* So far, only wildcards in last group of the pattern. */
1508 only_wildcards = true;
1509 }
1510 /* Only one group in the pattern, so even if its only made of wildcard(s),
1511 * it is assumed valid. */
1512 return false;
1513}
1514
1515bool BLI_path_extension_replace(char *path, size_t path_maxncpy, const char *ext)
1516{
1517 BLI_string_debug_size_after_nil(path, path_maxncpy);
1518
1519 char *path_ext = (char *)BLI_path_extension_or_end(path);
1520 const size_t ext_len = strlen(ext);
1521 if ((path_ext - path) + ext_len >= path_maxncpy) {
1522 return false;
1523 }
1524
1525 memcpy(path_ext, ext, ext_len + 1);
1526 return true;
1527}
1528
1530{
1531 char *path_ext = (char *)BLI_path_extension(path);
1532 if (path_ext == nullptr) {
1533 return false;
1534 }
1535 *path_ext = '\0';
1536 return true;
1537}
1538
1539bool BLI_path_extension_ensure(char *path, size_t path_maxncpy, const char *ext)
1540{
1541 BLI_string_debug_size_after_nil(path, path_maxncpy);
1542
1543 /* First check the extension is already there.
1544 * If `path_ext` is the end of the string this is simply checking if `ext` is also empty. */
1545 const char *path_ext = BLI_path_extension_or_end(path);
1546 if (STREQ(path_ext, ext)) {
1547 return true;
1548 }
1549
1550 const size_t path_len = strlen(path);
1551 const size_t ext_len = strlen(ext);
1552 int64_t a;
1553
1554 for (a = path_len - 1; a >= 0; a--) {
1555 if (path[a] == '.') {
1556 path[a] = '\0';
1557 }
1558 else {
1559 break;
1560 }
1561 }
1562 a++;
1563
1564 if (a + ext_len >= path_maxncpy) {
1565 return false;
1566 }
1567
1568 memcpy(path + a, ext, ext_len + 1);
1569 return true;
1570}
1571
1572bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *filename)
1573{
1574 BLI_string_debug_size_after_nil(filepath, filepath_maxncpy);
1575 char *c = (char *)BLI_path_basename(filepath);
1576 const size_t filename_size = strlen(filename) + 1;
1577 if (filename_size <= filepath_maxncpy - (c - filepath)) {
1578 memcpy(c, filename, filename_size);
1579 return true;
1580 }
1581 return false;
1582}
1583
1584void BLI_path_split_dir_file(const char *filepath,
1585 char *dir,
1586 const size_t dir_maxncpy,
1587 char *file,
1588 const size_t file_maxncpy)
1589{
1590 BLI_string_debug_size(dir, dir_maxncpy);
1591 BLI_string_debug_size(file, file_maxncpy);
1592
1593 const char *basename = BLI_path_basename(filepath);
1594 if (basename != filepath) {
1595 const size_t dir_size = (basename - filepath) + 1;
1596 BLI_strncpy(dir, filepath, std::min(dir_maxncpy, dir_size));
1597 }
1598 else {
1599 dir[0] = '\0';
1600 }
1601 BLI_strncpy(file, basename, file_maxncpy);
1602}
1603
1604void BLI_path_split_dir_part(const char *filepath, char *dir, const size_t dir_maxncpy)
1605{
1606 BLI_string_debug_size(dir, dir_maxncpy);
1607 const char *basename = BLI_path_basename(filepath);
1608 if (basename != filepath) {
1609 const size_t dir_size = (basename - filepath) + 1;
1610 BLI_strncpy(dir, filepath, std::min(dir_maxncpy, dir_size));
1611 }
1612 else {
1613 dir[0] = '\0';
1614 }
1615}
1616
1617void BLI_path_split_file_part(const char *filepath, char *file, const size_t file_maxncpy)
1618{
1619 BLI_string_debug_size(file, file_maxncpy);
1620 const char *basename = BLI_path_basename(filepath);
1621 BLI_strncpy(file, basename, file_maxncpy);
1622}
1623
1624const char *BLI_path_extension_or_end(const char *filepath)
1625{
1626 /* NOTE(@ideasman42): Skip the extension when there are no preceding non-extension characters in
1627 * the file name. This ignores extensions at the beginning of a string or directly after a slash.
1628 * Only using trailing extension characters has the advantage that stripping the extension
1629 * never leads to a blank string (which can't be used as a file path).
1630 * Matches Python's `os.path.splitext`. */
1631 const char *ext = nullptr;
1632 bool has_non_ext = false;
1633 const char *c = filepath;
1634 for (; *c; c++) {
1635 switch (*c) {
1636 case '.': {
1637 if (has_non_ext) {
1638 ext = c;
1639 }
1640 break;
1641 }
1642 case SEP:
1643 case ALTSEP: {
1644 ext = nullptr;
1645 has_non_ext = false;
1646 break;
1647 }
1648 default: {
1649 has_non_ext = true;
1650 break;
1651 }
1652 }
1653 }
1654 if (ext) {
1655 return ext;
1656 }
1657 BLI_assert(*c == '\0');
1658 return c;
1659}
1660
1661const char *BLI_path_extension(const char *filepath)
1662{
1663 const char *ext = BLI_path_extension_or_end(filepath);
1664 return *ext ? ext : nullptr;
1665}
1666
1667size_t BLI_path_append(char *__restrict dst, const size_t dst_maxncpy, const char *__restrict file)
1668{
1669 /* Slash ensure uses #BLI_string_debug_size */
1670 int dst_len = BLI_path_slash_ensure(dst, dst_maxncpy);
1671 if (dst_len + 1 < dst_maxncpy) {
1672 dst_len += BLI_strncpy_rlen(dst + dst_len, file, dst_maxncpy - dst_len);
1673 }
1674 return dst_len;
1675}
1676
1677size_t BLI_path_append_dir(char *__restrict dst,
1678 const size_t dst_maxncpy,
1679 const char *__restrict dir)
1680{
1681 size_t dst_len = BLI_path_append(dst, dst_maxncpy, dir);
1682 return BLI_path_slash_ensure_ex(dst, dst_maxncpy, dst_len);
1683}
1684
1685size_t BLI_path_join_array(char *__restrict dst,
1686 const size_t dst_maxncpy,
1687 const char *path_array[],
1688 const int path_array_num)
1689{
1690 BLI_assert(path_array_num > 0);
1691 BLI_string_debug_size(dst, dst_maxncpy);
1692
1693 if (UNLIKELY(dst_maxncpy == 0)) {
1694 return 0;
1695 }
1696 const char *path = path_array[0];
1697
1698 const size_t dst_last = dst_maxncpy - 1;
1699 size_t ofs = BLI_strncpy_rlen(dst, path, dst_maxncpy);
1700
1701 if (ofs == dst_last) {
1702 return ofs;
1703 }
1704
1705#ifdef WIN32
1706 /* Special case `//` for relative paths, don't use separator #SEP
1707 * as this has a special meaning on both WIN32 & UNIX.
1708 * Without this check joining `"//", "path"`. results in `"//\path"`. */
1709 if (ofs != 0) {
1710 size_t i;
1711 for (i = 0; i < ofs; i++) {
1712 if (dst[i] != '/') {
1713 break;
1714 }
1715 }
1716 if (i == ofs) {
1717 /* All slashes, keep them as-is, and join the remaining path array. */
1718 return path_array_num > 1 ?
1720 dst + ofs, dst_maxncpy - ofs, &path_array[1], path_array_num - 1) :
1721 ofs;
1722 }
1723 }
1724#endif
1725
1726 /* Remove trailing slashes, unless there are *only* trailing slashes
1727 * (allow `//` or `//some_path` as the first argument). */
1728 bool has_trailing_slash = false;
1729 if (ofs != 0) {
1730 size_t len = ofs;
1731 while ((len != 0) && BLI_path_slash_is_native_compat(path[len - 1])) {
1732 len -= 1;
1733 }
1734
1735 if (len != 0) {
1736 ofs = len;
1737 }
1738 has_trailing_slash = (path[len] != '\0');
1739 }
1740
1741 for (int path_index = 1; path_index < path_array_num; path_index++) {
1742 path = path_array[path_index];
1743 has_trailing_slash = false;
1744 const char *path_init = path;
1745 while (BLI_path_slash_is_native_compat(path[0])) {
1746 path++;
1747 }
1748 size_t len = strlen(path);
1749 if (len != 0) {
1750 while ((len != 0) && BLI_path_slash_is_native_compat(path[len - 1])) {
1751 len -= 1;
1752 }
1753
1754 if (len != 0) {
1755 /* The very first path may have a slash at the end. */
1756 if (ofs && !BLI_path_slash_is_native_compat(dst[ofs - 1])) {
1757 dst[ofs++] = SEP;
1758 if (ofs == dst_last) {
1759 break;
1760 }
1761 }
1762 has_trailing_slash = (path[len] != '\0');
1763 if (ofs + len >= dst_last) {
1764 len = dst_last - ofs;
1765 }
1766 memcpy(&dst[ofs], path, len);
1767 ofs += len;
1768 if (ofs == dst_last) {
1769 break;
1770 }
1771 }
1772 }
1773 else {
1774 has_trailing_slash = (path_init != path);
1775 }
1776 }
1777
1778 if (has_trailing_slash) {
1779 if ((ofs != dst_last) && (ofs != 0) && !BLI_path_slash_is_native_compat(dst[ofs - 1])) {
1780 dst[ofs++] = SEP;
1781 }
1782 }
1783
1784 BLI_assert(ofs <= dst_last);
1785 dst[ofs] = '\0';
1786
1787 return ofs;
1788}
1789
1790const char *BLI_path_basename(const char *path)
1791{
1792 const char *const filename = BLI_path_slash_rfind(path);
1793 return filename ? filename + 1 : path;
1794}
1795
1796static bool path_name_at_index_forward(const char *__restrict path,
1797 const int index,
1798 int *__restrict r_offset,
1799 int *__restrict r_len)
1800{
1801 BLI_assert(index >= 0);
1802 int index_step = 0;
1803 int prev = -1;
1804 int i = 0;
1805 while (true) {
1806 const char c = path[i];
1807 if ((c == '\0') || BLI_path_slash_is_native_compat(c)) {
1808 if (prev + 1 != i) {
1809 prev += 1;
1810 /* Skip `/./` (behave as if they don't exist). */
1811 if (!((i - prev == 1) && (prev != 0) && (path[prev] == '.'))) {
1812 if (index_step == index) {
1813 *r_offset = prev;
1814 *r_len = i - prev;
1815 return true;
1816 }
1817 index_step += 1;
1818 }
1819 }
1820 if (c == '\0') {
1821 break;
1822 }
1823 prev = i;
1824 }
1825 i += 1;
1826 }
1827 return false;
1828}
1829
1830static bool path_name_at_index_backward(const char *__restrict path,
1831 const int index,
1832 int *__restrict r_offset,
1833 int *__restrict r_len)
1834{
1835 /* Negative number, reverse where -1 is the last element. */
1836 BLI_assert(index < 0);
1837 int index_step = -1;
1838 int prev = strlen(path);
1839 int i = prev - 1;
1840 while (true) {
1841 const char c = i >= 0 ? path[i] : '\0';
1842 if ((c == '\0') || BLI_path_slash_is_native_compat(c)) {
1843 if (prev - 1 != i) {
1844 i += 1;
1845 /* Skip `/./` (behave as if they don't exist). */
1846 if (!((prev - i == 1) && (i != 0) && (path[i] == '.'))) {
1847 if (index_step == index) {
1848 *r_offset = i;
1849 *r_len = prev - i;
1850 return true;
1851 }
1852 index_step -= 1;
1853 }
1854 }
1855 if (c == '\0') {
1856 break;
1857 }
1858 prev = i;
1859 }
1860 i -= 1;
1861 }
1862 return false;
1863}
1864
1865bool BLI_path_name_at_index(const char *__restrict path,
1866 const int index,
1867 int *__restrict r_offset,
1868 int *__restrict r_len)
1869{
1870 return (index >= 0) ? path_name_at_index_forward(path, index, r_offset, r_len) :
1871 path_name_at_index_backward(path, index, r_offset, r_len);
1872}
1873
1874bool BLI_path_contains(const char *container_path, const char *containee_path)
1875{
1876 char container_native[PATH_MAX];
1877 char containee_native[PATH_MAX];
1878
1879 /* Keep space for a trailing slash. If the path is truncated by this, the containee path is
1880 * longer than #PATH_MAX and the result is ill-defined. */
1881 BLI_strncpy(container_native, container_path, PATH_MAX - 1);
1882 STRNCPY(containee_native, containee_path);
1883
1884 BLI_path_slash_native(container_native);
1885 BLI_path_slash_native(containee_native);
1886
1887 BLI_path_normalize(container_native);
1888 BLI_path_normalize(containee_native);
1889
1890#ifdef WIN32
1891 BLI_str_tolower_ascii(container_native, PATH_MAX);
1892 BLI_str_tolower_ascii(containee_native, PATH_MAX);
1893#endif
1894
1895 if (STREQ(container_native, containee_native)) {
1896 /* The paths are equal, they contain each other. */
1897 return true;
1898 }
1899
1900 /* Add a trailing slash to prevent same-prefix directories from matching.
1901 * e.g. `/some/path` doesn't contain `/some/path_lib`. */
1902 BLI_path_slash_ensure(container_native, sizeof(container_native));
1903
1904 return BLI_str_startswith(containee_native, container_native);
1905}
1906
1907const char *BLI_path_slash_find(const char *path)
1908{
1909 const char *const ffslash = strchr(path, '/');
1910 const char *const fbslash = strchr(path, '\\');
1911
1912 if (!ffslash) {
1913 return fbslash;
1914 }
1915 if (!fbslash) {
1916 return ffslash;
1917 }
1918
1919 return (ffslash < fbslash) ? ffslash : fbslash;
1920}
1921
1922const char *BLI_path_slash_rfind(const char *path)
1923{
1924 const char *const lfslash = strrchr(path, '/');
1925 const char *const lbslash = strrchr(path, '\\');
1926
1927 if (!lfslash) {
1928 return lbslash;
1929 }
1930 if (!lbslash) {
1931 return lfslash;
1932 }
1933
1934 return (lfslash > lbslash) ? lfslash : lbslash;
1935}
1936
1937int BLI_path_slash_ensure_ex(char *path, size_t path_maxncpy, size_t path_len)
1938{
1939 BLI_string_debug_size_after_nil(path, path_maxncpy);
1940 BLI_assert(strlen(path) == path_len);
1941 BLI_assert(path_len < path_maxncpy);
1942 if (path_len == 0 || !BLI_path_slash_is_native_compat(path[path_len - 1])) {
1943 /* Avoid unlikely buffer overflow. */
1944 if (path_len + 1 < path_maxncpy) {
1945 path[path_len++] = SEP;
1946 path[path_len] = '\0';
1947 }
1948 }
1949 return path_len;
1950}
1951
1952int BLI_path_slash_ensure(char *path, size_t path_maxncpy)
1953{
1954 return BLI_path_slash_ensure_ex(path, path_maxncpy, strlen(path));
1955}
1956
1958{
1959 int len = strlen(path);
1960 while (len) {
1961 if (BLI_path_slash_is_native_compat(path[len - 1])) {
1962 path[len - 1] = '\0';
1963 len--;
1964 }
1965 else {
1966 break;
1967 }
1968 }
1969}
1970
1971const char *BLI_path_slash_skip(const char *path)
1972{
1973 /* This accounts for a null byte too. */
1974 while (BLI_path_slash_is_native_compat(*path)) {
1975 path++;
1976 }
1977 return path;
1978}
1979
1981{
1982#ifdef WIN32
1983 if (path && BLI_strnlen(path, 3) > 2) {
1985 }
1986#else
1988#endif
1989}
1990
1991int BLI_path_cmp_normalized(const char *p1, const char *p2)
1992{
1993 BLI_assert_msg(!BLI_path_is_rel(p1) && !BLI_path_is_rel(p2), "Paths arguments must be absolute");
1994
1995 /* Normalize the paths so we can compare them. */
1996 char norm_p1_buf[256];
1997 char norm_p2_buf[256];
1998
1999 const size_t p1_size = strlen(p1) + 1;
2000 const size_t p2_size = strlen(p2) + 1;
2001
2002 char *norm_p1 = (p1_size <= sizeof(norm_p1_buf)) ? norm_p1_buf :
2003 MEM_calloc_arrayN<char>(p1_size, __func__);
2004 char *norm_p2 = (p2_size <= sizeof(norm_p2_buf)) ? norm_p2_buf :
2005 MEM_calloc_arrayN<char>(p2_size, __func__);
2006
2007 memcpy(norm_p1, p1, p1_size);
2008 memcpy(norm_p2, p2, p2_size);
2009
2010 BLI_path_slash_native(norm_p1);
2011 BLI_path_slash_native(norm_p2);
2012
2013 /* One of the paths ending with a slash does not make them different, strip both. */
2014 BLI_path_slash_rstrip(norm_p1);
2015 BLI_path_slash_rstrip(norm_p2);
2016
2017 BLI_path_normalize(norm_p1);
2018 BLI_path_normalize(norm_p2);
2019
2020 const int result = BLI_path_cmp(norm_p1, norm_p2);
2021
2022 if (norm_p1 != norm_p1_buf) {
2023 MEM_freeN(norm_p1);
2024 }
2025 if (norm_p2 != norm_p2_buf) {
2026 MEM_freeN(norm_p2);
2027 }
2028 return result;
2029}
2030
2031bool BLI_path_has_hidden_component(const char *path)
2032{
2033 bool component_start = true;
2034 char cur_char = path[0];
2035 char prev_char = '\0';
2036
2037 while (cur_char != '\0') {
2038 char next_char = path[1];
2039 /* If we're at a start of path component, current is '.'
2040 * and next one is not '.', end or separator: hidden. */
2041 if (component_start && cur_char == '.') {
2042 if (!ELEM(path[1], '.', '\0', '/', '\\')) {
2043 return true;
2044 }
2045 }
2046
2047 component_start = ELEM(cur_char, '/', '\\');
2048 /* Separator, and previous was tilde: hidden. */
2049 if (component_start && prev_char == '~') {
2050 return true;
2051 }
2052
2053 path++;
2054 prev_char = cur_char;
2055 cur_char = next_char;
2056 }
2057
2058 /* Was a tilde right at end of path: hidden. */
2059 if (prev_char == '~') {
2060 return true;
2061 }
2062
2063 /* Nothing was hidden. */
2064 return false;
2065}
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:360
char * BLI_current_working_dir(char *dir, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:81
int BLI_access(const char *filepath, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define PATH_MAX
Definition BLI_fileops.h:26
void BLI_kdtree_nd_ free(KDTree *tree)
#define ALTSEP
#define FILE_MAX
#define BLI_path_join(...)
#define SEP
BLI_INLINE bool BLI_path_slash_is_native_compat(const char ch)
#define BLI_path_cmp
int bool BLI_str_startswith(const char *__restrict str, const char *__restrict start) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
#define STRNCPY_RLEN(dst, src)
Definition BLI_string.h:603
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:604
int char char int int int int size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:913
#define BLI_string_debug_size_after_nil(str, str_maxncpy)
Definition BLI_string.h:681
#define BLI_string_debug_size(str, str_maxncpy)
Definition BLI_string.h:676
int char char int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
void BLI_str_tolower_ascii(char *str, size_t len) ATTR_NONNULL(1)
Definition string.cc:956
char char size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
int BLI_str_utf8_invalid_substitute(char *str, size_t str_len, const char substitute) ATTR_NONNULL(1)
void BLI_string_replace_char(char *str, char src, char dst) ATTR_NONNULL(1)
size_t BLI_string_replace_range(char *string, size_t string_maxncpy, int src_beg, int src_end, const char *dst)
unsigned int uint
unsigned short ushort
#define ARRAY_SIZE(arr)
#define UNUSED_VARS(...)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
#define LIKELY(x)
Compatibility-like things for windows.
void BLI_windows_get_default_root_dir(char root_dir[4])
#define S_ISDIR(x)
Read Guarded memory(de)allocation.
char program_filepath[FILE_MAX]
Definition appdir.cc:66
long long int int64_t
#define printf(...)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define L
void path_init(const string &path, const string &user_path)
Definition path.cpp:324
bool BLI_path_extension_check(const char *path, const char *ext)
size_t BLI_path_join_array(char *__restrict dst, const size_t dst_maxncpy, const char *path_array[], const int path_array_num)
bool BLI_path_is_unc(const char *path)
void BLI_path_slash_rstrip(char *path)
bool BLI_path_make_safe(char *path)
#define INVALID_CHARS
bool BLI_path_abs(char path[FILE_MAX], const char *basepath)
void BLI_path_split_dir_part(const char *filepath, char *dir, const size_t dir_maxncpy)
void BLI_path_sequence_encode(char *path, const size_t path_maxncpy, const char *head, const char *tail, ushort numlen, int pic)
const char * BLI_path_parent_dir_end(const char *path, size_t path_len)
void BLI_path_slash_native(char *path)
void BLI_path_frame_strip(char *path, char *r_ext, const size_t ext_maxncpy)
bool BLI_path_parent_dir(char *path)
bool BLI_path_suffix(char *path, size_t path_maxncpy, const char *suffix, const char *sep)
static bool path_name_at_index_forward(const char *__restrict path, const int index, int *__restrict r_offset, int *__restrict r_len)
bool BLI_path_extension_ensure(char *path, size_t path_maxncpy, const char *ext)
bool BLI_path_is_abs_from_cwd(const char *path)
const char * BLI_path_slash_skip(const char *path)
static bool path_frame_chars_find_range(const char *path, int *r_char_start, int *r_char_end)
const char * BLI_getenv(const char *env)
bool BLI_path_extension_check_array(const char *path, const char **ext_array)
int BLI_path_cmp_normalized(const char *p1, const char *p2)
static int path_normalize_impl(char *path, bool check_blend_relative_prefix)
bool BLI_path_is_win32_drive(const char *path)
size_t BLI_path_append_dir(char *__restrict dst, const size_t dst_maxncpy, const char *__restrict dir)
bool BLI_path_extension_strip(char *path)
#define FILENAME_FRAME_CHARS_MAX
Definition path_utils.cc:56
int BLI_path_sequence_decode(const char *path, char *head, const size_t head_maxncpy, char *tail, const size_t tail_maxncpy, ushort *r_digits_len)
Definition path_utils.cc:58
bool BLI_path_contains(const char *container_path, const char *containee_path)
bool BLI_path_extension_replace(char *path, size_t path_maxncpy, const char *ext)
bool BLI_path_parent_dir_until_exists(char *path)
int BLI_path_canonicalize_native(char *path, int path_maxncpy)
const char * BLI_path_extension_or_end(const char *filepath)
static bool path_extension_check_ex(const char *path, const size_t path_len, const char *ext, const size_t ext_len)
size_t BLI_path_append(char *__restrict dst, const size_t dst_maxncpy, const char *__restrict file)
bool BLI_path_abs_from_cwd(char *path, const size_t path_maxncpy)
const char * BLI_path_slash_find(const char *path)
bool BLI_path_frame_check_chars(const char *path)
bool BLI_path_make_safe_filename_ex(char *filename, bool allow_tokens)
int BLI_path_slash_ensure(char *path, size_t path_maxncpy)
bool BLI_path_extension_glob_validate(char *ext_fnmatch)
bool BLI_path_extension_check_glob(const char *path, const char *ext_fnmatch)
const char * BLI_path_extension(const char *filepath)
static void ensure_digits(char *path, int digits)
bool BLI_path_is_rel(const char *path)
static int BLI_path_unc_prefix_len(const char *path)
#define IS_PARENT_DIR(p)
void BLI_path_rel(char path[FILE_MAX], const char *basepath)
void BLI_path_split_dir_file(const char *filepath, char *dir, const size_t dir_maxncpy, char *file, const size_t file_maxncpy)
bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *filename)
int BLI_path_slash_ensure_ex(char *path, size_t path_maxncpy, size_t path_len)
bool BLI_path_is_win32_drive_only(const char *path)
int BLI_path_normalize_native(char *path)
void BLI_path_split_file_part(const char *filepath, char *file, const size_t file_maxncpy)
void BLI_setenv(const char *env, const char *val)
void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, const char *name)
void BLI_setenv_if_new(const char *env, const char *val)
bool BLI_path_make_safe_filename(char *filename)
bool BLI_path_is_win32_drive_with_slash(const char *path)
bool BLI_path_program_search(char *program_filepath, const size_t program_filepath_maxncpy, const char *program_name)
static bool path_name_at_index_backward(const char *__restrict path, const int index, int *__restrict r_offset, int *__restrict r_len)
bool BLI_path_has_hidden_component(const char *path)
bool BLI_path_frame(char *path, size_t path_maxncpy, int frame, int digits)
bool BLI_path_name_at_index(const char *__restrict path, const int index, int *__restrict r_offset, int *__restrict r_len)
int BLI_path_normalize(char *path)
bool BLI_path_frame_range(char *path, size_t path_maxncpy, int sta, int end, int digits)
#define INVALID_TOKENS
const char * BLI_path_slash_rfind(const char *path)
bool BLI_path_extension_check_n(const char *path,...)
bool BLI_path_frame_get(const char *path, int *r_frame, int *r_digits_len)
int BLI_path_normalize_dir(char *dir, size_t dir_maxncpy)
const char * BLI_path_basename(const char *path)
const char * name
return ret
i
Definition text_draw.cc:230
#define SEP_STR
Definition unit.cc:39
int uputenv(const char *name, const char *value)
wchar_t * alloc_utf16_from_8(const char *in8, size_t add)
Definition utfconv.cc:294
char * alloc_utf_8_from_16(const wchar_t *in16, size_t add)
Definition utfconv.cc:282
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8)
Definition utfconv.cc:116
uint len