Blender  V2.93
path_util.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  * various string, file, list operations.
19  */
20 
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "DNA_listBase.h"
30 
31 #include "BLI_fileops.h"
32 #include "BLI_fnmatch.h"
33 #include "BLI_path_util.h"
34 #include "BLI_string.h"
35 #include "BLI_string_utf8.h"
36 #include "BLI_utildefines.h"
37 
38 #ifdef WIN32
39 # include "utf_winfunc.h"
40 # include "utfconv.h"
41 # include <io.h>
42 # ifdef _WIN32_IE
43 # undef _WIN32_IE
44 # endif
45 # define _WIN32_IE 0x0501
46 # include "BLI_alloca.h"
47 # include "BLI_winstuff.h"
48 # include <shlobj.h>
49 # include <windows.h>
50 #else
51 # include "unistd.h"
52 #endif /* WIN32 */
53 
54 #include "MEM_guardedalloc.h"
55 
56 /* Declarations */
57 
58 #ifdef WIN32
59 
64 static bool BLI_path_is_abs(const char *name);
65 
66 #endif /* WIN32 */
67 
68 // #define DEBUG_STRSIZE
69 
70 /* implementation */
71 
83 int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort *r_num_len)
84 {
85  uint nums = 0, nume = 0;
86  int i;
87  bool found_digit = false;
88  const char *const lslash = BLI_path_slash_rfind(string);
89  const uint string_len = strlen(string);
90  const uint lslash_len = lslash != NULL ? (int)(lslash - string) : 0;
91  uint name_end = string_len;
92 
93  while (name_end > lslash_len && string[--name_end] != '.') {
94  /* name ends at dot if present */
95  }
96  if (name_end == lslash_len && string[name_end] != '.') {
97  name_end = string_len;
98  }
99 
100  for (i = name_end - 1; i >= (int)lslash_len; i--) {
101  if (isdigit(string[i])) {
102  if (found_digit) {
103  nums = i;
104  }
105  else {
106  nume = i;
107  nums = i;
108  found_digit = true;
109  }
110  }
111  else {
112  if (found_digit) {
113  break;
114  }
115  }
116  }
117 
118  if (found_digit) {
119  const long long int ret = strtoll(&(string[nums]), NULL, 10);
120  if (ret >= INT_MIN && ret <= INT_MAX) {
121  if (tail) {
122  strcpy(tail, &string[nume + 1]);
123  }
124  if (head) {
125  strcpy(head, string);
126  head[nums] = 0;
127  }
128  if (r_num_len) {
129  *r_num_len = nume - nums + 1;
130  }
131  return (int)ret;
132  }
133  }
134 
135  if (tail) {
136  strcpy(tail, string + name_end);
137  }
138  if (head) {
139  /* name_end points to last character of head,
140  * make it +1 so null-terminator is nicely placed
141  */
142  BLI_strncpy(head, string, name_end + 1);
143  }
144  if (r_num_len) {
145  *r_num_len = 0;
146  }
147  return 0;
148 }
149 
155  char *string, const char *head, const char *tail, unsigned short numlen, int pic)
156 {
157  sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail);
158 }
159 
160 static int BLI_path_unc_prefix_len(const char *path); /* defined below in same file */
161 
162 /* ******************** string encoding ***************** */
163 
173 void BLI_path_normalize(const char *relabase, char *path)
174 {
175  ptrdiff_t a;
176  char *start, *eind;
177  if (relabase) {
178  BLI_path_abs(path, relabase);
179  }
180  else {
181  if (path[0] == '/' && path[1] == '/') {
182  if (path[2] == '\0') {
183  return; /* path is "//" - cant clean it */
184  }
185  path = path + 2; /* leave the initial "//" untouched */
186  }
187  }
188 
189  /* Note
190  * memmove(start, eind, strlen(eind) + 1);
191  * is the same as
192  * strcpy(start, eind);
193  * except strcpy should not be used because there is overlap,
194  * so use memmove's slightly more obscure syntax - Campbell
195  */
196 
197 #ifdef WIN32
198  while ((start = strstr(path, "\\..\\"))) {
199  eind = start + strlen("\\..\\") - 1;
200  a = start - path - 1;
201  while (a > 0) {
202  if (path[a] == '\\') {
203  break;
204  }
205  a--;
206  }
207  if (a < 0) {
208  break;
209  }
210  else {
211  memmove(path + a, eind, strlen(eind) + 1);
212  }
213  }
214 
215  while ((start = strstr(path, "\\.\\"))) {
216  eind = start + strlen("\\.\\") - 1;
217  memmove(start, eind, strlen(eind) + 1);
218  }
219 
220  /* remove two consecutive backslashes, but skip the UNC prefix,
221  * which needs to be preserved */
222  while ((start = strstr(path + BLI_path_unc_prefix_len(path), "\\\\"))) {
223  eind = start + strlen("\\\\") - 1;
224  memmove(start, eind, strlen(eind) + 1);
225  }
226 #else
227  while ((start = strstr(path, "/../"))) {
228  a = start - path - 1;
229  if (a > 0) {
230  /* <prefix>/<parent>/../<postfix> => <prefix>/<postfix> */
231  eind = start + (4 - 1) /* strlen("/../") - 1 */; /* strip "/.." and keep last "/" */
232  while (a > 0 && path[a] != '/') { /* find start of <parent> */
233  a--;
234  }
235  memmove(path + a, eind, strlen(eind) + 1);
236  }
237  else {
238  /* support for odd paths: eg /../home/me --> /home/me
239  * this is a valid path in blender but we cant handle this the usual way below
240  * simply strip this prefix then evaluate the path as usual.
241  * pythons os.path.normpath() does this */
242 
243  /* Note: previous version of following call used an offset of 3 instead of 4,
244  * which meant that the "/../home/me" example actually became "home/me".
245  * Using offset of 3 gives behavior consistent with the aforementioned
246  * Python routine. */
247  memmove(path, path + 3, strlen(path + 3) + 1);
248  }
249  }
250 
251  while ((start = strstr(path, "/./"))) {
252  eind = start + (3 - 1) /* strlen("/./") - 1 */;
253  memmove(start, eind, strlen(eind) + 1);
254  }
255 
256  while ((start = strstr(path, "//"))) {
257  eind = start + (2 - 1) /* strlen("//") - 1 */;
258  memmove(start, eind, strlen(eind) + 1);
259  }
260 #endif
261 }
262 
266 void BLI_path_normalize_dir(const char *relabase, char *dir)
267 {
268  /* Would just create an unexpected "/" path, just early exit entirely. */
269  if (dir[0] == '\0') {
270  return;
271  }
272 
273  BLI_path_normalize(relabase, dir);
275 }
276 
299 bool BLI_filename_make_safe(char *fname)
300 {
301  const char *invalid =
302  "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
303  "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
304  "/\\?*:|\"<>";
305  char *fn;
306  bool changed = false;
307 
308  if (*fname == '\0') {
309  return changed;
310  }
311 
312  for (fn = fname; *fn && (fn = strpbrk(fn, invalid)); fn++) {
313  *fn = '_';
314  changed = true;
315  }
316 
317  /* Forbid only dots. */
318  for (fn = fname; *fn == '.'; fn++) {
319  /* pass */
320  }
321  if (*fn == '\0') {
322  *fname = '_';
323  changed = true;
324  }
325 
326 #ifdef WIN32
327  {
328  const size_t len = strlen(fname);
329  const char *invalid_names[] = {
330  "con", "prn", "aux", "null", "com1", "com2", "com3", "com4",
331  "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3",
332  "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", NULL,
333  };
334  char *lower_fname = BLI_strdup(fname);
335  const char **iname;
336 
337  /* Forbid trailing dot (trailing space has already been replaced above). */
338  if (fname[len - 1] == '.') {
339  fname[len - 1] = '_';
340  changed = true;
341  }
342 
343  /* Check for forbidden names - not we have to check all combination
344  * of upper and lower cases, hence the usage of lower_fname
345  * (more efficient than using BLI_strcasestr repeatedly). */
346  BLI_str_tolower_ascii(lower_fname, len);
347  for (iname = invalid_names; *iname; iname++) {
348  if (strstr(lower_fname, *iname) == lower_fname) {
349  const size_t iname_len = strlen(*iname);
350  /* Only invalid if the whole name is made of the invalid chunk, or it has an
351  * (assumed extension) dot just after. This means it will also catch 'valid'
352  * names like 'aux.foo.bar', but should be
353  * good enough for us! */
354  if ((iname_len == len) || (lower_fname[iname_len] == '.')) {
355  *fname = '_';
356  changed = true;
357  break;
358  }
359  }
360  }
361 
362  MEM_freeN(lower_fname);
363  }
364 #endif
365 
366  return changed;
367 }
368 
374 bool BLI_path_make_safe(char *path)
375 {
376  /* Simply apply BLI_filename_make_safe() over each component of the path.
377  * Luckily enough, same 'safe' rules applies to filenames and dirnames. */
378  char *curr_slash, *curr_path = path;
379  bool changed = false;
380  bool skip_first = false;
381 
382 #ifdef WIN32
383  if (BLI_path_is_abs(path)) {
384  /* Do not make safe 'C:' in 'C:\foo\bar'... */
385  skip_first = true;
386  }
387 #endif
388 
389  for (curr_slash = (char *)BLI_path_slash_find(curr_path); curr_slash;
390  curr_slash = (char *)BLI_path_slash_find(curr_path)) {
391  const char backup = *curr_slash;
392  *curr_slash = '\0';
393  if (!skip_first && (*curr_path != '\0') && BLI_filename_make_safe(curr_path)) {
394  changed = true;
395  }
396  skip_first = false;
397  curr_path = curr_slash + 1;
398  *curr_slash = backup;
399  }
400  if (BLI_filename_make_safe(curr_path)) {
401  changed = true;
402  }
403 
404  return changed;
405 }
406 
411 bool BLI_path_is_rel(const char *path)
412 {
413  return path[0] == '/' && path[1] == '/';
414 }
415 
416 /* return true if the path is a UNC share */
417 bool BLI_path_is_unc(const char *name)
418 {
419  return name[0] == '\\' && name[1] == '\\';
420 }
421 
428 static int BLI_path_unc_prefix_len(const char *path)
429 {
430  if (BLI_path_is_unc(path)) {
431  if ((path[2] == '?') && (path[3] == '\\')) {
432  /* we assume long UNC path like \\?\server\share\folder etc... */
433  return 4;
434  }
435 
436  return 2;
437  }
438 
439  return 0;
440 }
441 
442 #if defined(WIN32)
443 
448 static bool BLI_path_is_abs(const char *name)
449 {
450  return (name[1] == ':' && ELEM(name[2], '\\', '/')) || BLI_path_is_unc(name);
451 }
452 
453 static wchar_t *next_slash(wchar_t *path)
454 {
455  wchar_t *slash = path;
456  while (*slash && *slash != L'\\') {
457  slash++;
458  }
459  return slash;
460 }
461 
462 /* Adds a slash if the UNC path points to a share. */
463 static void BLI_path_add_slash_to_share(wchar_t *uncpath)
464 {
465  wchar_t *slash_after_server = next_slash(uncpath + 2);
466  if (*slash_after_server) {
467  wchar_t *slash_after_share = next_slash(slash_after_server + 1);
468  if (!(*slash_after_share)) {
469  slash_after_share[0] = L'\\';
470  slash_after_share[1] = L'\0';
471  }
472  }
473 }
474 
475 static void BLI_path_unc_to_short(wchar_t *unc)
476 {
477  wchar_t tmp[PATH_MAX];
478 
479  int len = wcslen(unc);
480  /* convert:
481  * \\?\UNC\server\share\folder\... to \\server\share\folder\...
482  * \\?\C:\ to C:\ and \\?\C:\folder\... to C:\folder\...
483  */
484  if ((len > 3) && (unc[0] == L'\\') && (unc[1] == L'\\') && (unc[2] == L'?') &&
485  ELEM(unc[3], L'\\', L'/')) {
486  if ((len > 5) && (unc[5] == L':')) {
487  wcsncpy(tmp, unc + 4, len - 4);
488  tmp[len - 4] = L'\0';
489  wcscpy(unc, tmp);
490  }
491  else if ((len > 7) && (wcsncmp(&unc[4], L"UNC", 3) == 0) && ELEM(unc[7], L'\\', L'/')) {
492  tmp[0] = L'\\';
493  tmp[1] = L'\\';
494  wcsncpy(tmp + 2, unc + 8, len - 8);
495  tmp[len - 6] = L'\0';
496  wcscpy(unc, tmp);
497  }
498  }
499 }
500 
501 void BLI_path_normalize_unc(char *path, int maxlen)
502 {
503  wchar_t *tmp_16 = alloc_utf16_from_8(path, 1);
504  BLI_path_normalize_unc_16(tmp_16);
505  conv_utf_16_to_8(tmp_16, path, maxlen);
506 }
507 
508 void BLI_path_normalize_unc_16(wchar_t *path_16)
509 {
510  BLI_path_unc_to_short(path_16);
511  BLI_path_add_slash_to_share(path_16);
512 }
513 #endif
514 
519 void BLI_path_rel(char *file, const char *relfile)
520 {
521  const char *lslash;
522  char temp[FILE_MAX];
523  char res[FILE_MAX];
524 
525  /* if file is already relative, bail out */
526  if (BLI_path_is_rel(file)) {
527  return;
528  }
529 
530  /* also bail out if relative path is not set */
531  if (relfile[0] == '\0') {
532  return;
533  }
534 
535 #ifdef WIN32
536  if (BLI_strnlen(relfile, 3) > 2 && !BLI_path_is_abs(relfile)) {
537  char *ptemp;
538  /* fix missing volume name in relative base,
539  * can happen with old recent-files.txt files */
541  ptemp = &temp[2];
542  if (relfile[0] != '\\' && relfile[0] != '/') {
543  ptemp++;
544  }
545  BLI_strncpy(ptemp, relfile, FILE_MAX - 3);
546  }
547  else {
548  BLI_strncpy(temp, relfile, FILE_MAX);
549  }
550 
551  if (BLI_strnlen(file, 3) > 2) {
552  bool is_unc = BLI_path_is_unc(file);
553 
554  /* Ensure paths are both UNC paths or are both drives */
555  if (BLI_path_is_unc(temp) != is_unc) {
556  return;
557  }
558 
559  /* Ensure both UNC paths are on the same share */
560  if (is_unc) {
561  int off;
562  int slash = 0;
563  for (off = 0; temp[off] && slash < 4; off++) {
564  if (temp[off] != file[off]) {
565  return;
566  }
567 
568  if (temp[off] == '\\') {
569  slash++;
570  }
571  }
572  }
573  else if ((temp[1] == ':' && file[1] == ':') && (tolower(temp[0]) != tolower(file[0]))) {
574  return;
575  }
576  }
577 #else
578  BLI_strncpy(temp, relfile, FILE_MAX);
579 #endif
580 
581  BLI_str_replace_char(temp + BLI_path_unc_prefix_len(temp), '\\', '/');
583 
584  /* remove /./ which confuse the following slash counting... */
586  BLI_path_normalize(NULL, temp);
587 
588  /* the last slash in the file indicates where the path part ends */
589  lslash = BLI_path_slash_rfind(temp);
590 
591  if (lslash) {
592  /* find the prefix of the filename that is equal for both filenames.
593  * This is replaced by the two slashes at the beginning */
594  const char *p = temp;
595  const char *q = file;
596  char *r = res;
597 
598 #ifdef WIN32
599  while (tolower(*p) == tolower(*q))
600 #else
601  while (*p == *q)
602 #endif
603  {
604  p++;
605  q++;
606 
607  /* don't search beyond the end of the string
608  * in the rare case they match */
609  if ((*p == '\0') || (*q == '\0')) {
610  break;
611  }
612  }
613 
614  /* we might have passed the slash when the beginning of a dir matches
615  * so we rewind. Only check on the actual filename
616  */
617  if (*q != '/') {
618  while ((q >= file) && (*q != '/')) {
619  q--;
620  p--;
621  }
622  }
623  else if (*p != '/') {
624  while ((p >= temp) && (*p != '/')) {
625  p--;
626  q--;
627  }
628  }
629 
630  r += BLI_strcpy_rlen(r, "//");
631 
632  /* p now points to the slash that is at the beginning of the part
633  * where the path is different from the relative path.
634  * We count the number of directories we need to go up in the
635  * hierarchy to arrive at the common 'prefix' of the path
636  */
637  if (p < temp) {
638  p = temp;
639  }
640  while (p && p < lslash) {
641  if (*p == '/') {
642  r += BLI_strcpy_rlen(r, "../");
643  }
644  p++;
645  }
646 
647  /* don't copy the slash at the beginning */
648  r += BLI_strncpy_rlen(r, q + 1, FILE_MAX - (r - res));
649 
650 #ifdef WIN32
651  BLI_str_replace_char(res + 2, '/', '\\');
652 #endif
653  strcpy(file, res);
654  }
655 }
656 
669 bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char *sep)
670 {
671 #ifdef DEBUG_STRSIZE
672  memset(string, 0xff, sizeof(*string) * maxlen);
673 #endif
674  const size_t string_len = strlen(string);
675  const size_t suffix_len = strlen(suffix);
676  const size_t sep_len = strlen(sep);
677  ssize_t a;
678  char extension[FILE_MAX];
679  bool has_extension = false;
680 
681  if (string_len + sep_len + suffix_len >= maxlen) {
682  return false;
683  }
684 
685  for (a = string_len - 1; a >= 0; a--) {
686  if (string[a] == '.') {
687  has_extension = true;
688  break;
689  }
690  if (ELEM(string[a], '/', '\\')) {
691  break;
692  }
693  }
694 
695  if (!has_extension) {
696  a = string_len;
697  }
698 
699  BLI_strncpy(extension, string + a, sizeof(extension));
700  sprintf(string + a, "%s%s%s", sep, suffix, extension);
701  return true;
702 }
703 
708 bool BLI_path_parent_dir(char *path)
709 {
710  const char parent_dir[] = {'.', '.', SEP, '\0'}; /* "../" or "..\\" */
711  char tmp[FILE_MAX + 4];
712 
713  BLI_join_dirfile(tmp, sizeof(tmp), path, parent_dir);
714  BLI_path_normalize(NULL, tmp); /* does all the work of normalizing the path for us */
715 
716  if (!BLI_path_extension_check(tmp, parent_dir)) {
717  strcpy(path, tmp); /* We assume pardir is always shorter... */
718  return true;
719  }
720 
721  return false;
722 }
723 
729 {
730  bool valid_path = true;
731 
732  /* Loop as long as cur path is not a dir, and we can get a parent path. */
733  while ((BLI_access(dir, R_OK) != 0) && (valid_path = BLI_path_parent_dir(dir))) {
734  /* pass */
735  }
736  return (valid_path && dir[0]);
737 }
738 
744 static bool stringframe_chars(const char *path, int *char_start, int *char_end)
745 {
746  uint ch_sta, ch_end, i;
747  /* Insert current frame: file### -> file001 */
748  ch_sta = ch_end = 0;
749  for (i = 0; path[i] != '\0'; i++) {
750  if (ELEM(path[i], '\\', '/')) {
751  ch_end = 0; /* this is a directory name, don't use any hashes we found */
752  }
753  else if (path[i] == '#') {
754  ch_sta = i;
755  ch_end = ch_sta + 1;
756  while (path[ch_end] == '#') {
757  ch_end++;
758  }
759  i = ch_end - 1; /* keep searching */
760 
761  /* don't break, there may be a slash after this that invalidates the previous #'s */
762  }
763  }
764 
765  if (ch_end) {
766  *char_start = ch_sta;
767  *char_end = ch_end;
768  return true;
769  }
770 
771  *char_start = -1;
772  *char_end = -1;
773  return false;
774 }
775 
780 static void ensure_digits(char *path, int digits)
781 {
782  char *file = (char *)BLI_path_slash_rfind(path);
783 
784  if (file == NULL) {
785  file = path;
786  }
787 
788  if (strrchr(file, '#') == NULL) {
789  int len = strlen(file);
790 
791  while (digits--) {
792  file[len++] = '#';
793  }
794  file[len] = '\0';
795  }
796 }
797 
802 bool BLI_path_frame(char *path, int frame, int digits)
803 {
804  int ch_sta, ch_end;
805 
806  if (digits) {
807  ensure_digits(path, digits);
808  }
809 
810  if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
811  char tmp[FILE_MAX];
812  BLI_snprintf(
813  tmp, sizeof(tmp), "%.*s%.*d%s", ch_sta, path, ch_end - ch_sta, frame, path + ch_end);
814  BLI_strncpy(path, tmp, FILE_MAX);
815  return true;
816  }
817  return false;
818 }
819 
825 bool BLI_path_frame_range(char *path, int sta, int end, int digits)
826 {
827  int ch_sta, ch_end;
828 
829  if (digits) {
830  ensure_digits(path, digits);
831  }
832 
833  if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
834  char tmp[FILE_MAX];
835  BLI_snprintf(tmp,
836  sizeof(tmp),
837  "%.*s%.*d-%.*d%s",
838  ch_sta,
839  path,
840  ch_end - ch_sta,
841  sta,
842  ch_end - ch_sta,
843  end,
844  path + ch_end);
845  BLI_strncpy(path, tmp, FILE_MAX);
846  return true;
847  }
848  return false;
849 }
850 
854 bool BLI_path_frame_get(char *path, int *r_frame, int *r_numdigits)
855 {
856  if (*path) {
857  char *file = (char *)BLI_path_slash_rfind(path);
858  char *c;
859  int len, numdigits;
860 
861  numdigits = *r_numdigits = 0;
862 
863  if (file == NULL) {
864  file = path;
865  }
866 
867  /* first get the extension part */
868  len = strlen(file);
869 
870  c = file + len;
871 
872  /* isolate extension */
873  while (--c != file) {
874  if (*c == '.') {
875  c--;
876  break;
877  }
878  }
879 
880  /* find start of number */
881  while (c != (file - 1) && isdigit(*c)) {
882  c--;
883  numdigits++;
884  }
885 
886  if (numdigits) {
887  char prevchar;
888 
889  c++;
890  prevchar = c[numdigits];
891  c[numdigits] = 0;
892 
893  /* was the number really an extension? */
894  *r_frame = atoi(c);
895  c[numdigits] = prevchar;
896 
897  *r_numdigits = numdigits;
898 
899  return true;
900  }
901  }
902 
903  return false;
904 }
905 
906 void BLI_path_frame_strip(char *path, char *r_ext)
907 {
908  *r_ext = '\0';
909  if (*path == '\0') {
910  return;
911  }
912 
913  char *file = (char *)BLI_path_slash_rfind(path);
914  char *c, *suffix;
915  int len;
916  int numdigits = 0;
917 
918  if (file == NULL) {
919  file = path;
920  }
921 
922  /* first get the extension part */
923  len = strlen(file);
924 
925  c = file + len;
926 
927  /* isolate extension */
928  while (--c != file) {
929  if (*c == '.') {
930  c--;
931  break;
932  }
933  }
934 
935  suffix = c + 1;
936 
937  /* find start of number */
938  while (c != (file - 1) && isdigit(*c)) {
939  c--;
940  numdigits++;
941  }
942 
943  c++;
944 
945  int suffix_length = len - (suffix - file);
946  BLI_strncpy(r_ext, suffix, suffix_length + 1);
947 
948  /* replace the number with the suffix and terminate the string */
949  while (numdigits--) {
950  *c++ = '#';
951  }
952  *c = '\0';
953 }
954 
958 bool BLI_path_frame_check_chars(const char *path)
959 {
960  int ch_sta, ch_end; /* dummy args */
961  return stringframe_chars(path, &ch_sta, &ch_end);
962 }
963 
968 void BLI_path_to_display_name(char *display_name, int maxlen, const char *name)
969 {
970  /* Strip leading underscores and spaces. */
971  int strip_offset = 0;
972  while (ELEM(name[strip_offset], '_', ' ')) {
973  strip_offset++;
974  }
975 
976  BLI_strncpy(display_name, name + strip_offset, maxlen);
977 
978  /* Replace underscores with spaces. */
979  BLI_str_replace_char(display_name, '_', ' ');
980 
981  /* Strip extension. */
982  BLI_path_extension_replace(display_name, maxlen, "");
983 
984  /* Test if string has any upper case characters. */
985  bool all_lower = true;
986  for (int i = 0; display_name[i]; i++) {
987  if (isupper(display_name[i])) {
988  all_lower = false;
989  break;
990  }
991  }
992 
993  if (all_lower) {
994  /* For full lowercase string, use title case. */
995  bool prevspace = true;
996  for (int i = 0; display_name[i]; i++) {
997  if (prevspace) {
998  display_name[i] = toupper(display_name[i]);
999  }
1000 
1001  prevspace = isspace(display_name[i]);
1002  }
1003  }
1004 }
1005 
1016 bool BLI_path_abs(char *path, const char *basepath)
1017 {
1018  const bool wasrelative = BLI_path_is_rel(path);
1019  char tmp[FILE_MAX];
1020  char base[FILE_MAX];
1021 #ifdef WIN32
1022 
1023  /* without this: "" --> "C:\" */
1024  if (*path == '\0') {
1025  return wasrelative;
1026  }
1027 
1028  /* we are checking here if we have an absolute path that is not in the current
1029  * blend file as a lib main - we are basically checking for the case that a
1030  * UNIX root '/' is passed.
1031  */
1032  if (!wasrelative && !BLI_path_is_abs(path)) {
1033  char *p = path;
1035  /* Get rid of the slashes at the beginning of the path. */
1036  while (ELEM(*p, '\\', '/')) {
1037  p++;
1038  }
1039  strcat(tmp, p);
1040  }
1041  else {
1042  BLI_strncpy(tmp, path, FILE_MAX);
1043  }
1044 #else
1045  BLI_strncpy(tmp, path, sizeof(tmp));
1046 
1047  /* Check for loading a MS-Windows path on a POSIX system
1048  * in this case, there is no use in trying `C:/` since it
1049  * will never exist on a Unix system.
1050  *
1051  * Add a `/` prefix and lowercase the drive-letter, remove the `:`.
1052  * `C:\foo.JPG` -> `/c/foo.JPG` */
1053 
1054  if (isalpha(tmp[0]) && (tmp[1] == ':') && ELEM(tmp[2], '\\', '/')) {
1055  tmp[1] = tolower(tmp[0]); /* Replace ':' with drive-letter. */
1056  tmp[0] = '/';
1057  /* `\` the slash will be converted later. */
1058  }
1059 
1060 #endif
1061 
1062  /* push slashes into unix mode - strings entering this part are
1063  * potentially messed up: having both back- and forward slashes.
1064  * Here we push into one conform direction, and at the end we
1065  * push them into the system specific dir. This ensures uniformity
1066  * of paths and solving some problems (and prevent potential future
1067  * ones) -jesterKing.
1068  * For UNC paths the first characters containing the UNC prefix
1069  * shouldn't be switched as we need to distinguish them from
1070  * paths relative to the .blend file -elubie */
1071  BLI_str_replace_char(tmp + BLI_path_unc_prefix_len(tmp), '\\', '/');
1072 
1073  /* Paths starting with // will get the blend file as their base,
1074  * this isn't standard in any os but is used in blender all over the place */
1075  if (wasrelative) {
1076  const char *lslash;
1077  BLI_strncpy(base, basepath, sizeof(base));
1078 
1079  /* file component is ignored, so don't bother with the trailing slash */
1080  BLI_path_normalize(NULL, base);
1081  lslash = BLI_path_slash_rfind(base);
1082  BLI_str_replace_char(base + BLI_path_unc_prefix_len(base), '\\', '/');
1083 
1084  if (lslash) {
1085  /* length up to and including last "/" */
1086  const int baselen = (int)(lslash - base) + 1;
1087  /* use path for temp storage here, we copy back over it right away */
1088  BLI_strncpy(path, tmp + 2, FILE_MAX); /* strip "//" */
1089 
1090  memcpy(tmp, base, baselen); /* prefix with base up to last "/" */
1091  BLI_strncpy(tmp + baselen, path, sizeof(tmp) - baselen); /* append path after "//" */
1092  BLI_strncpy(path, tmp, FILE_MAX); /* return as result */
1093  }
1094  else {
1095  /* base doesn't seem to be a directory--ignore it and just strip "//" prefix on path */
1096  BLI_strncpy(path, tmp + 2, FILE_MAX);
1097  }
1098  }
1099  else {
1100  /* base ignored */
1101  BLI_strncpy(path, tmp, FILE_MAX);
1102  }
1103 
1104 #ifdef WIN32
1105  /* skip first two chars, which in case of
1106  * absolute path will be drive:/blabla and
1107  * in case of relpath //blabla/. So relpath
1108  * // will be retained, rest will be nice and
1109  * shiny win32 backward slashes :) -jesterKing
1110  */
1111  BLI_str_replace_char(path + 2, '/', '\\');
1112 #endif
1113 
1114  /* ensure this is after correcting for path switch */
1115  BLI_path_normalize(NULL, path);
1116 
1117  return wasrelative;
1118 }
1119 
1128 bool BLI_path_abs_from_cwd(char *path, const size_t maxlen)
1129 {
1130 #ifdef DEBUG_STRSIZE
1131  memset(path, 0xff, sizeof(*path) * maxlen);
1132 #endif
1133  bool wasrelative = true;
1134  const int filelen = strlen(path);
1135 
1136 #ifdef WIN32
1137  if ((filelen >= 3 && BLI_path_is_abs(path)) || BLI_path_is_unc(path)) {
1138  wasrelative = false;
1139  }
1140 #else
1141  if (filelen >= 2 && path[0] == '/') {
1142  wasrelative = false;
1143  }
1144 #endif
1145 
1146  if (wasrelative) {
1147  char cwd[FILE_MAX];
1148  /* in case the full path to the blend isn't used */
1149  if (BLI_current_working_dir(cwd, sizeof(cwd))) {
1150  char origpath[FILE_MAX];
1151  BLI_strncpy(origpath, path, FILE_MAX);
1152  BLI_join_dirfile(path, maxlen, cwd, origpath);
1153  }
1154  else {
1155  printf("Could not get the current working directory - $PWD for an unknown reason.\n");
1156  }
1157  }
1158 
1159  return wasrelative;
1160 }
1161 
1162 #ifdef _WIN32
1168 bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen)
1169 {
1170  bool retval = false;
1171  int type;
1172 
1173  type = BLI_exists(name);
1174  if ((type == 0) || S_ISDIR(type)) {
1175  /* typically 3-5, ".EXE", ".BAT"... etc */
1176  const int ext_max = 12;
1177  const char *ext = BLI_getenv("PATHEXT");
1178  if (ext) {
1179  const int name_len = strlen(name);
1180  char *filename = alloca(name_len + ext_max);
1181  char *filename_ext;
1182  const char *ext_next;
1183 
1184  /* null terminated in the loop */
1185  memcpy(filename, name, name_len);
1186  filename_ext = filename + name_len;
1187 
1188  do {
1189  int ext_len;
1190  ext_next = strchr(ext, ';');
1191  ext_len = ext_next ? ((ext_next++) - ext) : strlen(ext);
1192 
1193  if (LIKELY(ext_len < ext_max)) {
1194  memcpy(filename_ext, ext, ext_len);
1195  filename_ext[ext_len] = '\0';
1196 
1197  type = BLI_exists(filename);
1198  if (type && (!S_ISDIR(type))) {
1199  retval = true;
1200  BLI_strncpy(name, filename, maxlen);
1201  break;
1202  }
1203  }
1204  } while ((ext = ext_next));
1205  }
1206  }
1207  else {
1208  retval = true;
1209  }
1210 
1211  return retval;
1212 }
1213 #endif /* WIN32 */
1214 
1218 bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *name)
1219 {
1220 #ifdef DEBUG_STRSIZE
1221  memset(fullname, 0xff, sizeof(*fullname) * maxlen);
1222 #endif
1223  const char *path;
1224  bool retval = false;
1225 
1226 #ifdef _WIN32
1227  const char separator = ';';
1228 #else
1229  const char separator = ':';
1230 #endif
1231 
1232  path = BLI_getenv("PATH");
1233  if (path) {
1234  char filename[FILE_MAX];
1235  const char *temp;
1236 
1237  do {
1238  temp = strchr(path, separator);
1239  if (temp) {
1240  memcpy(filename, path, temp - path);
1241  filename[temp - path] = 0;
1242  path = temp + 1;
1243  }
1244  else {
1245  BLI_strncpy(filename, path, sizeof(filename));
1246  }
1247 
1248  BLI_path_append(filename, maxlen, name);
1249  if (
1250 #ifdef _WIN32
1251  BLI_path_program_extensions_add_win32(filename, maxlen)
1252 #else
1253  BLI_exists(filename)
1254 #endif
1255  ) {
1256  BLI_strncpy(fullname, filename, maxlen);
1257  retval = true;
1258  break;
1259  }
1260  } while (temp);
1261  }
1262 
1263  if (retval == false) {
1264  *fullname = '\0';
1265  }
1266 
1267  return retval;
1268 }
1269 
1274 void BLI_setenv(const char *env, const char *val)
1275 {
1276  /* free windows */
1277 
1278 #if (defined(WIN32) || defined(WIN64))
1279  uputenv(env, val);
1280 
1281 #else
1282  /* Linux/macOS/BSD */
1283  if (val) {
1284  setenv(env, val, 1);
1285  }
1286  else {
1287  unsetenv(env);
1288  }
1289 #endif
1290 }
1291 
1298 void BLI_setenv_if_new(const char *env, const char *val)
1299 {
1300  if (BLI_getenv(env) == NULL) {
1301  BLI_setenv(env, val);
1302  }
1303 }
1304 
1313 const char *BLI_getenv(const char *env)
1314 {
1315 #ifdef _MSC_VER
1316  const char *result = NULL;
1317  /* 32767 is the maximum size of the environment variable on windows,
1318  * reserve one more character for the zero terminator. */
1319  static wchar_t buffer[32768];
1320  wchar_t *env_16 = alloc_utf16_from_8(env, 0);
1321  if (env_16) {
1322  if (GetEnvironmentVariableW(env_16, buffer, ARRAY_SIZE(buffer))) {
1323  char *res_utf8 = alloc_utf_8_from_16(buffer, 0);
1324  /* Make sure the result is valid, and will fit into our temporary storage buffer. */
1325  if (res_utf8) {
1326  if (strlen(res_utf8) + 1 < sizeof(buffer)) {
1327  /* We are re-using the utf16 buffer here, since allocating a second static buffer to
1328  * contain the UTF-8 version to return would be wasteful. */
1329  memcpy(buffer, res_utf8, strlen(res_utf8) + 1);
1330  result = (const char *)buffer;
1331  }
1332  free(res_utf8);
1333  }
1334  }
1335  }
1336  return result;
1337 #else
1338  return getenv(env);
1339 #endif
1340 }
1341 
1347 bool BLI_make_existing_file(const char *name)
1348 {
1349  char di[FILE_MAX];
1350  BLI_split_dir_part(name, di, sizeof(di));
1351 
1352  /* make if the dir doesn't exist */
1353  return BLI_dir_create_recursive(di);
1354 }
1355 
1365 void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file)
1366 {
1367  int sl;
1368 
1369  if (string) {
1370  /* ensure this is always set even if dir/file are NULL */
1371  string[0] = '\0';
1372 
1373  if (ELEM(NULL, dir, file)) {
1374  return; /* We don't want any NULLs */
1375  }
1376  }
1377  else {
1378  return; /* string is NULL, probably shouldn't happen but return anyway */
1379  }
1380 
1381  /* Resolve relative references */
1382  if (relabase && dir[0] == '/' && dir[1] == '/') {
1383  char *lslash;
1384 
1385  /* Get the file name, chop everything past the last slash (ie. the filename) */
1386  strcpy(string, relabase);
1387 
1388  lslash = (char *)BLI_path_slash_rfind(string);
1389  if (lslash) {
1390  *(lslash + 1) = 0;
1391  }
1392 
1393  dir += 2; /* Skip over the relative reference */
1394  }
1395 #ifdef WIN32
1396  else {
1397  if (BLI_strnlen(dir, 3) >= 2 && dir[1] == ':') {
1398  BLI_strncpy(string, dir, 3);
1399  dir += 2;
1400  }
1401  else if (BLI_strnlen(dir, 3) >= 2 && BLI_path_is_unc(dir)) {
1402  string[0] = 0;
1403  }
1404  else { /* no drive specified */
1405  /* first option: get the drive from the relabase if it has one */
1406  if (relabase && BLI_strnlen(relabase, 3) >= 2 && relabase[1] == ':') {
1407  BLI_strncpy(string, relabase, 3);
1408  string[2] = '\\';
1409  string[3] = '\0';
1410  }
1411  else { /* we're out of luck here, guessing the first valid drive, usually c:\ */
1413  }
1414 
1415  /* ignore leading slashes */
1416  while (ELEM(*dir, '/', '\\')) {
1417  dir++;
1418  }
1419  }
1420  }
1421 #endif
1422 
1423  strcat(string, dir);
1424 
1425  /* Make sure string ends in one (and only one) slash */
1426  /* first trim all slashes from the end of the string */
1427  sl = strlen(string);
1428  while ((sl > 0) && ELEM(string[sl - 1], '/', '\\')) {
1429  string[sl - 1] = '\0';
1430  sl--;
1431  }
1432  /* since we've now removed all slashes, put back one slash at the end. */
1433  strcat(string, "/");
1434 
1435  while (ELEM(*file, '/', '\\')) {
1436  /* Trim slashes from the front of file */
1437  file++;
1438  }
1439 
1440  strcat(string, file);
1441 
1442  /* Push all slashes to the system preferred direction */
1443  BLI_path_slash_native(string);
1444 }
1445 
1446 static bool path_extension_check_ex(const char *str,
1447  const size_t str_len,
1448  const char *ext,
1449  const size_t ext_len)
1450 {
1451  BLI_assert(strlen(str) == str_len);
1452  BLI_assert(strlen(ext) == ext_len);
1453 
1454  return (((str_len == 0 || ext_len == 0 || ext_len >= str_len) == 0) &&
1455  (BLI_strcasecmp(ext, str + str_len - ext_len) == 0));
1456 }
1457 
1458 /* does str end with ext. */
1459 bool BLI_path_extension_check(const char *str, const char *ext)
1460 {
1461  return path_extension_check_ex(str, strlen(str), ext, strlen(ext));
1462 }
1463 
1464 bool BLI_path_extension_check_n(const char *str, ...)
1465 {
1466  const size_t str_len = strlen(str);
1467 
1468  va_list args;
1469  const char *ext;
1470  bool ret = false;
1471 
1472  va_start(args, str);
1473 
1474  while ((ext = (const char *)va_arg(args, void *))) {
1475  if (path_extension_check_ex(str, str_len, ext, strlen(ext))) {
1476  ret = true;
1477  break;
1478  }
1479  }
1480 
1481  va_end(args);
1482 
1483  return ret;
1484 }
1485 
1486 /* does str end with any of the suffixes in *ext_array. */
1487 bool BLI_path_extension_check_array(const char *str, const char **ext_array)
1488 {
1489  const size_t str_len = strlen(str);
1490  int i = 0;
1491 
1492  while (ext_array[i]) {
1493  if (path_extension_check_ex(str, str_len, ext_array[i], strlen(ext_array[i]))) {
1494  return true;
1495  }
1496 
1497  i++;
1498  }
1499  return false;
1500 }
1501 
1506 bool BLI_path_extension_check_glob(const char *str, const char *ext_fnmatch)
1507 {
1508  const char *ext_step = ext_fnmatch;
1509  char pattern[16];
1510 
1511  while (ext_step[0]) {
1512  const char *ext_next;
1513  size_t len_ext;
1514 
1515  if ((ext_next = strchr(ext_step, ';'))) {
1516  len_ext = ext_next - ext_step + 1;
1517  BLI_strncpy(pattern, ext_step, (len_ext > sizeof(pattern)) ? sizeof(pattern) : len_ext);
1518  }
1519  else {
1520  len_ext = BLI_strncpy_rlen(pattern, ext_step, sizeof(pattern));
1521  }
1522 
1523  if (fnmatch(pattern, str, FNM_CASEFOLD) == 0) {
1524  return true;
1525  }
1526  ext_step += len_ext;
1527  }
1528 
1529  return false;
1530 }
1531 
1541 bool BLI_path_extension_glob_validate(char *ext_fnmatch)
1542 {
1543  bool only_wildcards = false;
1544 
1545  for (size_t i = strlen(ext_fnmatch); i-- > 0;) {
1546  if (ext_fnmatch[i] == ';') {
1547  /* Group separator, we truncate here if we only had wildcards so far.
1548  * Otherwise, all is sound and fine. */
1549  if (only_wildcards) {
1550  ext_fnmatch[i] = '\0';
1551  return true;
1552  }
1553  return false;
1554  }
1555  if (!ELEM(ext_fnmatch[i], '?', '*')) {
1556  /* Non-wildcard char, we can break here and consider the pattern valid. */
1557  return false;
1558  }
1559  /* So far, only wildcards in last group of the pattern... */
1560  only_wildcards = true;
1561  }
1562  /* Only one group in the pattern, so even if its only made of wildcard(s),
1563  * it is assumed valid. */
1564  return false;
1565 }
1566 
1571 bool BLI_path_extension_replace(char *path, size_t maxlen, const char *ext)
1572 {
1573 #ifdef DEBUG_STRSIZE
1574  memset(path, 0xff, sizeof(*path) * maxlen);
1575 #endif
1576  const size_t path_len = strlen(path);
1577  const size_t ext_len = strlen(ext);
1578  ssize_t a;
1579 
1580  for (a = path_len - 1; a >= 0; a--) {
1581  if (ELEM(path[a], '.', '/', '\\')) {
1582  break;
1583  }
1584  }
1585 
1586  if ((a < 0) || (path[a] != '.')) {
1587  a = path_len;
1588  }
1589 
1590  if (a + ext_len >= maxlen) {
1591  return false;
1592  }
1593 
1594  memcpy(path + a, ext, ext_len + 1);
1595  return true;
1596 }
1597 
1601 bool BLI_path_extension_ensure(char *path, size_t maxlen, const char *ext)
1602 {
1603 #ifdef DEBUG_STRSIZE
1604  memset(path, 0xff, sizeof(*path) * maxlen);
1605 #endif
1606  const size_t path_len = strlen(path);
1607  const size_t ext_len = strlen(ext);
1608  ssize_t a;
1609 
1610  /* first check the extension is already there */
1611  if ((ext_len <= path_len) && (STREQ(path + (path_len - ext_len), ext))) {
1612  return true;
1613  }
1614 
1615  for (a = path_len - 1; a >= 0; a--) {
1616  if (path[a] == '.') {
1617  path[a] = '\0';
1618  }
1619  else {
1620  break;
1621  }
1622  }
1623  a++;
1624 
1625  if (a + ext_len >= maxlen) {
1626  return false;
1627  }
1628 
1629  memcpy(path + a, ext, ext_len + 1);
1630  return true;
1631 }
1632 
1633 bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filename)
1634 {
1635 #ifdef DEBUG_STRSIZE
1636  memset(filepath, 0xff, sizeof(*filepath) * maxlen);
1637 #endif
1638  char *c = (char *)BLI_path_slash_rfind(filepath);
1639  if (!c || ((c - filepath) < maxlen - (strlen(filename) + 1))) {
1640  strcpy(c ? &c[1] : filepath, filename);
1641  return true;
1642  }
1643  return false;
1644 }
1645 
1655  const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen)
1656 {
1657 #ifdef DEBUG_STRSIZE
1658  memset(dir, 0xff, sizeof(*dir) * dirlen);
1659  memset(file, 0xff, sizeof(*file) * filelen);
1660 #endif
1661  const char *lslash_str = BLI_path_slash_rfind(string);
1662  const size_t lslash = lslash_str ? (size_t)(lslash_str - string) + 1 : 0;
1663 
1664  if (dir) {
1665  if (lslash) {
1666  /* +1 to include the slash and the last char */
1667  BLI_strncpy(dir, string, MIN2(dirlen, lslash + 1));
1668  }
1669  else {
1670  dir[0] = '\0';
1671  }
1672  }
1673 
1674  if (file) {
1675  BLI_strncpy(file, string + lslash, filelen);
1676  }
1677 }
1678 
1682 void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
1683 {
1684  BLI_split_dirfile(string, dir, NULL, dirlen, 0);
1685 }
1686 
1690 void BLI_split_file_part(const char *string, char *file, const size_t filelen)
1691 {
1692  BLI_split_dirfile(string, NULL, file, 0, filelen);
1693 }
1694 
1699 const char *BLI_path_extension(const char *filepath)
1700 {
1701  const char *extension = strrchr(filepath, '.');
1702  if (extension == NULL) {
1703  return NULL;
1704  }
1705  if (BLI_path_slash_find(extension) != NULL) {
1706  /* There is a path separator in the extension, so the '.' was found in a
1707  * directory component and not in the filename. */
1708  return NULL;
1709  }
1710  return extension;
1711 }
1712 
1716 void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file)
1717 {
1718  size_t dirlen = BLI_strnlen(dst, maxlen);
1719 
1720  /* inline BLI_path_slash_ensure */
1721  if ((dirlen > 0) && (dst[dirlen - 1] != SEP)) {
1722  dst[dirlen++] = SEP;
1723  dst[dirlen] = '\0';
1724  }
1725 
1726  if (dirlen >= maxlen) {
1727  return; /* fills the path */
1728  }
1729 
1730  BLI_strncpy(dst + dirlen, file, maxlen - dirlen);
1731 }
1732 
1737 void BLI_join_dirfile(char *__restrict dst,
1738  const size_t maxlen,
1739  const char *__restrict dir,
1740  const char *__restrict file)
1741 {
1742 #ifdef DEBUG_STRSIZE
1743  memset(dst, 0xff, sizeof(*dst) * maxlen);
1744 #endif
1745  size_t dirlen = BLI_strnlen(dir, maxlen);
1746 
1747  /* args can't match */
1748  BLI_assert(!ELEM(dst, dir, file));
1749 
1750  if (dirlen == maxlen) {
1751  memcpy(dst, dir, dirlen);
1752  dst[dirlen - 1] = '\0';
1753  return; /* dir fills the path */
1754  }
1755 
1756  memcpy(dst, dir, dirlen + 1);
1757 
1758  if (dirlen + 1 >= maxlen) {
1759  return; /* fills the path */
1760  }
1761 
1762  /* inline BLI_path_slash_ensure */
1763  if ((dirlen > 0) && !ELEM(dst[dirlen - 1], SEP, ALTSEP)) {
1764  dst[dirlen++] = SEP;
1765  dst[dirlen] = '\0';
1766  }
1767 
1768  if (dirlen >= maxlen) {
1769  return; /* fills the path */
1770  }
1771 
1772  BLI_strncpy(dst + dirlen, file, maxlen - dirlen);
1773 }
1774 
1782 size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *path, ...)
1783 {
1784 #ifdef DEBUG_STRSIZE
1785  memset(dst, 0xff, sizeof(*dst) * dst_len);
1786 #endif
1787  if (UNLIKELY(dst_len == 0)) {
1788  return 0;
1789  }
1790  const size_t dst_last = dst_len - 1;
1791  size_t ofs = BLI_strncpy_rlen(dst, path, dst_len);
1792 
1793  if (ofs == dst_last) {
1794  return ofs;
1795  }
1796 
1797  /* remove trailing slashes, unless there are _only_ trailing slashes
1798  * (allow "//" as the first argument). */
1799  bool has_trailing_slash = false;
1800  if (ofs != 0) {
1801  size_t len = ofs;
1802  while ((len != 0) && ELEM(path[len - 1], SEP, ALTSEP)) {
1803  len -= 1;
1804  }
1805  if (len != 0) {
1806  ofs = len;
1807  }
1808  has_trailing_slash = (path[len] != '\0');
1809  }
1810 
1811  va_list args;
1812  va_start(args, path);
1813  while ((path = (const char *)va_arg(args, const char *))) {
1814  has_trailing_slash = false;
1815  const char *path_init = path;
1816  while (ELEM(path[0], SEP, ALTSEP)) {
1817  path++;
1818  }
1819  size_t len = strlen(path);
1820  if (len != 0) {
1821  while ((len != 0) && ELEM(path[len - 1], SEP, ALTSEP)) {
1822  len -= 1;
1823  }
1824 
1825  if (len != 0) {
1826  /* the very first path may have a slash at the end */
1827  if (ofs && !ELEM(dst[ofs - 1], SEP, ALTSEP)) {
1828  dst[ofs++] = SEP;
1829  if (ofs == dst_last) {
1830  break;
1831  }
1832  }
1833  has_trailing_slash = (path[len] != '\0');
1834  if (ofs + len >= dst_last) {
1835  len = dst_last - ofs;
1836  }
1837  memcpy(&dst[ofs], path, len);
1838  ofs += len;
1839  if (ofs == dst_last) {
1840  break;
1841  }
1842  }
1843  }
1844  else {
1845  has_trailing_slash = (path_init != path);
1846  }
1847  }
1848  va_end(args);
1849 
1850  if (has_trailing_slash) {
1851  if ((ofs != dst_last) && (ofs != 0) && (ELEM(dst[ofs - 1], SEP, ALTSEP) == 0)) {
1852  dst[ofs++] = SEP;
1853  }
1854  }
1855 
1856  BLI_assert(ofs <= dst_last);
1857  dst[ofs] = '\0';
1858 
1859  return ofs;
1860 }
1861 
1868 const char *BLI_path_basename(const char *path)
1869 {
1870  const char *const filename = BLI_path_slash_rfind(path);
1871  return filename ? filename + 1 : path;
1872 }
1873 
1883 bool BLI_path_name_at_index(const char *__restrict path,
1884  const int index,
1885  int *__restrict r_offset,
1886  int *__restrict r_len)
1887 {
1888  if (index >= 0) {
1889  int index_step = 0;
1890  int prev = -1;
1891  int i = 0;
1892  while (true) {
1893  const char c = path[i];
1894  if (ELEM(c, SEP, ALTSEP, '\0')) {
1895  if (prev + 1 != i) {
1896  prev += 1;
1897  if (index_step == index) {
1898  *r_offset = prev;
1899  *r_len = i - prev;
1900  /* printf("!!! %d %d\n", start, end); */
1901  return true;
1902  }
1903  index_step += 1;
1904  }
1905  if (c == '\0') {
1906  break;
1907  }
1908  prev = i;
1909  }
1910  i += 1;
1911  }
1912  return false;
1913  }
1914 
1915  /* negative number, reverse where -1 is the last element */
1916  int index_step = -1;
1917  int prev = strlen(path);
1918  int i = prev - 1;
1919  while (true) {
1920  const char c = i >= 0 ? path[i] : '\0';
1921  if (ELEM(c, SEP, ALTSEP, '\0')) {
1922  if (prev - 1 != i) {
1923  i += 1;
1924  if (index_step == index) {
1925  *r_offset = i;
1926  *r_len = prev - i;
1927  return true;
1928  }
1929  index_step -= 1;
1930  }
1931  if (c == '\0') {
1932  break;
1933  }
1934  prev = i;
1935  }
1936  i -= 1;
1937  }
1938  return false;
1939 }
1940 
1944 const char *BLI_path_slash_find(const char *string)
1945 {
1946  const char *const ffslash = strchr(string, '/');
1947  const char *const fbslash = strchr(string, '\\');
1948 
1949  if (!ffslash) {
1950  return fbslash;
1951  }
1952  if (!fbslash) {
1953  return ffslash;
1954  }
1955 
1956  return (ffslash < fbslash) ? ffslash : fbslash;
1957 }
1958 
1962 const char *BLI_path_slash_rfind(const char *string)
1963 {
1964  const char *const lfslash = strrchr(string, '/');
1965  const char *const lbslash = strrchr(string, '\\');
1966 
1967  if (!lfslash) {
1968  return lbslash;
1969  }
1970  if (!lbslash) {
1971  return lfslash;
1972  }
1973 
1974  return (lfslash > lbslash) ? lfslash : lbslash;
1975 }
1976 
1981 int BLI_path_slash_ensure(char *string)
1982 {
1983  int len = strlen(string);
1984  if (len == 0 || string[len - 1] != SEP) {
1985  string[len] = SEP;
1986  string[len + 1] = '\0';
1987  return len + 1;
1988  }
1989  return len;
1990 }
1991 
1995 void BLI_path_slash_rstrip(char *string)
1996 {
1997  int len = strlen(string);
1998  while (len) {
1999  if (string[len - 1] == SEP) {
2000  string[len - 1] = '\0';
2001  len--;
2002  }
2003  else {
2004  break;
2005  }
2006  }
2007 }
2008 
2012 void BLI_path_slash_native(char *path)
2013 {
2014 #ifdef WIN32
2015  if (path && BLI_strnlen(path, 3) > 2) {
2016  BLI_str_replace_char(path + 2, '/', '\\');
2017  }
2018 #else
2019  BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), '\\', '/');
2020 #endif
2021 }
#define BLI_assert(a)
Definition: BLI_assert.h:58
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:349
int BLI_access(const char *filename, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:1024
bool BLI_dir_create_recursive(const char *dir) ATTR_NONNULL()
Definition: fileops.c:1329
char * BLI_current_working_dir(char *dir, const size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:81
#define PATH_MAX
Definition: BLI_fileops.h:44
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:116
#define ALTSEP
#define FILE_MAX
#define SEP
void BLI_str_tolower_ascii(char *str, const size_t len) ATTR_NONNULL()
Definition: string.c:890
int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:666
size_t BLI_strcpy_rlen(char *__restrict dst, const char *__restrict src) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:201
size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:187
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:70
size_t BLI_strnlen(const char *str, const size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:878
void BLI_str_replace_char(char *string, char src, char dst) ATTR_NONNULL()
Definition: string.c:532
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
unsigned int uint
Definition: BLI_sys_types.h:83
unsigned short ushort
Definition: BLI_sys_types.h:84
#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)
SSIZE_T ssize_t
Definition: BLI_winstuff.h:87
#define S_ISDIR(x)
Definition: BLI_winstuff.h:64
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
#define str(s)
__kernel void ccl_constant KernelData ccl_global void ccl_global char ccl_global int ccl_global char ccl_global unsigned int ccl_global float * buffer
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
#define L
static unsigned c
Definition: RandGen.cpp:97
static unsigned a[3]
Definition: RandGen.cpp:92
const char * BLI_path_slash_rfind(const char *string)
Definition: path_util.c:1962
static bool stringframe_chars(const char *path, int *char_start, int *char_end)
Definition: path_util.c:744
bool BLI_path_make_safe(char *path)
Definition: path_util.c:374
bool BLI_path_abs(char *path, const char *basepath)
Definition: path_util.c:1016
bool BLI_path_parent_dir_until_exists(char *dir)
Definition: path_util.c:728
bool BLI_filename_make_safe(char *fname)
Definition: path_util.c:299
size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *path,...)
Definition: path_util.c:1782
void BLI_path_normalize_dir(const char *relabase, char *dir)
Definition: path_util.c:266
void BLI_path_slash_native(char *path)
Definition: path_util.c:2012
void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
Definition: path_util.c:1682
const char * BLI_path_extension(const char *filepath)
Definition: path_util.c:1699
bool BLI_path_extension_replace(char *path, size_t maxlen, const char *ext)
Definition: path_util.c:1571
void BLI_path_normalize(const char *relabase, char *path)
Definition: path_util.c:173
bool BLI_path_parent_dir(char *path)
Definition: path_util.c:708
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:1446
bool BLI_make_existing_file(const char *name)
Definition: path_util.c:1347
bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *name)
Definition: path_util.c:1218
bool BLI_path_extension_ensure(char *path, size_t maxlen, const char *ext)
Definition: path_util.c:1601
bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char *sep)
Definition: path_util.c:669
bool BLI_path_extension_check_glob(const char *str, const char *ext_fnmatch)
Definition: path_util.c:1506
bool BLI_path_extension_check_array(const char *str, const char **ext_array)
Definition: path_util.c:1487
const char * BLI_path_slash_find(const char *string)
Definition: path_util.c:1944
bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filename)
Definition: path_util.c:1633
void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file)
Definition: path_util.c:1365
bool BLI_path_extension_check_n(const char *str,...)
Definition: path_util.c:1464
bool BLI_path_extension_check(const char *str, const char *ext)
Definition: path_util.c:1459
int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort *r_num_len)
Definition: path_util.c:83
void BLI_path_rel(char *file, const char *relfile)
Definition: path_util.c:519
void BLI_path_slash_rstrip(char *string)
Definition: path_util.c:1995
void BLI_path_sequence_encode(char *string, const char *head, const char *tail, unsigned short numlen, int pic)
Definition: path_util.c:154
void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file)
Definition: path_util.c:1716
bool BLI_path_frame_range(char *path, int sta, int end, int digits)
Definition: path_util.c:825
bool BLI_path_frame_check_chars(const char *path)
Definition: path_util.c:958
bool BLI_path_abs_from_cwd(char *path, const size_t maxlen)
Definition: path_util.c:1128
bool BLI_path_extension_glob_validate(char *ext_fnmatch)
Definition: path_util.c:1541
const char * BLI_getenv(const char *env)
Definition: path_util.c:1313
static void ensure_digits(char *path, int digits)
Definition: path_util.c:780
bool BLI_path_is_rel(const char *path)
Definition: path_util.c:411
void BLI_path_frame_strip(char *path, char *r_ext)
Definition: path_util.c:906
static int BLI_path_unc_prefix_len(const char *path)
Definition: path_util.c:428
bool BLI_path_is_unc(const char *name)
Definition: path_util.c:417
void BLI_join_dirfile(char *__restrict dst, const size_t maxlen, const char *__restrict dir, const char *__restrict file)
Definition: path_util.c:1737
void BLI_setenv(const char *env, const char *val)
Definition: path_util.c:1274
int BLI_path_slash_ensure(char *string)
Definition: path_util.c:1981
void BLI_path_to_display_name(char *display_name, int maxlen, const char *name)
Definition: path_util.c:968
void BLI_setenv_if_new(const char *env, const char *val)
Definition: path_util.c:1298
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:1883
bool BLI_path_frame(char *path, int frame, int digits)
Definition: path_util.c:802
void BLI_split_dirfile(const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen)
Definition: path_util.c:1654
const char * BLI_path_basename(const char *path)
Definition: path_util.c:1868
void BLI_split_file_part(const char *string, char *file, const size_t filelen)
Definition: path_util.c:1690
bool BLI_path_frame_get(char *path, int *r_frame, int *r_numdigits)
Definition: path_util.c:854
return ret
int uputenv(const char *name, const char *value)
Definition: utf_winfunc.c:158
wchar_t * alloc_utf16_from_8(const char *in8, size_t add)
Definition: utfconv.c:296
char * alloc_utf_8_from_16(const wchar_t *in16, size_t add)
Definition: utfconv.c:285
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8)
Definition: utfconv.c:127
void path_init(const string &path, const string &user_path)
Definition: util_path.cpp:338
uint len