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