Blender V4.5
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_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 nullptr. */
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", nullptr,
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
1291bool BLI_path_program_search(char *program_filepath,
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 = nullptr;
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 /* Skip zero for both empty strings and when the environment variable isn't found. */
1378 if (GetEnvironmentVariableW(env_16, buffer, ARRAY_SIZE(buffer))) {
1379 char *res_utf8 = alloc_utf_8_from_16(buffer, 0);
1380 /* Make sure the result is valid, and will fit into our temporary storage buffer. */
1381 if (res_utf8) {
1382 if (strlen(res_utf8) + 1 < sizeof(buffer)) {
1383 /* We are re-using the utf16 buffer here, since allocating a second static buffer to
1384 * contain the UTF8 version to return would be wasteful. */
1385 memcpy(buffer, res_utf8, strlen(res_utf8) + 1);
1386 result = (const char *)buffer;
1387 }
1388 free(res_utf8);
1389 }
1390 }
1391 }
1392 return result;
1393#else
1394 return getenv(env);
1395#endif
1396}
1397
1398static bool path_extension_check_ex(const char *path,
1399 const size_t path_len,
1400 const char *ext,
1401 const size_t ext_len)
1402{
1403 BLI_assert(strlen(path) == path_len);
1404 BLI_assert(strlen(ext) == ext_len);
1405
1406 return (((path_len == 0 || ext_len == 0 || ext_len >= path_len) == 0) &&
1407 (BLI_strcasecmp(ext, path + path_len - ext_len) == 0));
1408}
1409
1410bool BLI_path_extension_check(const char *path, const char *ext)
1411{
1412 return path_extension_check_ex(path, strlen(path), ext, strlen(ext));
1413}
1414
1415bool BLI_path_extension_check_n(const char *path, ...)
1416{
1417 const size_t path_len = strlen(path);
1418
1419 va_list args;
1420 const char *ext;
1421 bool ret = false;
1422
1423 va_start(args, path);
1424
1425 while ((ext = (const char *)va_arg(args, void *))) {
1426 if (path_extension_check_ex(path, path_len, ext, strlen(ext))) {
1427 ret = true;
1428 break;
1429 }
1430 }
1431
1432 va_end(args);
1433
1434 return ret;
1435}
1436
1437bool BLI_path_extension_check_array(const char *path, const char **ext_array)
1438{
1439 const size_t path_len = strlen(path);
1440 int i = 0;
1441
1442 while (ext_array[i]) {
1443 if (path_extension_check_ex(path, path_len, ext_array[i], strlen(ext_array[i]))) {
1444 return true;
1445 }
1446
1447 i++;
1448 }
1449 return false;
1450}
1451
1452bool BLI_path_extension_check_glob(const char *path, const char *ext_fnmatch)
1453{
1454 const char *ext_step = ext_fnmatch;
1455 char pattern[16];
1456
1457 while (ext_step[0]) {
1458 const char *ext_next;
1459 size_t len_ext;
1460
1461 if ((ext_next = strchr(ext_step, ';'))) {
1462 len_ext = ext_next - ext_step + 1;
1463 BLI_strncpy(pattern, ext_step, (len_ext > sizeof(pattern)) ? sizeof(pattern) : len_ext);
1464 }
1465 else {
1466 len_ext = STRNCPY_RLEN(pattern, ext_step);
1467 }
1468
1469 if (fnmatch(pattern, path, FNM_CASEFOLD) == 0) {
1470 return true;
1471 }
1472 ext_step += len_ext;
1473 }
1474
1475 return false;
1476}
1477
1479{
1480 bool only_wildcards = false;
1481
1482 for (size_t i = strlen(ext_fnmatch); i-- > 0;) {
1483 if (ext_fnmatch[i] == ';') {
1484 /* Group separator, we truncate here if we only had wildcards so far.
1485 * Otherwise, all is sound and fine. */
1486 if (only_wildcards) {
1487 ext_fnmatch[i] = '\0';
1488 return true;
1489 }
1490 return false;
1491 }
1492 if (!ELEM(ext_fnmatch[i], '?', '*')) {
1493 /* Non-wildcard char, we can break here and consider the pattern valid. */
1494 return false;
1495 }
1496 /* So far, only wildcards in last group of the pattern. */
1497 only_wildcards = true;
1498 }
1499 /* Only one group in the pattern, so even if its only made of wildcard(s),
1500 * it is assumed valid. */
1501 return false;
1502}
1503
1504bool BLI_path_extension_replace(char *path, size_t path_maxncpy, const char *ext)
1505{
1506 BLI_string_debug_size_after_nil(path, path_maxncpy);
1507
1508 char *path_ext = (char *)BLI_path_extension_or_end(path);
1509 const size_t ext_len = strlen(ext);
1510 if ((path_ext - path) + ext_len >= path_maxncpy) {
1511 return false;
1512 }
1513
1514 memcpy(path_ext, ext, ext_len + 1);
1515 return true;
1516}
1517
1519{
1520 char *path_ext = (char *)BLI_path_extension(path);
1521 if (path_ext == nullptr) {
1522 return false;
1523 }
1524 *path_ext = '\0';
1525 return true;
1526}
1527
1528bool BLI_path_extension_ensure(char *path, size_t path_maxncpy, const char *ext)
1529{
1530 BLI_string_debug_size_after_nil(path, path_maxncpy);
1531
1532 /* First check the extension is already there.
1533 * If `path_ext` is the end of the string this is simply checking if `ext` is also empty. */
1534 const char *path_ext = BLI_path_extension_or_end(path);
1535 if (STREQ(path_ext, ext)) {
1536 return true;
1537 }
1538
1539 const size_t path_len = strlen(path);
1540 const size_t ext_len = strlen(ext);
1541 int64_t a;
1542
1543 for (a = path_len - 1; a >= 0; a--) {
1544 if (path[a] == '.') {
1545 path[a] = '\0';
1546 }
1547 else {
1548 break;
1549 }
1550 }
1551 a++;
1552
1553 if (a + ext_len >= path_maxncpy) {
1554 return false;
1555 }
1556
1557 memcpy(path + a, ext, ext_len + 1);
1558 return true;
1559}
1560
1561bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *filename)
1562{
1563 BLI_string_debug_size_after_nil(filepath, filepath_maxncpy);
1564 char *c = (char *)BLI_path_basename(filepath);
1565 const size_t filename_size = strlen(filename) + 1;
1566 if (filename_size <= filepath_maxncpy - (c - filepath)) {
1567 memcpy(c, filename, filename_size);
1568 return true;
1569 }
1570 return false;
1571}
1572
1573void BLI_path_split_dir_file(const char *filepath,
1574 char *dir,
1575 const size_t dir_maxncpy,
1576 char *file,
1577 const size_t file_maxncpy)
1578{
1579 BLI_string_debug_size(dir, dir_maxncpy);
1580 BLI_string_debug_size(file, file_maxncpy);
1581
1582 const char *basename = BLI_path_basename(filepath);
1583 if (basename != filepath) {
1584 const size_t dir_size = (basename - filepath) + 1;
1585 BLI_strncpy(dir, filepath, std::min(dir_maxncpy, dir_size));
1586 }
1587 else {
1588 dir[0] = '\0';
1589 }
1590 BLI_strncpy(file, basename, file_maxncpy);
1591}
1592
1593void BLI_path_split_dir_part(const char *filepath, char *dir, const size_t dir_maxncpy)
1594{
1595 BLI_string_debug_size(dir, dir_maxncpy);
1596 const char *basename = BLI_path_basename(filepath);
1597 if (basename != filepath) {
1598 const size_t dir_size = (basename - filepath) + 1;
1599 BLI_strncpy(dir, filepath, std::min(dir_maxncpy, dir_size));
1600 }
1601 else {
1602 dir[0] = '\0';
1603 }
1604}
1605
1606void BLI_path_split_file_part(const char *filepath, char *file, const size_t file_maxncpy)
1607{
1608 BLI_string_debug_size(file, file_maxncpy);
1609 const char *basename = BLI_path_basename(filepath);
1610 BLI_strncpy(file, basename, file_maxncpy);
1611}
1612
1613const char *BLI_path_extension_or_end(const char *filepath)
1614{
1615 /* NOTE(@ideasman42): Skip the extension when there are no preceding non-extension characters in
1616 * the file name. This ignores extensions at the beginning of a string or directly after a slash.
1617 * Only using trailing extension characters has the advantage that stripping the extension
1618 * never leads to a blank string (which can't be used as a file path).
1619 * Matches Python's `os.path.splitext`. */
1620 const char *ext = nullptr;
1621 bool has_non_ext = false;
1622 const char *c = filepath;
1623 for (; *c; c++) {
1624 switch (*c) {
1625 case '.': {
1626 if (has_non_ext) {
1627 ext = c;
1628 }
1629 break;
1630 }
1631 case SEP:
1632 case ALTSEP: {
1633 ext = nullptr;
1634 has_non_ext = false;
1635 break;
1636 }
1637 default: {
1638 has_non_ext = true;
1639 break;
1640 }
1641 }
1642 }
1643 if (ext) {
1644 return ext;
1645 }
1646 BLI_assert(*c == '\0');
1647 return c;
1648}
1649
1650const char *BLI_path_extension(const char *filepath)
1651{
1652 const char *ext = BLI_path_extension_or_end(filepath);
1653 return *ext ? ext : nullptr;
1654}
1655
1656size_t BLI_path_append(char *__restrict dst, const size_t dst_maxncpy, const char *__restrict file)
1657{
1658 /* Slash ensure uses #BLI_string_debug_size */
1659 int dst_len = BLI_path_slash_ensure(dst, dst_maxncpy);
1660 if (dst_len + 1 < dst_maxncpy) {
1661 dst_len += BLI_strncpy_rlen(dst + dst_len, file, dst_maxncpy - dst_len);
1662 }
1663 return dst_len;
1664}
1665
1666size_t BLI_path_append_dir(char *__restrict dst,
1667 const size_t dst_maxncpy,
1668 const char *__restrict dir)
1669{
1670 size_t dst_len = BLI_path_append(dst, dst_maxncpy, dir);
1671 return BLI_path_slash_ensure_ex(dst, dst_maxncpy, dst_len);
1672}
1673
1674size_t BLI_path_join_array(char *__restrict dst,
1675 const size_t dst_maxncpy,
1676 const char *path_array[],
1677 const int path_array_num)
1678{
1679 BLI_assert(path_array_num > 0);
1680 BLI_string_debug_size(dst, dst_maxncpy);
1681
1682 if (UNLIKELY(dst_maxncpy == 0)) {
1683 return 0;
1684 }
1685 const char *path = path_array[0];
1686
1687 const size_t dst_last = dst_maxncpy - 1;
1688 size_t ofs = BLI_strncpy_rlen(dst, path, dst_maxncpy);
1689
1690 if (ofs == dst_last) {
1691 return ofs;
1692 }
1693
1694#ifdef WIN32
1695 /* Special case `//` for relative paths, don't use separator #SEP
1696 * as this has a special meaning on both WIN32 & UNIX.
1697 * Without this check joining `"//", "path"`. results in `"//\path"`. */
1698 if (ofs != 0) {
1699 size_t i;
1700 for (i = 0; i < ofs; i++) {
1701 if (dst[i] != '/') {
1702 break;
1703 }
1704 }
1705 if (i == ofs) {
1706 /* All slashes, keep them as-is, and join the remaining path array. */
1707 return path_array_num > 1 ?
1709 dst + ofs, dst_maxncpy - ofs, &path_array[1], path_array_num - 1) :
1710 ofs;
1711 }
1712 }
1713#endif
1714
1715 /* Remove trailing slashes, unless there are *only* trailing slashes
1716 * (allow `//` or `//some_path` as the first argument). */
1717 bool has_trailing_slash = false;
1718 if (ofs != 0) {
1719 size_t len = ofs;
1720 while ((len != 0) && BLI_path_slash_is_native_compat(path[len - 1])) {
1721 len -= 1;
1722 }
1723
1724 if (len != 0) {
1725 ofs = len;
1726 }
1727 has_trailing_slash = (path[len] != '\0');
1728 }
1729
1730 for (int path_index = 1; path_index < path_array_num; path_index++) {
1731 path = path_array[path_index];
1732 has_trailing_slash = false;
1733 const char *path_init = path;
1734 while (BLI_path_slash_is_native_compat(path[0])) {
1735 path++;
1736 }
1737 size_t len = strlen(path);
1738 if (len != 0) {
1739 while ((len != 0) && BLI_path_slash_is_native_compat(path[len - 1])) {
1740 len -= 1;
1741 }
1742
1743 if (len != 0) {
1744 /* The very first path may have a slash at the end. */
1745 if (ofs && !BLI_path_slash_is_native_compat(dst[ofs - 1])) {
1746 dst[ofs++] = SEP;
1747 if (ofs == dst_last) {
1748 break;
1749 }
1750 }
1751 has_trailing_slash = (path[len] != '\0');
1752 if (ofs + len >= dst_last) {
1753 len = dst_last - ofs;
1754 }
1755 memcpy(&dst[ofs], path, len);
1756 ofs += len;
1757 if (ofs == dst_last) {
1758 break;
1759 }
1760 }
1761 }
1762 else {
1763 has_trailing_slash = (path_init != path);
1764 }
1765 }
1766
1767 if (has_trailing_slash) {
1768 if ((ofs != dst_last) && (ofs != 0) && !BLI_path_slash_is_native_compat(dst[ofs - 1])) {
1769 dst[ofs++] = SEP;
1770 }
1771 }
1772
1773 BLI_assert(ofs <= dst_last);
1774 dst[ofs] = '\0';
1775
1776 return ofs;
1777}
1778
1779const char *BLI_path_basename(const char *path)
1780{
1781 const char *const filename = BLI_path_slash_rfind(path);
1782 return filename ? filename + 1 : path;
1783}
1784
1785static bool path_name_at_index_forward(const char *__restrict path,
1786 const int index,
1787 int *__restrict r_offset,
1788 int *__restrict r_len)
1789{
1790 BLI_assert(index >= 0);
1791 int index_step = 0;
1792 int prev = -1;
1793 int i = 0;
1794 while (true) {
1795 const char c = path[i];
1796 if ((c == '\0') || BLI_path_slash_is_native_compat(c)) {
1797 if (prev + 1 != i) {
1798 prev += 1;
1799 /* Skip `/./` (behave as if they don't exist). */
1800 if (!((i - prev == 1) && (prev != 0) && (path[prev] == '.'))) {
1801 if (index_step == index) {
1802 *r_offset = prev;
1803 *r_len = i - prev;
1804 return true;
1805 }
1806 index_step += 1;
1807 }
1808 }
1809 if (c == '\0') {
1810 break;
1811 }
1812 prev = i;
1813 }
1814 i += 1;
1815 }
1816 return false;
1817}
1818
1819static bool path_name_at_index_backward(const char *__restrict path,
1820 const int index,
1821 int *__restrict r_offset,
1822 int *__restrict r_len)
1823{
1824 /* Negative number, reverse where -1 is the last element. */
1825 BLI_assert(index < 0);
1826 int index_step = -1;
1827 int prev = strlen(path);
1828 int i = prev - 1;
1829 while (true) {
1830 const char c = i >= 0 ? path[i] : '\0';
1831 if ((c == '\0') || BLI_path_slash_is_native_compat(c)) {
1832 if (prev - 1 != i) {
1833 i += 1;
1834 /* Skip `/./` (behave as if they don't exist). */
1835 if (!((prev - i == 1) && (i != 0) && (path[i] == '.'))) {
1836 if (index_step == index) {
1837 *r_offset = i;
1838 *r_len = prev - i;
1839 return true;
1840 }
1841 index_step -= 1;
1842 }
1843 }
1844 if (c == '\0') {
1845 break;
1846 }
1847 prev = i;
1848 }
1849 i -= 1;
1850 }
1851 return false;
1852}
1853
1854bool BLI_path_name_at_index(const char *__restrict path,
1855 const int index,
1856 int *__restrict r_offset,
1857 int *__restrict r_len)
1858{
1859 return (index >= 0) ? path_name_at_index_forward(path, index, r_offset, r_len) :
1860 path_name_at_index_backward(path, index, r_offset, r_len);
1861}
1862
1863bool BLI_path_contains(const char *container_path, const char *containee_path)
1864{
1865 char container_native[PATH_MAX];
1866 char containee_native[PATH_MAX];
1867
1868 /* Keep space for a trailing slash. If the path is truncated by this, the containee path is
1869 * longer than #PATH_MAX and the result is ill-defined. */
1870 BLI_strncpy(container_native, container_path, PATH_MAX - 1);
1871 STRNCPY(containee_native, containee_path);
1872
1873 BLI_path_slash_native(container_native);
1874 BLI_path_slash_native(containee_native);
1875
1876 BLI_path_normalize(container_native);
1877 BLI_path_normalize(containee_native);
1878
1879#ifdef WIN32
1880 BLI_str_tolower_ascii(container_native, PATH_MAX);
1881 BLI_str_tolower_ascii(containee_native, PATH_MAX);
1882#endif
1883
1884 if (STREQ(container_native, containee_native)) {
1885 /* The paths are equal, they contain each other. */
1886 return true;
1887 }
1888
1889 /* Add a trailing slash to prevent same-prefix directories from matching.
1890 * e.g. "/some/path" doesn't contain "/some/path_lib". */
1891 BLI_path_slash_ensure(container_native, sizeof(container_native));
1892
1893 return BLI_str_startswith(containee_native, container_native);
1894}
1895
1896const char *BLI_path_slash_find(const char *path)
1897{
1898 const char *const ffslash = strchr(path, '/');
1899 const char *const fbslash = strchr(path, '\\');
1900
1901 if (!ffslash) {
1902 return fbslash;
1903 }
1904 if (!fbslash) {
1905 return ffslash;
1906 }
1907
1908 return (ffslash < fbslash) ? ffslash : fbslash;
1909}
1910
1911const char *BLI_path_slash_rfind(const char *path)
1912{
1913 const char *const lfslash = strrchr(path, '/');
1914 const char *const lbslash = strrchr(path, '\\');
1915
1916 if (!lfslash) {
1917 return lbslash;
1918 }
1919 if (!lbslash) {
1920 return lfslash;
1921 }
1922
1923 return (lfslash > lbslash) ? lfslash : lbslash;
1924}
1925
1926int BLI_path_slash_ensure_ex(char *path, size_t path_maxncpy, size_t path_len)
1927{
1928 BLI_string_debug_size_after_nil(path, path_maxncpy);
1929 BLI_assert(strlen(path) == path_len);
1930 BLI_assert(path_len < path_maxncpy);
1931 if (path_len == 0 || !BLI_path_slash_is_native_compat(path[path_len - 1])) {
1932 /* Avoid unlikely buffer overflow. */
1933 if (path_len + 1 < path_maxncpy) {
1934 path[path_len++] = SEP;
1935 path[path_len] = '\0';
1936 }
1937 }
1938 return path_len;
1939}
1940
1941int BLI_path_slash_ensure(char *path, size_t path_maxncpy)
1942{
1943 return BLI_path_slash_ensure_ex(path, path_maxncpy, strlen(path));
1944}
1945
1947{
1948 int len = strlen(path);
1949 while (len) {
1950 if (BLI_path_slash_is_native_compat(path[len - 1])) {
1951 path[len - 1] = '\0';
1952 len--;
1953 }
1954 else {
1955 break;
1956 }
1957 }
1958}
1959
1960const char *BLI_path_slash_skip(const char *path)
1961{
1962 /* This accounts for a null byte too. */
1963 while (BLI_path_slash_is_native_compat(*path)) {
1964 path++;
1965 }
1966 return path;
1967}
1968
1970{
1971#ifdef WIN32
1972 if (path && BLI_strnlen(path, 3) > 2) {
1974 }
1975#else
1977#endif
1978}
1979
1980int BLI_path_cmp_normalized(const char *p1, const char *p2)
1981{
1982 BLI_assert_msg(!BLI_path_is_rel(p1) && !BLI_path_is_rel(p2), "Paths arguments must be absolute");
1983
1984 /* Normalize the paths so we can compare them. */
1985 char norm_p1_buf[256];
1986 char norm_p2_buf[256];
1987
1988 const size_t p1_size = strlen(p1) + 1;
1989 const size_t p2_size = strlen(p2) + 1;
1990
1991 char *norm_p1 = (p1_size <= sizeof(norm_p1_buf)) ? norm_p1_buf :
1992 MEM_calloc_arrayN<char>(p1_size, __func__);
1993 char *norm_p2 = (p2_size <= sizeof(norm_p2_buf)) ? norm_p2_buf :
1994 MEM_calloc_arrayN<char>(p2_size, __func__);
1995
1996 memcpy(norm_p1, p1, p1_size);
1997 memcpy(norm_p2, p2, p2_size);
1998
1999 BLI_path_slash_native(norm_p1);
2000 BLI_path_slash_native(norm_p2);
2001
2002 /* One of the paths ending with a slash does not make them different, strip both. */
2003 BLI_path_slash_rstrip(norm_p1);
2004 BLI_path_slash_rstrip(norm_p2);
2005
2006 BLI_path_normalize(norm_p1);
2007 BLI_path_normalize(norm_p2);
2008
2009 const int result = BLI_path_cmp(norm_p1, norm_p2);
2010
2011 if (norm_p1 != norm_p1_buf) {
2012 MEM_freeN(norm_p1);
2013 }
2014 if (norm_p2 != norm_p2_buf) {
2015 MEM_freeN(norm_p2);
2016 }
2017 return result;
2018}
2019
2020bool BLI_path_has_hidden_component(const char *path)
2021{
2022 bool component_start = true;
2023 char cur_char = path[0];
2024 char prev_char = '\0';
2025
2026 while (cur_char != '\0') {
2027 char next_char = path[1];
2028 /* If we're at a start of path component, current is '.'
2029 * and next one is not '.', end or separator: hidden. */
2030 if (component_start && cur_char == '.') {
2031 if (!ELEM(path[1], '.', '\0', '/', '\\')) {
2032 return true;
2033 }
2034 }
2035
2036 component_start = ELEM(cur_char, '/', '\\');
2037 /* Separator, and previous was tilde: hidden. */
2038 if (component_start && prev_char == '~') {
2039 return true;
2040 }
2041
2042 path++;
2043 prev_char = cur_char;
2044 cur_char = next_char;
2045 }
2046
2047 /* Was a tilde right at end of path: hidden. */
2048 if (prev_char == '~') {
2049 return true;
2050 }
2051
2052 /* Nothing was hidden. */
2053 return false;
2054}
#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:373
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:26
void BLI_kdtree_nd_ free(KDTree *tree)
bool BLI_path_parent_dir(char *path) ATTR_NONNULL(1)
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
bool BLI_path_parent_dir_until_exists(char *path) ATTR_NONNULL(1)
#define ALTSEP
bool void BLI_path_frame_strip(char *path, char *r_ext, size_t ext_maxncpy) ATTR_NONNULL(1
bool BLI_path_make_safe(char *path) ATTR_NONNULL(1)
size_t BLI_path_append(char *__restrict dst, size_t dst_maxncpy, const char *__restrict file) ATTR_NONNULL(1
bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *filename) ATTR_NONNULL(1
bool bool BLI_path_suffix(char *path, size_t path_maxncpy, const char *suffix, const char *sep) ATTR_NONNULL(1
bool bool BLI_path_extension_strip(char *path) ATTR_NONNULL(1)
bool void bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, const char *name) ATTR_NONNULL(1
int BLI_path_normalize_dir(char *dir, size_t dir_maxncpy) ATTR_NONNULL(1)
bool BLI_path_has_hidden_component(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool BLI_path_extension_check_array(const char *path, const char **ext_array) ATTR_NONNULL(1
int BLI_path_slash_ensure_ex(char *path, size_t path_maxncpy, const size_t path_len) ATTR_NONNULL(1)
#define FILE_MAX
bool BLI_path_extension_replace(char *path, size_t path_maxncpy, const char *ext) ATTR_NONNULL(1
void BLI_setenv(const char *env, const char *val) ATTR_NONNULL(1)
void BLI_path_slash_native(char *path) ATTR_NONNULL(1)
bool BLI_path_program_search(char *program_filepath, size_t program_filepath_maxncpy, const char *program_name) ATTR_NONNULL(1
int BLI_path_normalize(char *path) ATTR_NONNULL(1)
bool BLI_path_contains(const char *container_path, const char *containee_path) ATTR_NONNULL(1
#define BLI_path_join(...)
const char * BLI_path_extension_or_end(const char *filepath) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
const char * BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool BLI_path_is_win32_drive(const char *path)
bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
void void void BLI_path_split_file_part(const char *filepath, char *file, size_t file_maxncpy) ATTR_NONNULL(1
int BLI_path_canonicalize_native(char *path, int path_maxncpy)
void BLI_path_split_dir_file(const char *filepath, char *dir, size_t dir_maxncpy, char *file, size_t file_maxncpy) ATTR_NONNULL(1
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define SEP
bool BLI_path_extension_glob_validate(char *ext_fnmatch) ATTR_NONNULL(1)
bool BLI_path_frame_get(const char *path, int *r_frame, int *r_digits_len) ATTR_NONNULL(1
const char * BLI_path_slash_skip(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool BLI_path_extension_check(const char *path, const char *ext) ATTR_NONNULL(1
const char * BLI_path_slash_find(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
BLI_INLINE bool BLI_path_slash_is_native_compat(const char ch)
bool BLI_path_extension_ensure(char *path, size_t path_maxncpy, const char *ext) ATTR_NONNULL(1
int BLI_path_sequence_decode(const char *path, char *head, size_t head_maxncpy, char *tail, size_t tail_maxncpy, unsigned short *r_digits_len)
Definition path_utils.cc:57
const char * BLI_path_parent_dir_end(const char *path, size_t path_len) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
int BLI_path_cmp_normalized(const char *p1, const char *p2) ATTR_NONNULL(1
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
size_t BLI_path_join_array(char *__restrict dst, const size_t dst_maxncpy, const char *path_array[], const int path_array_num) ATTR_NONNULL(1
void BLI_setenv_if_new(const char *env, const char *val) ATTR_NONNULL(1)
bool void BLI_path_rel(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1)
bool BLI_path_extension_check_n(const char *path,...) ATTR_NONNULL(1) ATTR_SENTINEL(0)
bool BLI_path_is_win32_drive_only(const char *path)
bool BLI_path_make_safe_filename_ex(char *filename, bool allow_tokens) ATTR_NONNULL(1)
size_t size_t BLI_path_append_dir(char *__restrict dst, size_t dst_maxncpy, const char *__restrict dir) ATTR_NONNULL(1
void BLI_path_slash_rstrip(char *path) ATTR_NONNULL(1)
bool BLI_path_is_win32_drive_with_slash(const char *path)
int BLI_path_slash_ensure(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
bool BLI_path_abs_from_cwd(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
bool BLI_path_make_safe_filename(char *filename) ATTR_NONNULL(1)
const char * BLI_path_slash_rfind(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool BLI_path_frame_range(char *path, size_t path_maxncpy, int sta, int end, int digits) ATTR_NONNULL(1)
const char * BLI_path_extension(const char *filepath) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
int BLI_path_normalize_native(char *path) ATTR_NONNULL(1)
bool BLI_path_frame(char *path, size_t path_maxncpy, int frame, int digits) ATTR_NONNULL(1)
bool BLI_path_extension_check_glob(const char *path, const char *ext_fnmatch) ATTR_NONNULL(1
bool BLI_path_is_unc(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool BLI_path_name_at_index(const char *__restrict path, int index, int *__restrict r_offset, int *__restrict r_len) ATTR_NONNULL(1
#define BLI_path_cmp
void BLI_path_sequence_encode(char *path, size_t path_maxncpy, const char *head, const char *tail, unsigned short numlen, int pic)
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:598
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:599
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:923
#define BLI_string_debug_size_after_nil(str, str_maxncpy)
Definition BLI_string.h:676
#define BLI_string_debug_size(str, str_maxncpy)
Definition BLI_string.h:671
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:688
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:966
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 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.
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
ccl_device_inline float len(const float2 a)
#define L
void path_init(const string &path, const string &user_path)
Definition path.cpp:324
#define INVALID_CHARS
static bool path_name_at_index_forward(const char *__restrict path, const int index, int *__restrict r_offset, int *__restrict r_len)
static bool path_frame_chars_find_range(const char *path, int *r_char_start, int *r_char_end)
static int path_normalize_impl(char *path, bool check_blend_relative_prefix)
#define FILENAME_FRAME_CHARS_MAX
Definition path_utils.cc:55
static bool path_extension_check_ex(const char *path, const size_t path_len, const char *ext, const size_t ext_len)
static void ensure_digits(char *path, int digits)
static int BLI_path_unc_prefix_len(const char *path)
#define IS_PARENT_DIR(p)
static bool path_name_at_index_backward(const char *__restrict path, const int index, int *__restrict r_offset, int *__restrict r_len)
#define INVALID_TOKENS
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: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