Blender  V3.3
path_util.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include "DNA_listBase.h"
14 
15 #include "BLI_fileops.h"
16 #include "BLI_fnmatch.h"
17 #include "BLI_path_util.h"
18 #include "BLI_string.h"
19 #include "BLI_string_utf8.h"
20 #include "BLI_utildefines.h"
21 
22 #ifdef WIN32
23 # include "utf_winfunc.h"
24 # include "utfconv.h"
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 
42 #ifdef WIN32
43 
48 static bool BLI_path_is_abs(const char *name);
49 
50 #endif /* WIN32 */
51 
52 // #define DEBUG_STRSIZE
53 
54 /* implementation */
55 
56 int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort *r_digits_len)
57 {
58  uint nums = 0, nume = 0;
59  int i;
60  bool found_digit = false;
61  const char *const lslash = BLI_path_slash_rfind(string);
62  const uint string_len = strlen(string);
63  const uint lslash_len = lslash != NULL ? (int)(lslash - string) : 0;
64  uint name_end = string_len;
65 
66  while (name_end > lslash_len && string[--name_end] != '.') {
67  /* name ends at dot if present */
68  }
69  if (name_end == lslash_len && string[name_end] != '.') {
70  name_end = string_len;
71  }
72 
73  for (i = name_end - 1; i >= (int)lslash_len; i--) {
74  if (isdigit(string[i])) {
75  if (found_digit) {
76  nums = i;
77  }
78  else {
79  nume = i;
80  nums = i;
81  found_digit = true;
82  }
83  }
84  else {
85  if (found_digit) {
86  break;
87  }
88  }
89  }
90 
91  if (found_digit) {
92  const long long int ret = strtoll(&(string[nums]), NULL, 10);
93  if (ret >= INT_MIN && ret <= INT_MAX) {
94  if (tail) {
95  strcpy(tail, &string[nume + 1]);
96  }
97  if (head) {
98  strcpy(head, string);
99  head[nums] = 0;
100  }
101  if (r_digits_len) {
102  *r_digits_len = nume - nums + 1;
103  }
104  return (int)ret;
105  }
106  }
107 
108  if (tail) {
109  strcpy(tail, string + name_end);
110  }
111  if (head) {
112  /* name_end points to last character of head,
113  * make it +1 so null-terminator is nicely placed
114  */
115  BLI_strncpy(head, string, name_end + 1);
116  }
117  if (r_digits_len) {
118  *r_digits_len = 0;
119  }
120  return 0;
121 }
122 
124  char *string, const char *head, const char *tail, unsigned short numlen, int pic)
125 {
126  sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail);
127 }
128 
129 static int BLI_path_unc_prefix_len(const char *path); /* defined below in same file */
130 
131 void BLI_path_normalize(const char *relabase, char *path)
132 {
133  ptrdiff_t a;
134  char *start, *eind;
135  if (relabase) {
136  BLI_path_abs(path, relabase);
137  }
138  else {
139  if (path[0] == '/' && path[1] == '/') {
140  if (path[2] == '\0') {
141  return; /* path is "//" - can't clean it */
142  }
143  path = path + 2; /* leave the initial "//" untouched */
144  }
145  }
146 
147  /* Note
148  * memmove(start, eind, strlen(eind) + 1);
149  * is the same as
150  * strcpy(start, eind);
151  * except strcpy should not be used because there is overlap,
152  * so use memmove's slightly more obscure syntax - Campbell
153  */
154 
155 #ifdef WIN32
156  while ((start = strstr(path, "\\..\\"))) {
157  eind = start + strlen("\\..\\") - 1;
158  a = start - path - 1;
159  while (a > 0) {
160  if (path[a] == '\\') {
161  break;
162  }
163  a--;
164  }
165  if (a < 0) {
166  break;
167  }
168  else {
169  memmove(path + a, eind, strlen(eind) + 1);
170  }
171  }
172 
173  while ((start = strstr(path, "\\.\\"))) {
174  eind = start + strlen("\\.\\") - 1;
175  memmove(start, eind, strlen(eind) + 1);
176  }
177 
178  /* remove two consecutive backslashes, but skip the UNC prefix,
179  * which needs to be preserved */
180  while ((start = strstr(path + BLI_path_unc_prefix_len(path), "\\\\"))) {
181  eind = start + strlen("\\\\") - 1;
182  memmove(start, eind, strlen(eind) + 1);
183  }
184 #else
185  while ((start = strstr(path, "/../"))) {
186  a = start - path - 1;
187  if (a > 0) {
188  /* <prefix>/<parent>/../<postfix> => <prefix>/<postfix> */
189  eind = start + (4 - 1) /* strlen("/../") - 1 */; /* strip "/.." and keep last "/" */
190  while (a > 0 && path[a] != '/') { /* find start of <parent> */
191  a--;
192  }
193  memmove(path + a, eind, strlen(eind) + 1);
194  }
195  else {
196  /* Support for odd paths: eg `/../home/me` --> `/home/me`
197  * this is a valid path in blender but we can't handle this the usual way below
198  * simply strip this prefix then evaluate the path as usual.
199  * Python's `os.path.normpath()` does this. */
200 
201  /* NOTE: previous version of following call used an offset of 3 instead of 4,
202  * which meant that the `/../home/me` example actually became `home/me`.
203  * Using offset of 3 gives behavior consistent with the aforementioned
204  * Python routine. */
205  memmove(path, path + 3, strlen(path + 3) + 1);
206  }
207  }
208 
209  while ((start = strstr(path, "/./"))) {
210  eind = start + (3 - 1) /* strlen("/./") - 1 */;
211  memmove(start, eind, strlen(eind) + 1);
212  }
213 
214  while ((start = strstr(path, "//"))) {
215  eind = start + (2 - 1) /* strlen("//") - 1 */;
216  memmove(start, eind, strlen(eind) + 1);
217  }
218 #endif
219 }
220 
221 void BLI_path_normalize_dir(const char *relabase, char *dir)
222 {
223  /* Would just create an unexpected "/" path, just early exit entirely. */
224  if (dir[0] == '\0') {
225  return;
226  }
227 
228  BLI_path_normalize(relabase, dir);
230 }
231 
232 bool BLI_filename_make_safe_ex(char *fname, bool allow_tokens)
233 {
234 #define INVALID_CHARS \
235  "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
236  "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
237  "/\\?*:|\""
238 #define INVALID_TOKENS "<>"
239 
240  const char *invalid = allow_tokens ? INVALID_CHARS : INVALID_CHARS INVALID_TOKENS;
241 
242 #undef INVALID_CHARS
243 #undef INVALID_TOKENS
244 
245  char *fn;
246  bool changed = false;
247 
248  if (*fname == '\0') {
249  return changed;
250  }
251 
252  for (fn = fname; *fn && (fn = strpbrk(fn, invalid)); fn++) {
253  *fn = '_';
254  changed = true;
255  }
256 
257  /* Forbid only dots. */
258  for (fn = fname; *fn == '.'; fn++) {
259  /* pass */
260  }
261  if (*fn == '\0') {
262  *fname = '_';
263  changed = true;
264  }
265 
266 #ifdef WIN32
267  {
268  const size_t len = strlen(fname);
269  const char *invalid_names[] = {
270  "con", "prn", "aux", "null", "com1", "com2", "com3", "com4",
271  "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3",
272  "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", NULL,
273  };
274  char *lower_fname = BLI_strdup(fname);
275  const char **iname;
276 
277  /* Forbid trailing dot (trailing space has already been replaced above). */
278  if (fname[len - 1] == '.') {
279  fname[len - 1] = '_';
280  changed = true;
281  }
282 
283  /* Check for forbidden names - not we have to check all combination
284  * of upper and lower cases, hence the usage of lower_fname
285  * (more efficient than using BLI_strcasestr repeatedly). */
286  BLI_str_tolower_ascii(lower_fname, len);
287  for (iname = invalid_names; *iname; iname++) {
288  if (strstr(lower_fname, *iname) == lower_fname) {
289  const size_t iname_len = strlen(*iname);
290  /* Only invalid if the whole name is made of the invalid chunk, or it has an
291  * (assumed extension) dot just after. This means it will also catch 'valid'
292  * names like 'aux.foo.bar', but should be
293  * good enough for us! */
294  if ((iname_len == len) || (lower_fname[iname_len] == '.')) {
295  *fname = '_';
296  changed = true;
297  break;
298  }
299  }
300  }
301 
302  MEM_freeN(lower_fname);
303  }
304 #endif
305 
306  return changed;
307 }
308 
309 bool BLI_filename_make_safe(char *fname)
310 {
311  return BLI_filename_make_safe_ex(fname, false);
312 }
313 
314 bool BLI_path_make_safe(char *path)
315 {
316  /* Simply apply #BLI_filename_make_safe() over each component of the path.
317  * Luckily enough, same 'safe' rules applies to file & directory names. */
318  char *curr_slash, *curr_path = path;
319  bool changed = false;
320  bool skip_first = false;
321 
322 #ifdef WIN32
323  if (BLI_path_is_abs(path)) {
324  /* Do not make safe 'C:' in 'C:\foo\bar'... */
325  skip_first = true;
326  }
327 #endif
328 
329  for (curr_slash = (char *)BLI_path_slash_find(curr_path); curr_slash;
330  curr_slash = (char *)BLI_path_slash_find(curr_path)) {
331  const char backup = *curr_slash;
332  *curr_slash = '\0';
333  if (!skip_first && (*curr_path != '\0') && BLI_filename_make_safe(curr_path)) {
334  changed = true;
335  }
336  skip_first = false;
337  curr_path = curr_slash + 1;
338  *curr_slash = backup;
339  }
340  if (BLI_filename_make_safe(curr_path)) {
341  changed = true;
342  }
343 
344  return changed;
345 }
346 
347 bool BLI_path_is_rel(const char *path)
348 {
349  return path[0] == '/' && path[1] == '/';
350 }
351 
352 bool BLI_path_is_unc(const char *name)
353 {
354  return name[0] == '\\' && name[1] == '\\';
355 }
356 
363 static int BLI_path_unc_prefix_len(const char *path)
364 {
365  if (BLI_path_is_unc(path)) {
366  if ((path[2] == '?') && (path[3] == '\\')) {
367  /* we assume long UNC path like \\?\server\share\folder etc... */
368  return 4;
369  }
370 
371  return 2;
372  }
373 
374  return 0;
375 }
376 
377 #if defined(WIN32)
378 
383 static bool BLI_path_is_abs(const char *name)
384 {
385  return (name[1] == ':' && ELEM(name[2], '\\', '/')) || BLI_path_is_unc(name);
386 }
387 
388 static wchar_t *next_slash(wchar_t *path)
389 {
390  wchar_t *slash = path;
391  while (*slash && *slash != L'\\') {
392  slash++;
393  }
394  return slash;
395 }
396 
397 /* Adds a slash if the UNC path points to a share. */
398 static void BLI_path_add_slash_to_share(wchar_t *uncpath)
399 {
400  wchar_t *slash_after_server = next_slash(uncpath + 2);
401  if (*slash_after_server) {
402  wchar_t *slash_after_share = next_slash(slash_after_server + 1);
403  if (!(*slash_after_share)) {
404  slash_after_share[0] = L'\\';
405  slash_after_share[1] = L'\0';
406  }
407  }
408 }
409 
410 static void BLI_path_unc_to_short(wchar_t *unc)
411 {
412  wchar_t tmp[PATH_MAX];
413 
414  int len = wcslen(unc);
415  /* convert:
416  * \\?\UNC\server\share\folder\... to \\server\share\folder\...
417  * \\?\C:\ to C:\ and \\?\C:\folder\... to C:\folder\...
418  */
419  if ((len > 3) && (unc[0] == L'\\') && (unc[1] == L'\\') && (unc[2] == L'?') &&
420  ELEM(unc[3], L'\\', L'/')) {
421  if ((len > 5) && (unc[5] == L':')) {
422  wcsncpy(tmp, unc + 4, len - 4);
423  tmp[len - 4] = L'\0';
424  wcscpy(unc, tmp);
425  }
426  else if ((len > 7) && (wcsncmp(&unc[4], L"UNC", 3) == 0) && ELEM(unc[7], L'\\', L'/')) {
427  tmp[0] = L'\\';
428  tmp[1] = L'\\';
429  wcsncpy(tmp + 2, unc + 8, len - 8);
430  tmp[len - 6] = L'\0';
431  wcscpy(unc, tmp);
432  }
433  }
434 }
435 
436 void BLI_path_normalize_unc(char *path, int maxlen)
437 {
438  wchar_t *tmp_16 = alloc_utf16_from_8(path, 1);
439  BLI_path_normalize_unc_16(tmp_16);
440  conv_utf_16_to_8(tmp_16, path, maxlen);
441 }
442 
443 void BLI_path_normalize_unc_16(wchar_t *path_16)
444 {
445  BLI_path_unc_to_short(path_16);
446  BLI_path_add_slash_to_share(path_16);
447 }
448 #endif
449 
450 void BLI_path_rel(char *file, const char *relfile)
451 {
452  const char *lslash;
453  char temp[FILE_MAX];
454  char res[FILE_MAX];
455 
456  /* if file is already relative, bail out */
457  if (BLI_path_is_rel(file)) {
458  return;
459  }
460 
461  /* also bail out if relative path is not set */
462  if (relfile[0] == '\0') {
463  return;
464  }
465 
466 #ifdef WIN32
467  if (BLI_strnlen(relfile, 3) > 2 && !BLI_path_is_abs(relfile)) {
468  char *ptemp;
469  /* fix missing volume name in relative base,
470  * can happen with old recent-files.txt files */
472  ptemp = &temp[2];
473  if (!ELEM(relfile[0], '\\', '/')) {
474  ptemp++;
475  }
476  BLI_strncpy(ptemp, relfile, FILE_MAX - 3);
477  }
478  else {
479  BLI_strncpy(temp, relfile, FILE_MAX);
480  }
481 
482  if (BLI_strnlen(file, 3) > 2) {
483  bool is_unc = BLI_path_is_unc(file);
484 
485  /* Ensure paths are both UNC paths or are both drives */
486  if (BLI_path_is_unc(temp) != is_unc) {
487  return;
488  }
489 
490  /* Ensure both UNC paths are on the same share */
491  if (is_unc) {
492  int off;
493  int slash = 0;
494  for (off = 0; temp[off] && slash < 4; off++) {
495  if (temp[off] != file[off]) {
496  return;
497  }
498 
499  if (temp[off] == '\\') {
500  slash++;
501  }
502  }
503  }
504  else if ((temp[1] == ':' && file[1] == ':') && (tolower(temp[0]) != tolower(file[0]))) {
505  return;
506  }
507  }
508 #else
509  BLI_strncpy(temp, relfile, FILE_MAX);
510 #endif
511 
512  BLI_str_replace_char(temp + BLI_path_unc_prefix_len(temp), '\\', '/');
514 
515  /* remove /./ which confuse the following slash counting... */
517  BLI_path_normalize(NULL, temp);
518 
519  /* the last slash in the file indicates where the path part ends */
520  lslash = BLI_path_slash_rfind(temp);
521 
522  if (lslash) {
523  /* find the prefix of the filename that is equal for both filenames.
524  * This is replaced by the two slashes at the beginning */
525  const char *p = temp;
526  const char *q = file;
527  char *r = res;
528 
529 #ifdef WIN32
530  while (tolower(*p) == tolower(*q))
531 #else
532  while (*p == *q)
533 #endif
534  {
535  p++;
536  q++;
537 
538  /* don't search beyond the end of the string
539  * in the rare case they match */
540  if ((*p == '\0') || (*q == '\0')) {
541  break;
542  }
543  }
544 
545  /* we might have passed the slash when the beginning of a dir matches
546  * so we rewind. Only check on the actual filename
547  */
548  if (*q != '/') {
549  while ((q >= file) && (*q != '/')) {
550  q--;
551  p--;
552  }
553  }
554  else if (*p != '/') {
555  while ((p >= temp) && (*p != '/')) {
556  p--;
557  q--;
558  }
559  }
560 
561  r += BLI_strcpy_rlen(r, "//");
562 
563  /* p now points to the slash that is at the beginning of the part
564  * where the path is different from the relative path.
565  * We count the number of directories we need to go up in the
566  * hierarchy to arrive at the common 'prefix' of the path
567  */
568  if (p < temp) {
569  p = temp;
570  }
571  while (p && p < lslash) {
572  if (*p == '/') {
573  r += BLI_strcpy_rlen(r, "../");
574  }
575  p++;
576  }
577 
578  /* don't copy the slash at the beginning */
579  r += BLI_strncpy_rlen(r, q + 1, FILE_MAX - (r - res));
580 
581 #ifdef WIN32
582  BLI_str_replace_char(res + 2, '/', '\\');
583 #endif
584  strcpy(file, res);
585  }
586 }
587 
588 bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char *sep)
589 {
590 #ifdef DEBUG_STRSIZE
591  memset(string, 0xff, sizeof(*string) * maxlen);
592 #endif
593  const size_t string_len = strlen(string);
594  const size_t suffix_len = strlen(suffix);
595  const size_t sep_len = strlen(sep);
596  ssize_t a;
597  char extension[FILE_MAX];
598  bool has_extension = false;
599 
600  if (string_len + sep_len + suffix_len >= maxlen) {
601  return false;
602  }
603 
604  for (a = string_len - 1; a >= 0; a--) {
605  if (string[a] == '.') {
606  has_extension = true;
607  break;
608  }
609  if (ELEM(string[a], '/', '\\')) {
610  break;
611  }
612  }
613 
614  if (!has_extension) {
615  a = string_len;
616  }
617 
618  BLI_strncpy(extension, string + a, sizeof(extension));
619  sprintf(string + a, "%s%s%s", sep, suffix, extension);
620  return true;
621 }
622 
623 bool BLI_path_parent_dir(char *path)
624 {
625  const char parent_dir[] = {'.', '.', SEP, '\0'}; /* "../" or "..\\" */
626  char tmp[FILE_MAX + 4];
627 
628  BLI_join_dirfile(tmp, sizeof(tmp), path, parent_dir);
629  BLI_path_normalize(NULL, tmp); /* does all the work of normalizing the path for us */
630 
631  if (!BLI_path_extension_check(tmp, parent_dir)) {
632  strcpy(path, tmp); /* We assume the parent directory is always shorter. */
633  return true;
634  }
635 
636  return false;
637 }
638 
640 {
641  bool valid_path = true;
642 
643  /* Loop as long as cur path is not a dir, and we can get a parent path. */
644  while ((BLI_access(dir, R_OK) != 0) && (valid_path = BLI_path_parent_dir(dir))) {
645  /* pass */
646  }
647  return (valid_path && dir[0]);
648 }
649 
655 static bool stringframe_chars(const char *path, int *char_start, int *char_end)
656 {
657  uint ch_sta, ch_end, i;
658  /* Insert current frame: file### -> file001 */
659  ch_sta = ch_end = 0;
660  for (i = 0; path[i] != '\0'; i++) {
661  if (ELEM(path[i], '\\', '/')) {
662  ch_end = 0; /* this is a directory name, don't use any hashes we found */
663  }
664  else if (path[i] == '#') {
665  ch_sta = i;
666  ch_end = ch_sta + 1;
667  while (path[ch_end] == '#') {
668  ch_end++;
669  }
670  i = ch_end - 1; /* keep searching */
671 
672  /* don't break, there may be a slash after this that invalidates the previous #'s */
673  }
674  }
675 
676  if (ch_end) {
677  *char_start = ch_sta;
678  *char_end = ch_end;
679  return true;
680  }
681 
682  *char_start = -1;
683  *char_end = -1;
684  return false;
685 }
686 
691 static void ensure_digits(char *path, int digits)
692 {
693  char *file = (char *)BLI_path_slash_rfind(path);
694 
695  if (file == NULL) {
696  file = path;
697  }
698 
699  if (strrchr(file, '#') == NULL) {
700  int len = strlen(file);
701 
702  while (digits--) {
703  file[len++] = '#';
704  }
705  file[len] = '\0';
706  }
707 }
708 
709 bool BLI_path_frame(char *path, int frame, int digits)
710 {
711  int ch_sta, ch_end;
712 
713  if (digits) {
714  ensure_digits(path, digits);
715  }
716 
717  if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
718  char tmp[FILE_MAX];
719  BLI_snprintf(
720  tmp, sizeof(tmp), "%.*s%.*d%s", ch_sta, path, ch_end - ch_sta, frame, path + ch_end);
721  BLI_strncpy(path, tmp, FILE_MAX);
722  return true;
723  }
724  return false;
725 }
726 
727 bool BLI_path_frame_range(char *path, int sta, int end, int digits)
728 {
729  int ch_sta, ch_end;
730 
731  if (digits) {
732  ensure_digits(path, digits);
733  }
734 
735  if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
736  char tmp[FILE_MAX];
737  BLI_snprintf(tmp,
738  sizeof(tmp),
739  "%.*s%.*d-%.*d%s",
740  ch_sta,
741  path,
742  ch_end - ch_sta,
743  sta,
744  ch_end - ch_sta,
745  end,
746  path + ch_end);
747  BLI_strncpy(path, tmp, FILE_MAX);
748  return true;
749  }
750  return false;
751 }
752 
753 bool BLI_path_frame_get(char *path, int *r_frame, int *r_digits_len)
754 {
755  if (*path) {
756  char *file = (char *)BLI_path_slash_rfind(path);
757  char *c;
758  int len, digits_len;
759 
760  digits_len = *r_digits_len = 0;
761 
762  if (file == NULL) {
763  file = path;
764  }
765 
766  /* first get the extension part */
767  len = strlen(file);
768 
769  c = file + len;
770 
771  /* isolate extension */
772  while (--c != file) {
773  if (*c == '.') {
774  c--;
775  break;
776  }
777  }
778 
779  /* find start of number */
780  while (c != (file - 1) && isdigit(*c)) {
781  c--;
782  digits_len++;
783  }
784 
785  if (digits_len) {
786  char prevchar;
787 
788  c++;
789  prevchar = c[digits_len];
790  c[digits_len] = 0;
791 
792  /* was the number really an extension? */
793  *r_frame = atoi(c);
794  c[digits_len] = prevchar;
795 
796  *r_digits_len = digits_len;
797 
798  return true;
799  }
800  }
801 
802  return false;
803 }
804 
805 void BLI_path_frame_strip(char *path, char *r_ext, const size_t ext_maxlen)
806 {
807  *r_ext = '\0';
808  if (*path == '\0') {
809  return;
810  }
811 
812  char *file = (char *)BLI_path_slash_rfind(path);
813  char *c, *suffix;
814  int len;
815  int digits_len = 0;
816 
817  if (file == NULL) {
818  file = path;
819  }
820 
821  /* first get the extension part */
822  len = strlen(file);
823 
824  c = file + len;
825 
826  /* isolate extension */
827  while (--c != file) {
828  if (*c == '.') {
829  c--;
830  break;
831  }
832  }
833 
834  suffix = c + 1;
835 
836  /* find start of number */
837  while (c != (file - 1) && isdigit(*c)) {
838  c--;
839  digits_len++;
840  }
841 
842  c++;
843 
844  BLI_strncpy(r_ext, suffix, ext_maxlen);
845 
846  /* replace the number with the suffix and terminate the string */
847  while (digits_len--) {
848  *c++ = '#';
849  }
850  *c = '\0';
851 }
852 
853 bool BLI_path_frame_check_chars(const char *path)
854 {
855  int ch_sta, ch_end; /* dummy args */
856  return stringframe_chars(path, &ch_sta, &ch_end);
857 }
858 
859 void BLI_path_to_display_name(char *display_name, int maxlen, const char *name)
860 {
861  /* Strip leading underscores and spaces. */
862  int strip_offset = 0;
863  while (ELEM(name[strip_offset], '_', ' ')) {
864  strip_offset++;
865  }
866 
867  BLI_strncpy(display_name, name + strip_offset, maxlen);
868 
869  /* Replace underscores with spaces. */
870  BLI_str_replace_char(display_name, '_', ' ');
871 
872  /* Strip extension. */
873  BLI_path_extension_replace(display_name, maxlen, "");
874 
875  /* Test if string has any upper case characters. */
876  bool all_lower = true;
877  for (int i = 0; display_name[i]; i++) {
878  if (isupper(display_name[i])) {
879  all_lower = false;
880  break;
881  }
882  }
883 
884  if (all_lower) {
885  /* For full lowercase string, use title case. */
886  bool prevspace = true;
887  for (int i = 0; display_name[i]; i++) {
888  if (prevspace) {
889  display_name[i] = toupper(display_name[i]);
890  }
891 
892  prevspace = isspace(display_name[i]);
893  }
894  }
895 }
896 
897 bool BLI_path_abs(char *path, const char *basepath)
898 {
899  const bool wasrelative = BLI_path_is_rel(path);
900  char tmp[FILE_MAX];
901  char base[FILE_MAX];
902 #ifdef WIN32
903 
904  /* without this: "" --> "C:\" */
905  if (*path == '\0') {
906  return wasrelative;
907  }
908 
909  /* we are checking here if we have an absolute path that is not in the current
910  * blend file as a lib main - we are basically checking for the case that a
911  * UNIX root '/' is passed.
912  */
913  if (!wasrelative && !BLI_path_is_abs(path)) {
914  char *p = path;
916  /* Get rid of the slashes at the beginning of the path. */
917  while (ELEM(*p, '\\', '/')) {
918  p++;
919  }
920  strcat(tmp, p);
921  }
922  else {
923  BLI_strncpy(tmp, path, FILE_MAX);
924  }
925 #else
926  BLI_strncpy(tmp, path, sizeof(tmp));
927 
928  /* Check for loading a MS-Windows path on a POSIX system
929  * in this case, there is no use in trying `C:/` since it
930  * will never exist on a Unix system.
931  *
932  * Add a `/` prefix and lowercase the drive-letter, remove the `:`.
933  * `C:\foo.JPG` -> `/c/foo.JPG` */
934 
935  if (isalpha(tmp[0]) && (tmp[1] == ':') && ELEM(tmp[2], '\\', '/')) {
936  tmp[1] = tolower(tmp[0]); /* Replace ':' with drive-letter. */
937  tmp[0] = '/';
938  /* `\` the slash will be converted later. */
939  }
940 
941 #endif
942 
943  /* NOTE(@jesterKing): push slashes into unix mode - strings entering this part are
944  * potentially messed up: having both back- and forward slashes.
945  * Here we push into one conform direction, and at the end we
946  * push them into the system specific dir. This ensures uniformity
947  * of paths and solving some problems (and prevent potential future ones).
948  *
949  * NOTE(@elubie): For UNC paths the first characters containing the UNC prefix
950  * shouldn't be switched as we need to distinguish them from
951  * paths relative to the `.blend` file. */
952  BLI_str_replace_char(tmp + BLI_path_unc_prefix_len(tmp), '\\', '/');
953 
954  /* Paths starting with `//` will get the blend file as their base,
955  * this isn't standard in any OS but is used in blender all over the place. */
956  if (wasrelative) {
957  const char *lslash;
958  BLI_strncpy(base, basepath, sizeof(base));
959 
960  /* file component is ignored, so don't bother with the trailing slash */
961  BLI_path_normalize(NULL, base);
962  lslash = BLI_path_slash_rfind(base);
963  BLI_str_replace_char(base + BLI_path_unc_prefix_len(base), '\\', '/');
964 
965  if (lslash) {
966  /* length up to and including last "/" */
967  const int baselen = (int)(lslash - base) + 1;
968  /* use path for temp storage here, we copy back over it right away */
969  BLI_strncpy(path, tmp + 2, FILE_MAX); /* strip "//" */
970 
971  memcpy(tmp, base, baselen); /* prefix with base up to last "/" */
972  BLI_strncpy(tmp + baselen, path, sizeof(tmp) - baselen); /* append path after "//" */
973  BLI_strncpy(path, tmp, FILE_MAX); /* return as result */
974  }
975  else {
976  /* base doesn't seem to be a directory--ignore it and just strip "//" prefix on path */
977  BLI_strncpy(path, tmp + 2, FILE_MAX);
978  }
979  }
980  else {
981  /* base ignored */
982  BLI_strncpy(path, tmp, FILE_MAX);
983  }
984 
985 #ifdef WIN32
986  /* NOTE(@jesterking): Skip first two chars, which in case of absolute path will
987  * be `drive:/blabla` and in case of `relpath` `//blabla/`.
988  * So `relpath` `//` will be retained, rest will be nice and shiny WIN32 backward slashes. */
989  BLI_str_replace_char(path + 2, '/', '\\');
990 #endif
991 
992  /* ensure this is after correcting for path switch */
993  BLI_path_normalize(NULL, path);
994 
995  return wasrelative;
996 }
997 
998 bool BLI_path_is_abs_from_cwd(const char *path)
999 {
1000  bool is_abs = false;
1001  const int path_len_clamp = BLI_strnlen(path, 3);
1002 
1003 #ifdef WIN32
1004  if ((path_len_clamp >= 3 && BLI_path_is_abs(path)) || BLI_path_is_unc(path)) {
1005  is_abs = true;
1006  }
1007 #else
1008  if (path_len_clamp >= 2 && path[0] == '/') {
1009  is_abs = true;
1010  }
1011 #endif
1012  return is_abs;
1013 }
1014 
1015 bool BLI_path_abs_from_cwd(char *path, const size_t maxlen)
1016 {
1017 #ifdef DEBUG_STRSIZE
1018  memset(path, 0xff, sizeof(*path) * maxlen);
1019 #endif
1020 
1021  if (!BLI_path_is_abs_from_cwd(path)) {
1022  char cwd[FILE_MAX];
1023  /* in case the full path to the blend isn't used */
1024  if (BLI_current_working_dir(cwd, sizeof(cwd))) {
1025  char origpath[FILE_MAX];
1026  BLI_strncpy(origpath, path, FILE_MAX);
1027  BLI_join_dirfile(path, maxlen, cwd, origpath);
1028  }
1029  else {
1030  printf("Could not get the current working directory - $PWD for an unknown reason.\n");
1031  }
1032  return true;
1033  }
1034 
1035  return false;
1036 }
1037 
1038 #ifdef _WIN32
1044 bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen)
1045 {
1046  bool retval = false;
1047  int type;
1048 
1049  type = BLI_exists(name);
1050  if ((type == 0) || S_ISDIR(type)) {
1051  /* typically 3-5, ".EXE", ".BAT"... etc */
1052  const int ext_max = 12;
1053  const char *ext = BLI_getenv("PATHEXT");
1054  if (ext) {
1055  const int name_len = strlen(name);
1056  char *filename = alloca(name_len + ext_max);
1057  char *filename_ext;
1058  const char *ext_next;
1059 
1060  /* null terminated in the loop */
1061  memcpy(filename, name, name_len);
1062  filename_ext = filename + name_len;
1063 
1064  do {
1065  int ext_len;
1066  ext_next = strchr(ext, ';');
1067  ext_len = ext_next ? ((ext_next++) - ext) : strlen(ext);
1068 
1069  if (LIKELY(ext_len < ext_max)) {
1070  memcpy(filename_ext, ext, ext_len);
1071  filename_ext[ext_len] = '\0';
1072 
1073  type = BLI_exists(filename);
1074  if (type && (!S_ISDIR(type))) {
1075  retval = true;
1076  BLI_strncpy(name, filename, maxlen);
1077  break;
1078  }
1079  }
1080  } while ((ext = ext_next));
1081  }
1082  }
1083  else {
1084  retval = true;
1085  }
1086 
1087  return retval;
1088 }
1089 #endif /* WIN32 */
1090 
1091 bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *name)
1092 {
1093 #ifdef DEBUG_STRSIZE
1094  memset(fullname, 0xff, sizeof(*fullname) * maxlen);
1095 #endif
1096  const char *path;
1097  bool retval = false;
1098 
1099 #ifdef _WIN32
1100  const char separator = ';';
1101 #else
1102  const char separator = ':';
1103 #endif
1104 
1105  path = BLI_getenv("PATH");
1106  if (path) {
1107  char filename[FILE_MAX];
1108  const char *temp;
1109 
1110  do {
1111  temp = strchr(path, separator);
1112  if (temp) {
1113  memcpy(filename, path, temp - path);
1114  filename[temp - path] = 0;
1115  path = temp + 1;
1116  }
1117  else {
1118  BLI_strncpy(filename, path, sizeof(filename));
1119  }
1120 
1121  BLI_path_append(filename, maxlen, name);
1122  if (
1123 #ifdef _WIN32
1124  BLI_path_program_extensions_add_win32(filename, maxlen)
1125 #else
1126  BLI_exists(filename)
1127 #endif
1128  ) {
1129  BLI_strncpy(fullname, filename, maxlen);
1130  retval = true;
1131  break;
1132  }
1133  } while (temp);
1134  }
1135 
1136  if (retval == false) {
1137  *fullname = '\0';
1138  }
1139 
1140  return retval;
1141 }
1142 
1143 void BLI_setenv(const char *env, const char *val)
1144 {
1145  /* free windows */
1146 
1147 #if (defined(_WIN32) || defined(_WIN64))
1148  uputenv(env, val);
1149 
1150 #else
1151  /* Linux/macOS/BSD */
1152  if (val) {
1153  setenv(env, val, 1);
1154  }
1155  else {
1156  unsetenv(env);
1157  }
1158 #endif
1159 }
1160 
1161 void BLI_setenv_if_new(const char *env, const char *val)
1162 {
1163  if (BLI_getenv(env) == NULL) {
1164  BLI_setenv(env, val);
1165  }
1166 }
1167 
1168 const char *BLI_getenv(const char *env)
1169 {
1170 #ifdef _MSC_VER
1171  const char *result = NULL;
1172  /* 32767 is the maximum size of the environment variable on windows,
1173  * reserve one more character for the zero terminator. */
1174  static wchar_t buffer[32768];
1175  wchar_t *env_16 = alloc_utf16_from_8(env, 0);
1176  if (env_16) {
1177  if (GetEnvironmentVariableW(env_16, buffer, ARRAY_SIZE(buffer))) {
1178  char *res_utf8 = alloc_utf_8_from_16(buffer, 0);
1179  /* Make sure the result is valid, and will fit into our temporary storage buffer. */
1180  if (res_utf8) {
1181  if (strlen(res_utf8) + 1 < sizeof(buffer)) {
1182  /* We are re-using the utf16 buffer here, since allocating a second static buffer to
1183  * contain the UTF-8 version to return would be wasteful. */
1184  memcpy(buffer, res_utf8, strlen(res_utf8) + 1);
1185  result = (const char *)buffer;
1186  }
1187  free(res_utf8);
1188  }
1189  }
1190  }
1191  return result;
1192 #else
1193  return getenv(env);
1194 #endif
1195 }
1196 
1197 bool BLI_make_existing_file(const char *name)
1198 {
1199  char di[FILE_MAX];
1200  BLI_split_dir_part(name, di, sizeof(di));
1201 
1202  /* make if the dir doesn't exist */
1203  return BLI_dir_create_recursive(di);
1204 }
1205 
1206 void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file)
1207 {
1208  int sl;
1209 
1210  if (string) {
1211  /* ensure this is always set even if dir/file are NULL */
1212  string[0] = '\0';
1213 
1214  if (ELEM(NULL, dir, file)) {
1215  return; /* We don't want any NULLs */
1216  }
1217  }
1218  else {
1219  return; /* string is NULL, probably shouldn't happen but return anyway */
1220  }
1221 
1222  /* Resolve relative references */
1223  if (relabase && dir[0] == '/' && dir[1] == '/') {
1224  char *lslash;
1225 
1226  /* Get the file name, chop everything past the last slash (ie. the filename) */
1227  strcpy(string, relabase);
1228 
1229  lslash = (char *)BLI_path_slash_rfind(string);
1230  if (lslash) {
1231  *(lslash + 1) = 0;
1232  }
1233 
1234  dir += 2; /* Skip over the relative reference */
1235  }
1236 #ifdef WIN32
1237  else {
1238  if (BLI_strnlen(dir, 3) >= 2 && dir[1] == ':') {
1239  BLI_strncpy(string, dir, 3);
1240  dir += 2;
1241  }
1242  else if (BLI_strnlen(dir, 3) >= 2 && BLI_path_is_unc(dir)) {
1243  string[0] = 0;
1244  }
1245  else { /* no drive specified */
1246  /* first option: get the drive from the relabase if it has one */
1247  if (relabase && BLI_strnlen(relabase, 3) >= 2 && relabase[1] == ':') {
1248  BLI_strncpy(string, relabase, 3);
1249  string[2] = '\\';
1250  string[3] = '\0';
1251  }
1252  else { /* we're out of luck here, guessing the first valid drive, usually c:\ */
1254  }
1255 
1256  /* ignore leading slashes */
1257  while (ELEM(*dir, '/', '\\')) {
1258  dir++;
1259  }
1260  }
1261  }
1262 #endif
1263 
1264  strcat(string, dir);
1265 
1266  /* Make sure string ends in one (and only one) slash */
1267  /* first trim all slashes from the end of the string */
1268  sl = strlen(string);
1269  while ((sl > 0) && ELEM(string[sl - 1], '/', '\\')) {
1270  string[sl - 1] = '\0';
1271  sl--;
1272  }
1273  /* since we've now removed all slashes, put back one slash at the end. */
1274  strcat(string, "/");
1275 
1276  while (ELEM(*file, '/', '\\')) {
1277  /* Trim slashes from the front of file */
1278  file++;
1279  }
1280 
1281  strcat(string, file);
1282 
1283  /* Push all slashes to the system preferred direction */
1284  BLI_path_slash_native(string);
1285 }
1286 
1287 static bool path_extension_check_ex(const char *str,
1288  const size_t str_len,
1289  const char *ext,
1290  const size_t ext_len)
1291 {
1292  BLI_assert(strlen(str) == str_len);
1293  BLI_assert(strlen(ext) == ext_len);
1294 
1295  return (((str_len == 0 || ext_len == 0 || ext_len >= str_len) == 0) &&
1296  (BLI_strcasecmp(ext, str + str_len - ext_len) == 0));
1297 }
1298 
1299 bool BLI_path_extension_check(const char *str, const char *ext)
1300 {
1301  return path_extension_check_ex(str, strlen(str), ext, strlen(ext));
1302 }
1303 
1304 bool BLI_path_extension_check_n(const char *str, ...)
1305 {
1306  const size_t str_len = strlen(str);
1307 
1308  va_list args;
1309  const char *ext;
1310  bool ret = false;
1311 
1312  va_start(args, str);
1313 
1314  while ((ext = (const char *)va_arg(args, void *))) {
1315  if (path_extension_check_ex(str, str_len, ext, strlen(ext))) {
1316  ret = true;
1317  break;
1318  }
1319  }
1320 
1321  va_end(args);
1322 
1323  return ret;
1324 }
1325 
1326 bool BLI_path_extension_check_array(const char *str, const char **ext_array)
1327 {
1328  const size_t str_len = strlen(str);
1329  int i = 0;
1330 
1331  while (ext_array[i]) {
1332  if (path_extension_check_ex(str, str_len, ext_array[i], strlen(ext_array[i]))) {
1333  return true;
1334  }
1335 
1336  i++;
1337  }
1338  return false;
1339 }
1340 
1341 bool BLI_path_extension_check_glob(const char *str, const char *ext_fnmatch)
1342 {
1343  const char *ext_step = ext_fnmatch;
1344  char pattern[16];
1345 
1346  while (ext_step[0]) {
1347  const char *ext_next;
1348  size_t len_ext;
1349 
1350  if ((ext_next = strchr(ext_step, ';'))) {
1351  len_ext = ext_next - ext_step + 1;
1352  BLI_strncpy(pattern, ext_step, (len_ext > sizeof(pattern)) ? sizeof(pattern) : len_ext);
1353  }
1354  else {
1355  len_ext = BLI_strncpy_rlen(pattern, ext_step, sizeof(pattern));
1356  }
1357 
1358  if (fnmatch(pattern, str, FNM_CASEFOLD) == 0) {
1359  return true;
1360  }
1361  ext_step += len_ext;
1362  }
1363 
1364  return false;
1365 }
1366 
1367 bool BLI_path_extension_glob_validate(char *ext_fnmatch)
1368 {
1369  bool only_wildcards = false;
1370 
1371  for (size_t i = strlen(ext_fnmatch); i-- > 0;) {
1372  if (ext_fnmatch[i] == ';') {
1373  /* Group separator, we truncate here if we only had wildcards so far.
1374  * Otherwise, all is sound and fine. */
1375  if (only_wildcards) {
1376  ext_fnmatch[i] = '\0';
1377  return true;
1378  }
1379  return false;
1380  }
1381  if (!ELEM(ext_fnmatch[i], '?', '*')) {
1382  /* Non-wildcard char, we can break here and consider the pattern valid. */
1383  return false;
1384  }
1385  /* So far, only wildcards in last group of the pattern... */
1386  only_wildcards = true;
1387  }
1388  /* Only one group in the pattern, so even if its only made of wildcard(s),
1389  * it is assumed valid. */
1390  return false;
1391 }
1392 
1393 bool BLI_path_extension_replace(char *path, size_t maxlen, const char *ext)
1394 {
1395 #ifdef DEBUG_STRSIZE
1396  memset(path, 0xff, sizeof(*path) * maxlen);
1397 #endif
1398  const size_t path_len = strlen(path);
1399  const size_t ext_len = strlen(ext);
1400  ssize_t a;
1401 
1402  for (a = path_len - 1; a >= 0; a--) {
1403  if (ELEM(path[a], '.', '/', '\\')) {
1404  break;
1405  }
1406  }
1407 
1408  if ((a < 0) || (path[a] != '.')) {
1409  a = path_len;
1410  }
1411 
1412  if (a + ext_len >= maxlen) {
1413  return false;
1414  }
1415 
1416  memcpy(path + a, ext, ext_len + 1);
1417  return true;
1418 }
1419 
1420 bool BLI_path_extension_ensure(char *path, size_t maxlen, const char *ext)
1421 {
1422 #ifdef DEBUG_STRSIZE
1423  memset(path, 0xff, sizeof(*path) * maxlen);
1424 #endif
1425  const size_t path_len = strlen(path);
1426  const size_t ext_len = strlen(ext);
1427  ssize_t a;
1428 
1429  /* first check the extension is already there */
1430  if ((ext_len <= path_len) && (STREQ(path + (path_len - ext_len), ext))) {
1431  return true;
1432  }
1433 
1434  for (a = path_len - 1; a >= 0; a--) {
1435  if (path[a] == '.') {
1436  path[a] = '\0';
1437  }
1438  else {
1439  break;
1440  }
1441  }
1442  a++;
1443 
1444  if (a + ext_len >= maxlen) {
1445  return false;
1446  }
1447 
1448  memcpy(path + a, ext, ext_len + 1);
1449  return true;
1450 }
1451 
1452 bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filename)
1453 {
1454 #ifdef DEBUG_STRSIZE
1455  memset(filepath, 0xff, sizeof(*filepath) * maxlen);
1456 #endif
1457  char *c = (char *)BLI_path_slash_rfind(filepath);
1458  if (!c || ((c - filepath) < maxlen - (strlen(filename) + 1))) {
1459  strcpy(c ? &c[1] : filepath, filename);
1460  return true;
1461  }
1462  return false;
1463 }
1464 
1466  const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen)
1467 {
1468 #ifdef DEBUG_STRSIZE
1469  memset(dir, 0xff, sizeof(*dir) * dirlen);
1470  memset(file, 0xff, sizeof(*file) * filelen);
1471 #endif
1472  const char *lslash_str = BLI_path_slash_rfind(string);
1473  const size_t lslash = lslash_str ? (size_t)(lslash_str - string) + 1 : 0;
1474 
1475  if (dir) {
1476  if (lslash) {
1477  /* +1 to include the slash and the last char */
1478  BLI_strncpy(dir, string, MIN2(dirlen, lslash + 1));
1479  }
1480  else {
1481  dir[0] = '\0';
1482  }
1483  }
1484 
1485  if (file) {
1486  BLI_strncpy(file, string + lslash, filelen);
1487  }
1488 }
1489 
1490 void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
1491 {
1492  BLI_split_dirfile(string, dir, NULL, dirlen, 0);
1493 }
1494 
1495 void BLI_split_file_part(const char *string, char *file, const size_t filelen)
1496 {
1497  BLI_split_dirfile(string, NULL, file, 0, filelen);
1498 }
1499 
1500 const char *BLI_path_extension(const char *filepath)
1501 {
1502  const char *extension = strrchr(filepath, '.');
1503  if (extension == NULL) {
1504  return NULL;
1505  }
1506  if (BLI_path_slash_find(extension) != NULL) {
1507  /* There is a path separator in the extension, so the '.' was found in a
1508  * directory component and not in the filename. */
1509  return NULL;
1510  }
1511  return extension;
1512 }
1513 
1514 void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file)
1515 {
1516  size_t dirlen = BLI_strnlen(dst, maxlen);
1517 
1518  /* inline BLI_path_slash_ensure */
1519  if ((dirlen > 0) && (dst[dirlen - 1] != SEP)) {
1520  dst[dirlen++] = SEP;
1521  dst[dirlen] = '\0';
1522  }
1523 
1524  if (dirlen >= maxlen) {
1525  return; /* fills the path */
1526  }
1527 
1528  BLI_strncpy(dst + dirlen, file, maxlen - dirlen);
1529 }
1530 
1531 void BLI_join_dirfile(char *__restrict dst,
1532  const size_t maxlen,
1533  const char *__restrict dir,
1534  const char *__restrict file)
1535 {
1536 #ifdef DEBUG_STRSIZE
1537  memset(dst, 0xff, sizeof(*dst) * maxlen);
1538 #endif
1539  size_t dirlen = BLI_strnlen(dir, maxlen);
1540 
1541  /* Arguments can't match. */
1542  BLI_assert(!ELEM(dst, dir, file));
1543 
1544  /* Files starting with a separator cause a double-slash which could later be interpreted
1545  * as a relative path where: `dir == "/"` and `file == "/file"` would result in "//file". */
1546  BLI_assert(file[0] != SEP);
1547 
1548  if (dirlen == maxlen) {
1549  memcpy(dst, dir, dirlen);
1550  dst[dirlen - 1] = '\0';
1551  return; /* dir fills the path */
1552  }
1553 
1554  memcpy(dst, dir, dirlen + 1);
1555 
1556  if (dirlen + 1 >= maxlen) {
1557  return; /* fills the path */
1558  }
1559 
1560  /* inline BLI_path_slash_ensure */
1561  if ((dirlen > 0) && !ELEM(dst[dirlen - 1], SEP, ALTSEP)) {
1562  dst[dirlen++] = SEP;
1563  dst[dirlen] = '\0';
1564  }
1565 
1566  if (dirlen >= maxlen) {
1567  return; /* fills the path */
1568  }
1569 
1570  BLI_strncpy(dst + dirlen, file, maxlen - dirlen);
1571 }
1572 
1573 size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *path, ...)
1574 {
1575 #ifdef DEBUG_STRSIZE
1576  memset(dst, 0xff, sizeof(*dst) * dst_len);
1577 #endif
1578  if (UNLIKELY(dst_len == 0)) {
1579  return 0;
1580  }
1581  const size_t dst_last = dst_len - 1;
1582  size_t ofs = BLI_strncpy_rlen(dst, path, dst_len);
1583 
1584  if (ofs == dst_last) {
1585  return ofs;
1586  }
1587 
1588  /* remove trailing slashes, unless there are _only_ trailing slashes
1589  * (allow "//" as the first argument). */
1590  bool has_trailing_slash = false;
1591  if (ofs != 0) {
1592  size_t len = ofs;
1593  while ((len != 0) && ELEM(path[len - 1], SEP, ALTSEP)) {
1594  len -= 1;
1595  }
1596  if (len != 0) {
1597  ofs = len;
1598  }
1599  has_trailing_slash = (path[len] != '\0');
1600  }
1601 
1602  va_list args;
1603  va_start(args, path);
1604  while ((path = (const char *)va_arg(args, const char *))) {
1605  has_trailing_slash = false;
1606  const char *path_init = path;
1607  while (ELEM(path[0], SEP, ALTSEP)) {
1608  path++;
1609  }
1610  size_t len = strlen(path);
1611  if (len != 0) {
1612  while ((len != 0) && ELEM(path[len - 1], SEP, ALTSEP)) {
1613  len -= 1;
1614  }
1615 
1616  if (len != 0) {
1617  /* the very first path may have a slash at the end */
1618  if (ofs && !ELEM(dst[ofs - 1], SEP, ALTSEP)) {
1619  dst[ofs++] = SEP;
1620  if (ofs == dst_last) {
1621  break;
1622  }
1623  }
1624  has_trailing_slash = (path[len] != '\0');
1625  if (ofs + len >= dst_last) {
1626  len = dst_last - ofs;
1627  }
1628  memcpy(&dst[ofs], path, len);
1629  ofs += len;
1630  if (ofs == dst_last) {
1631  break;
1632  }
1633  }
1634  }
1635  else {
1636  has_trailing_slash = (path_init != path);
1637  }
1638  }
1639  va_end(args);
1640 
1641  if (has_trailing_slash) {
1642  if ((ofs != dst_last) && (ofs != 0) && (ELEM(dst[ofs - 1], SEP, ALTSEP) == 0)) {
1643  dst[ofs++] = SEP;
1644  }
1645  }
1646 
1647  BLI_assert(ofs <= dst_last);
1648  dst[ofs] = '\0';
1649 
1650  return ofs;
1651 }
1652 
1653 const char *BLI_path_basename(const char *path)
1654 {
1655  const char *const filename = BLI_path_slash_rfind(path);
1656  return filename ? filename + 1 : path;
1657 }
1658 
1659 bool BLI_path_name_at_index(const char *__restrict path,
1660  const int index,
1661  int *__restrict r_offset,
1662  int *__restrict r_len)
1663 {
1664  if (index >= 0) {
1665  int index_step = 0;
1666  int prev = -1;
1667  int i = 0;
1668  while (true) {
1669  const char c = path[i];
1670  if (ELEM(c, SEP, ALTSEP, '\0')) {
1671  if (prev + 1 != i) {
1672  prev += 1;
1673  if (index_step == index) {
1674  *r_offset = prev;
1675  *r_len = i - prev;
1676  // printf("!!! %d %d\n", start, end);
1677  return true;
1678  }
1679  index_step += 1;
1680  }
1681  if (c == '\0') {
1682  break;
1683  }
1684  prev = i;
1685  }
1686  i += 1;
1687  }
1688  return false;
1689  }
1690 
1691  /* negative number, reverse where -1 is the last element */
1692  int index_step = -1;
1693  int prev = strlen(path);
1694  int i = prev - 1;
1695  while (true) {
1696  const char c = i >= 0 ? path[i] : '\0';
1697  if (ELEM(c, SEP, ALTSEP, '\0')) {
1698  if (prev - 1 != i) {
1699  i += 1;
1700  if (index_step == index) {
1701  *r_offset = i;
1702  *r_len = prev - i;
1703  return true;
1704  }
1705  index_step -= 1;
1706  }
1707  if (c == '\0') {
1708  break;
1709  }
1710  prev = i;
1711  }
1712  i -= 1;
1713  }
1714  return false;
1715 }
1716 
1717 bool BLI_path_contains(const char *container_path, const char *containee_path)
1718 {
1719  char container_native[PATH_MAX];
1720  char containee_native[PATH_MAX];
1721 
1722  /* Keep space for a trailing slash. If the path is truncated by this, the containee path is
1723  * longer than PATH_MAX and the result is ill-defined. */
1724  BLI_strncpy(container_native, container_path, PATH_MAX - 1);
1725  BLI_strncpy(containee_native, containee_path, PATH_MAX);
1726 
1727  BLI_path_slash_native(container_native);
1728  BLI_path_slash_native(containee_native);
1729 
1730  BLI_path_normalize(NULL, container_native);
1731  BLI_path_normalize(NULL, containee_native);
1732 
1733 #ifdef WIN32
1734  BLI_str_tolower_ascii(container_native, PATH_MAX);
1735  BLI_str_tolower_ascii(containee_native, PATH_MAX);
1736 #endif
1737 
1738  if (STREQ(container_native, containee_native)) {
1739  /* The paths are equal, they contain each other. */
1740  return true;
1741  }
1742 
1743  /* Add a trailing slash to prevent same-prefix directories from matching.
1744  * e.g. "/some/path" doesn't contain "/some/path_lib". */
1745  BLI_path_slash_ensure(container_native);
1746 
1747  return BLI_str_startswith(containee_native, container_native);
1748 }
1749 
1750 const char *BLI_path_slash_find(const char *string)
1751 {
1752  const char *const ffslash = strchr(string, '/');
1753  const char *const fbslash = strchr(string, '\\');
1754 
1755  if (!ffslash) {
1756  return fbslash;
1757  }
1758  if (!fbslash) {
1759  return ffslash;
1760  }
1761 
1762  return (ffslash < fbslash) ? ffslash : fbslash;
1763 }
1764 
1765 const char *BLI_path_slash_rfind(const char *string)
1766 {
1767  const char *const lfslash = strrchr(string, '/');
1768  const char *const lbslash = strrchr(string, '\\');
1769 
1770  if (!lfslash) {
1771  return lbslash;
1772  }
1773  if (!lbslash) {
1774  return lfslash;
1775  }
1776 
1777  return (lfslash > lbslash) ? lfslash : lbslash;
1778 }
1779 
1780 int BLI_path_slash_ensure(char *string)
1781 {
1782  int len = strlen(string);
1783  if (len == 0 || string[len - 1] != SEP) {
1784  string[len] = SEP;
1785  string[len + 1] = '\0';
1786  return len + 1;
1787  }
1788  return len;
1789 }
1790 
1791 void BLI_path_slash_rstrip(char *string)
1792 {
1793  int len = strlen(string);
1794  while (len) {
1795  if (string[len - 1] == SEP) {
1796  string[len - 1] = '\0';
1797  len--;
1798  }
1799  else {
1800  break;
1801  }
1802  }
1803 }
1804 
1805 void BLI_path_slash_native(char *path)
1806 {
1807 #ifdef WIN32
1808  if (path && BLI_strnlen(path, 3) > 2) {
1809  BLI_str_replace_char(path + 2, ALTSEP, SEP);
1810  }
1811 #else
1813 #endif
1814 }
1815 
1816 int BLI_path_cmp_normalized(const char *p1, const char *p2)
1817 {
1818  BLI_assert_msg(!BLI_path_is_rel(p1) && !BLI_path_is_rel(p2), "Paths arguments must be absolute");
1819 
1820  /* Normalize the paths so we can compare them. */
1821  char norm_p1[FILE_MAX];
1822  char norm_p2[FILE_MAX];
1823 
1824  BLI_strncpy(norm_p1, p1, sizeof(norm_p1));
1825  BLI_strncpy(norm_p2, p2, sizeof(norm_p2));
1826 
1827  BLI_path_slash_native(norm_p1);
1828  BLI_path_slash_native(norm_p2);
1829 
1830  BLI_path_normalize(NULL, norm_p1);
1831  BLI_path_normalize(NULL, norm_p2);
1832 
1833  return BLI_path_cmp(norm_p1, norm_p2);
1834 }
#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.c:314
char * BLI_current_working_dir(char *dir, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:59
bool BLI_dir_create_recursive(const char *dir) ATTR_NONNULL()
Definition: fileops.c:1219
int BLI_access(const char *filepath, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:927
#define PATH_MAX
Definition: BLI_fileops.h:29
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:102
#define ALTSEP
#define FILE_MAX
#define SEP
#define BLI_path_cmp
void BLI_str_replace_char(char *str, char src, char dst) ATTR_NONNULL()
Definition: string.c:503
bool BLI_str_startswith(const char *__restrict str, const char *__restrict start) ATTR_NONNULL()
Definition: string.c:860
size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:120
size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:899
int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:623
size_t BLI_strcpy_rlen(char *__restrict dst, const char *__restrict src) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:134
void BLI_str_tolower_ascii(char *str, size_t len) ATTR_NONNULL()
Definition: string.c:927
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:42
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
unsigned int uint
Definition: BLI_sys_types.h:67
unsigned short ushort
Definition: BLI_sys_types.h:68
#define ARRAY_SIZE(arr)
#define MAX2(a, b)
#define UNLIKELY(x)
#define ELEM(...)
#define MIN2(a, b)
#define STREQ(a, b)
#define LIKELY(x)
Compatibility-like things for windows.
void BLI_windows_get_default_root_dir(char root_dir[4])
SSIZE_T ssize_t
Definition: BLI_winstuff.h:71
#define S_ISDIR(x)
Definition: BLI_winstuff.h:48
These structs are the foundation for all linked lists in the library system.
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble GLdouble r _GL_VOID_RET _GL_VOID GLfloat GLfloat r _GL_VOID_RET _GL_VOID GLint GLint r _GL_VOID_RET _GL_VOID GLshort GLshort r _GL_VOID_RET _GL_VOID GLdouble GLdouble r
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
Read Guarded memory(de)allocation.
FILE * file
AnimationBackup * backup
int len
Definition: draw_manager.c:108
#define str(s)
ccl_global float * buffer
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
#define L
static unsigned c
Definition: RandGen.cpp:83
static unsigned a[3]
Definition: RandGen.cpp:78
SymEdge< T > * prev(const SymEdge< T > *se)
Definition: delaunay_2d.cc:105
void path_init(const string &path, const string &user_path)
Definition: path.cpp:325
const char * BLI_path_slash_rfind(const char *string)
Definition: path_util.c:1765
static bool stringframe_chars(const char *path, int *char_start, int *char_end)
Definition: path_util.c:655
bool BLI_path_make_safe(char *path)
Definition: path_util.c:314
#define INVALID_CHARS
bool BLI_path_abs(char *path, const char *basepath)
Definition: path_util.c:897
bool BLI_path_parent_dir_until_exists(char *dir)
Definition: path_util.c:639
bool BLI_filename_make_safe(char *fname)
Definition: path_util.c:309
size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *path,...)
Definition: path_util.c:1573
void BLI_path_normalize_dir(const char *relabase, char *dir)
Definition: path_util.c:221
void BLI_path_slash_native(char *path)
Definition: path_util.c:1805
void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
Definition: path_util.c:1490
const char * BLI_path_extension(const char *filepath)
Definition: path_util.c:1500
bool BLI_path_extension_replace(char *path, size_t maxlen, const char *ext)
Definition: path_util.c:1393
void BLI_path_normalize(const char *relabase, char *path)
Definition: path_util.c:131
bool BLI_path_parent_dir(char *path)
Definition: path_util.c:623
static bool path_extension_check_ex(const char *str, const size_t str_len, const char *ext, const size_t ext_len)
Definition: path_util.c:1287
bool BLI_path_is_abs_from_cwd(const char *path)
Definition: path_util.c:998
bool BLI_make_existing_file(const char *name)
Definition: path_util.c:1197
bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *name)
Definition: path_util.c:1091
bool BLI_path_extension_ensure(char *path, size_t maxlen, const char *ext)
Definition: path_util.c:1420
bool BLI_filename_make_safe_ex(char *fname, bool allow_tokens)
Definition: path_util.c:232
int BLI_path_cmp_normalized(const char *p1, const char *p2)
Definition: path_util.c:1816
bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char *sep)
Definition: path_util.c:588
bool BLI_path_extension_check_glob(const char *str, const char *ext_fnmatch)
Definition: path_util.c:1341
bool BLI_path_extension_check_array(const char *str, const char **ext_array)
Definition: path_util.c:1326
const char * BLI_path_slash_find(const char *string)
Definition: path_util.c:1750
bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filename)
Definition: path_util.c:1452
void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file)
Definition: path_util.c:1206
bool BLI_path_extension_check_n(const char *str,...)
Definition: path_util.c:1304
bool BLI_path_extension_check(const char *str, const char *ext)
Definition: path_util.c:1299
bool BLI_path_contains(const char *container_path, const char *containee_path)
Definition: path_util.c:1717
void BLI_path_rel(char *file, const char *relfile)
Definition: path_util.c:450
void BLI_path_slash_rstrip(char *string)
Definition: path_util.c:1791
void BLI_path_sequence_encode(char *string, const char *head, const char *tail, unsigned short numlen, int pic)
Definition: path_util.c:123
void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file)
Definition: path_util.c:1514
bool BLI_path_frame_range(char *path, int sta, int end, int digits)
Definition: path_util.c:727
bool BLI_path_frame_check_chars(const char *path)
Definition: path_util.c:853
bool BLI_path_abs_from_cwd(char *path, const size_t maxlen)
Definition: path_util.c:1015
bool BLI_path_extension_glob_validate(char *ext_fnmatch)
Definition: path_util.c:1367
const char * BLI_getenv(const char *env)
Definition: path_util.c:1168
static void ensure_digits(char *path, int digits)
Definition: path_util.c:691
bool BLI_path_is_rel(const char *path)
Definition: path_util.c:347
static int BLI_path_unc_prefix_len(const char *path)
Definition: path_util.c:363
bool BLI_path_is_unc(const char *name)
Definition: path_util.c:352
void BLI_join_dirfile(char *__restrict dst, const size_t maxlen, const char *__restrict dir, const char *__restrict file)
Definition: path_util.c:1531
void BLI_setenv(const char *env, const char *val)
Definition: path_util.c:1143
int BLI_path_slash_ensure(char *string)
Definition: path_util.c:1780
void BLI_path_frame_strip(char *path, char *r_ext, const size_t ext_maxlen)
Definition: path_util.c:805
void BLI_path_to_display_name(char *display_name, int maxlen, const char *name)
Definition: path_util.c:859
void BLI_setenv_if_new(const char *env, const char *val)
Definition: path_util.c:1161
bool BLI_path_name_at_index(const char *__restrict path, const int index, int *__restrict r_offset, int *__restrict r_len)
Definition: path_util.c:1659
bool BLI_path_frame(char *path, int frame, int digits)
Definition: path_util.c:709
void BLI_split_dirfile(const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen)
Definition: path_util.c:1465
const char * BLI_path_basename(const char *path)
Definition: path_util.c:1653
void BLI_split_file_part(const char *string, char *file, const size_t filelen)
Definition: path_util.c:1495
#define INVALID_TOKENS
int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort *r_digits_len)
Definition: path_util.c:56
bool BLI_path_frame_get(char *path, int *r_frame, int *r_digits_len)
Definition: path_util.c:753
return ret
int uputenv(const char *name, const char *value)
Definition: utf_winfunc.c:146
wchar_t * alloc_utf16_from_8(const char *in8, size_t add)
Definition: utfconv.c:291
char * alloc_utf_8_from_16(const wchar_t *in16, size_t add)
Definition: utfconv.c:279
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8)
Definition: utfconv.c:115