Fix T65951 Saving project right before rendering (Cycles) causes empty render-preview
[blender.git] / source / blender / blenlib / intern / path_util.c
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
21 /** \file
22  * \ingroup bli
23  */
24
25 #include <ctype.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <assert.h>
29
30 #include "DNA_listBase.h"
31
32 #include "BLI_utildefines.h"
33 #include "BLI_fileops.h"
34 #include "BLI_path_util.h"
35 #include "BLI_string.h"
36 #include "BLI_fnmatch.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 <windows.h>
47 #  include <shlobj.h>
48 #  include "BLI_winstuff.h"
49 #  include "BLI_alloca.h"
50 #else
51 #  include "unistd.h"
52 #endif /* WIN32 */
53
54 #include "MEM_guardedalloc.h"
55
56 /* Declarations */
57
58 #ifdef WIN32
59
60 /** Return true if the path is absolute ie starts with a drive specifier
61  * (eg A:\) or is a UNC path. */
62 static bool BLI_path_is_abs(const char *name);
63
64 #endif /* WIN32 */
65
66 // #define DEBUG_STRSIZE
67
68 /* implementation */
69
70 /**
71  * Looks for a sequence of decimal digits in string, preceding any filename extension,
72  * returning the integer value if found, or 0 if not.
73  *
74  * \param string: String to scan.
75  * \param head: Optional area to return copy of part of string prior to digits,
76  * or before dot if no digits.
77  * \param tail: Optional area to return copy of part of string following digits,
78  * or from dot if no digits.
79  * \param r_num_len: Optional to return number of digits found.
80  */
81 int BLI_stringdec(const char *string, char *head, char *tail, ushort *r_num_len)
82 {
83   uint nums = 0, nume = 0;
84   int i;
85   bool found_digit = false;
86   const char *const lslash = BLI_last_slash(string);
87   const uint string_len = strlen(string);
88   const uint lslash_len = lslash != NULL ? (int)(lslash - string) : 0;
89   uint name_end = string_len;
90
91   while (name_end > lslash_len && string[--name_end] != '.') {
92     /* name ends at dot if present */
93   }
94   if (name_end == lslash_len && string[name_end] != '.') {
95     name_end = string_len;
96   }
97
98   for (i = name_end - 1; i >= (int)lslash_len; i--) {
99     if (isdigit(string[i])) {
100       if (found_digit) {
101         nums = i;
102       }
103       else {
104         nume = i;
105         nums = i;
106         found_digit = true;
107       }
108     }
109     else {
110       if (found_digit) {
111         break;
112       }
113     }
114   }
115
116   if (found_digit) {
117     const long long int ret = strtoll(&(string[nums]), NULL, 10);
118     if (ret >= INT_MIN && ret <= INT_MAX) {
119       if (tail) {
120         strcpy(tail, &string[nume + 1]);
121       }
122       if (head) {
123         strcpy(head, string);
124         head[nums] = 0;
125       }
126       if (r_num_len) {
127         *r_num_len = nume - nums + 1;
128       }
129       return (int)ret;
130     }
131   }
132
133   if (tail) {
134     strcpy(tail, string + name_end);
135   }
136   if (head) {
137     /* name_end points to last character of head,
138      * make it +1 so null-terminator is nicely placed
139      */
140     BLI_strncpy(head, string, name_end + 1);
141   }
142   if (r_num_len) {
143     *r_num_len = 0;
144   }
145   return 0;
146 }
147
148 /**
149  * Returns in area pointed to by string a string of the form "<head><pic><tail>", where pic
150  * is formatted as numlen digits with leading zeroes.
151  */
152 void BLI_stringenc(
153     char *string, const char *head, const char *tail, unsigned short numlen, int pic)
154 {
155   sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail);
156 }
157
158 static int BLI_path_unc_prefix_len(const char *path); /* defined below in same file */
159
160 /* ******************** string encoding ***************** */
161
162 /**
163  * Remove redundant characters from \a path and optionally make absolute.
164  *
165  * \param relabase: The path this is relative to, or ignored when NULL.
166  * \param path: Can be any input, and this function converts it to a regular full path.
167  * Also removes garbage from directory paths, like `/../` or double slashes etc.
168  *
169  * \note \a path isn't protected for max string names...
170  */
171 void BLI_cleanup_path(const char *relabase, char *path)
172 {
173   ptrdiff_t a;
174   char *start, *eind;
175   if (relabase) {
176     BLI_path_abs(path, relabase);
177   }
178   else {
179     if (path[0] == '/' && path[1] == '/') {
180       if (path[2] == '\0') {
181         return; /* path is "//" - cant clean it */
182       }
183       path = path + 2; /* leave the initial "//" untouched */
184     }
185   }
186
187   /* Note
188    *   memmove(start, eind, strlen(eind) + 1);
189    * is the same as
190    *   strcpy(start, eind);
191    * except strcpy should not be used because there is overlap,
192    * so use memmove's slightly more obscure syntax - Campbell
193    */
194
195 #ifdef WIN32
196   while ((start = strstr(path, "\\..\\"))) {
197     eind = start + strlen("\\..\\") - 1;
198     a = start - path - 1;
199     while (a > 0) {
200       if (path[a] == '\\') {
201         break;
202       }
203       a--;
204     }
205     if (a < 0) {
206       break;
207     }
208     else {
209       memmove(path + a, eind, strlen(eind) + 1);
210     }
211   }
212
213   while ((start = strstr(path, "\\.\\"))) {
214     eind = start + strlen("\\.\\") - 1;
215     memmove(start, eind, strlen(eind) + 1);
216   }
217
218   /* remove two consecutive backslashes, but skip the UNC prefix,
219    * which needs to be preserved */
220   while ((start = strstr(path + BLI_path_unc_prefix_len(path), "\\\\"))) {
221     eind = start + strlen("\\\\") - 1;
222     memmove(start, eind, strlen(eind) + 1);
223   }
224 #else
225   while ((start = strstr(path, "/../"))) {
226     a = start - path - 1;
227     if (a > 0) {
228       /* <prefix>/<parent>/../<postfix> => <prefix>/<postfix> */
229       eind = start + (4 - 1) /* strlen("/../") - 1 */; /* strip "/.." and keep last "/" */
230       while (a > 0 && path[a] != '/') {                /* find start of <parent> */
231         a--;
232       }
233       memmove(path + a, eind, strlen(eind) + 1);
234     }
235     else {
236       /* support for odd paths: eg /../home/me --> /home/me
237        * this is a valid path in blender but we cant handle this the usual way below
238        * simply strip this prefix then evaluate the path as usual.
239        * pythons os.path.normpath() does this */
240
241       /* Note: previous version of following call used an offset of 3 instead of 4,
242        * which meant that the "/../home/me" example actually became "home/me".
243        * Using offset of 3 gives behavior consistent with the abovementioned
244        * Python routine. */
245       memmove(path, path + 3, strlen(path + 3) + 1);
246     }
247   }
248
249   while ((start = strstr(path, "/./"))) {
250     eind = start + (3 - 1) /* strlen("/./") - 1 */;
251     memmove(start, eind, strlen(eind) + 1);
252   }
253
254   while ((start = strstr(path, "//"))) {
255     eind = start + (2 - 1) /* strlen("//") - 1 */;
256     memmove(start, eind, strlen(eind) + 1);
257   }
258 #endif
259 }
260
261 /**
262  * Cleanup filepath ensuring a trailing slash.
263  */
264 void BLI_cleanup_dir(const char *relabase, char *dir)
265 {
266   BLI_cleanup_path(relabase, dir);
267   BLI_add_slash(dir);
268 }
269
270 /**
271  * Cleanup filepath ensuring no trailing slash.
272  */
273 void BLI_cleanup_file(const char *relabase, char *path)
274 {
275   BLI_cleanup_path(relabase, path);
276   BLI_del_slash(path);
277 }
278
279 /**
280  * Make given name safe to be used in paths.
281  *
282  * \return true if \a fname was changed, false otherwise.
283  *
284  * For now, simply replaces reserved chars (as listed in
285  * https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words )
286  * by underscores ('_').
287  *
288  * \note Space case ' ' is a bit of an edge case here - in theory it is allowed,
289  * but again can be an issue in some cases, so we simply replace it by an underscore too
290  * (good practice anyway).
291  * REMOVED based on popular demand (see T45900).
292  * Percent '%' char is a bit same case - not recommended to use it,
293  * but supported by all decent FS/OS around.
294  *
295  * \note On Windows, it also ensures there is no '.' (dot char) at the end of the file,
296  * this can lead to issues.
297  *
298  * \note On Windows, it also checks for forbidden names
299  * (see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx ).
300  */
301 bool BLI_filename_make_safe(char *fname)
302 {
303   const char *invalid =
304       "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
305       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
306       "/\\?*:|\"<>";
307   char *fn;
308   bool changed = false;
309
310   if (*fname == '\0') {
311     return changed;
312   }
313
314   for (fn = fname; *fn && (fn = strpbrk(fn, invalid)); fn++) {
315     *fn = '_';
316     changed = true;
317   }
318
319   /* Forbid only dots. */
320   for (fn = fname; *fn == '.'; fn++) {
321     /* pass */
322   }
323   if (*fn == '\0') {
324     *fname = '_';
325     changed = true;
326   }
327
328 #ifdef WIN32
329   {
330     const size_t len = strlen(fname);
331     const char *invalid_names[] = {
332         "con",  "prn",  "aux",  "null", "com1", "com2", "com3", "com4",
333         "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3",
334         "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", NULL,
335     };
336     char *lower_fname = BLI_strdup(fname);
337     const char **iname;
338
339     /* Forbid trailing dot (trailing space has already been replaced above). */
340     if (fname[len - 1] == '.') {
341       fname[len - 1] = '_';
342       changed = true;
343     }
344
345     /* Check for forbidden names - not we have to check all combination
346      * of upper and lower cases, hence the usage of lower_fname
347      * (more efficient than using BLI_strcasestr repeatedly). */
348     BLI_str_tolower_ascii(lower_fname, len);
349     for (iname = invalid_names; *iname; iname++) {
350       if (strstr(lower_fname, *iname) == lower_fname) {
351         const size_t iname_len = strlen(*iname);
352         /* Only invalid if the whole name is made of the invalid chunk, or it has an
353          * (assumed extension) dot just after. This means it will also catch 'valid'
354          * names like 'aux.foo.bar', but should be
355          * good enough for us! */
356         if ((iname_len == len) || (lower_fname[iname_len] == '.')) {
357           *fname = '_';
358           changed = true;
359           break;
360         }
361       }
362     }
363
364     MEM_freeN(lower_fname);
365   }
366 #endif
367
368   return changed;
369 }
370
371 /**
372  * Make given path OS-safe.
373  *
374  * \return true if \a path was changed, false otherwise.
375  */
376 bool BLI_path_make_safe(char *path)
377 {
378   /* Simply apply BLI_filename_make_safe() over each component of the path.
379    * Luckily enough, same 'safe' rules applies to filenames and dirnames. */
380   char *curr_slash, *curr_path = path;
381   bool changed = false;
382   bool skip_first = false;
383
384 #ifdef WIN32
385   if (BLI_path_is_abs(path)) {
386     /* Do not make safe 'C:' in 'C:\foo\bar'... */
387     skip_first = true;
388   }
389 #endif
390
391   for (curr_slash = (char *)BLI_first_slash(curr_path); curr_slash;
392        curr_slash = (char *)BLI_first_slash(curr_path)) {
393     const char backup = *curr_slash;
394     *curr_slash = '\0';
395     if (!skip_first && (*curr_path != '\0') && BLI_filename_make_safe(curr_path)) {
396       changed = true;
397     }
398     skip_first = false;
399     curr_path = curr_slash + 1;
400     *curr_slash = backup;
401   }
402   if (BLI_filename_make_safe(curr_path)) {
403     changed = true;
404   }
405
406   return changed;
407 }
408
409 /**
410  * Does path begin with the special "//" prefix that Blender uses to indicate
411  * a path relative to the .blend file.
412  */
413 bool BLI_path_is_rel(const char *path)
414 {
415   return path[0] == '/' && path[1] == '/';
416 }
417
418 /* return true if the path is a UNC share */
419 bool BLI_path_is_unc(const char *name)
420 {
421   return name[0] == '\\' && name[1] == '\\';
422 }
423
424 /**
425  * Returns the length of the identifying prefix
426  * of a UNC path which can start with '\\' (short version)
427  * or '\\?\' (long version)
428  * If the path is not a UNC path, return 0
429  */
430 static int BLI_path_unc_prefix_len(const char *path)
431 {
432   if (BLI_path_is_unc(path)) {
433     if ((path[2] == '?') && (path[3] == '\\')) {
434       /* we assume long UNC path like \\?\server\share\folder etc... */
435       return 4;
436     }
437     else {
438       return 2;
439     }
440   }
441
442   return 0;
443 }
444
445 #if defined(WIN32)
446
447 /** Return true if the path is absolute ie starts with a drive specifier
448  * (eg A:\) or is a UNC path. */
449 static bool BLI_path_is_abs(const char *name)
450 {
451   return (name[1] == ':' && (name[2] == '\\' || name[2] == '/')) || BLI_path_is_unc(name);
452 }
453
454 static wchar_t *next_slash(wchar_t *path)
455 {
456   wchar_t *slash = path;
457   while (*slash && *slash != L'\\') {
458     slash++;
459   }
460   return slash;
461 }
462
463 /* adds a slash if the unc path points sto a share */
464 static void BLI_path_add_slash_to_share(wchar_t *uncpath)
465 {
466   wchar_t *slash_after_server = next_slash(uncpath + 2);
467   if (*slash_after_server) {
468     wchar_t *slash_after_share = next_slash(slash_after_server + 1);
469     if (!(*slash_after_share)) {
470       slash_after_share[0] = L'\\';
471       slash_after_share[1] = L'\0';
472     }
473   }
474 }
475
476 static void BLI_path_unc_to_short(wchar_t *unc)
477 {
478   wchar_t tmp[PATH_MAX];
479
480   int len = wcslen(unc);
481   /* convert:
482    *    \\?\UNC\server\share\folder\... to \\server\share\folder\...
483    *    \\?\C:\ to C:\ and \\?\C:\folder\... to C:\folder\...
484    */
485   if ((len > 3) && (unc[0] == L'\\') && (unc[1] == L'\\') && (unc[2] == L'?') &&
486       ((unc[3] == L'\\') || (unc[3] == L'/'))) {
487     if ((len > 5) && (unc[5] == L':')) {
488       wcsncpy(tmp, unc + 4, len - 4);
489       tmp[len - 4] = L'\0';
490       wcscpy(unc, tmp);
491     }
492     else if ((len > 7) && (wcsncmp(&unc[4], L"UNC", 3) == 0) &&
493              ((unc[7] == L'\\') || (unc[7] == L'/'))) {
494       tmp[0] = L'\\';
495       tmp[1] = L'\\';
496       wcsncpy(tmp + 2, unc + 8, len - 8);
497       tmp[len - 6] = L'\0';
498       wcscpy(unc, tmp);
499     }
500   }
501 }
502
503 void BLI_cleanup_unc(char *path, int maxlen)
504 {
505   wchar_t *tmp_16 = alloc_utf16_from_8(path, 1);
506   BLI_cleanup_unc_16(tmp_16);
507   conv_utf_16_to_8(tmp_16, path, maxlen);
508 }
509
510 void BLI_cleanup_unc_16(wchar_t *path_16)
511 {
512   BLI_path_unc_to_short(path_16);
513   BLI_path_add_slash_to_share(path_16);
514 }
515 #endif
516
517 /**
518  * Replaces *file with a relative version (prefixed by "//") such that BLI_path_abs, given
519  * the same *relfile, will convert it back to its original value.
520  */
521 void BLI_path_rel(char *file, const char *relfile)
522 {
523   const char *lslash;
524   char temp[FILE_MAX];
525   char res[FILE_MAX];
526
527   /* if file is already relative, bail out */
528   if (BLI_path_is_rel(file)) {
529     return;
530   }
531
532   /* also bail out if relative path is not set */
533   if (relfile[0] == '\0') {
534     return;
535   }
536
537 #ifdef WIN32
538   if (BLI_strnlen(relfile, 3) > 2 && !BLI_path_is_abs(relfile)) {
539     char *ptemp;
540     /* fix missing volume name in relative base,
541      * can happen with old recent-files.txt files */
542     get_default_root(temp);
543     ptemp = &temp[2];
544     if (relfile[0] != '\\' && relfile[0] != '/') {
545       ptemp++;
546     }
547     BLI_strncpy(ptemp, relfile, FILE_MAX - 3);
548   }
549   else {
550     BLI_strncpy(temp, relfile, FILE_MAX);
551   }
552
553   if (BLI_strnlen(file, 3) > 2) {
554     bool is_unc = BLI_path_is_unc(file);
555
556     /* Ensure paths are both UNC paths or are both drives */
557     if (BLI_path_is_unc(temp) != is_unc) {
558       return;
559     }
560
561     /* Ensure both UNC paths are on the same share */
562     if (is_unc) {
563       int off;
564       int slash = 0;
565       for (off = 0; temp[off] && slash < 4; off++) {
566         if (temp[off] != file[off]) {
567           return;
568         }
569
570         if (temp[off] == '\\') {
571           slash++;
572         }
573       }
574     }
575     else if (temp[1] == ':' && file[1] == ':' && temp[0] != file[0]) {
576       return;
577     }
578   }
579 #else
580   BLI_strncpy(temp, relfile, FILE_MAX);
581 #endif
582
583   BLI_str_replace_char(temp + BLI_path_unc_prefix_len(temp), '\\', '/');
584   BLI_str_replace_char(file + BLI_path_unc_prefix_len(file), '\\', '/');
585
586   /* remove /./ which confuse the following slash counting... */
587   BLI_cleanup_path(NULL, file);
588   BLI_cleanup_path(NULL, temp);
589
590   /* the last slash in the file indicates where the path part ends */
591   lslash = BLI_last_slash(temp);
592
593   if (lslash) {
594     /* find the prefix of the filename that is equal for both filenames.
595      * This is replaced by the two slashes at the beginning */
596     const char *p = temp;
597     const char *q = file;
598     char *r = res;
599
600 #ifdef WIN32
601     while (tolower(*p) == tolower(*q))
602 #else
603     while (*p == *q)
604 #endif
605     {
606       p++;
607       q++;
608
609       /* don't search beyond the end of the string
610        * in the rare case they match */
611       if ((*p == '\0') || (*q == '\0')) {
612         break;
613       }
614     }
615
616     /* we might have passed the slash when the beginning of a dir matches
617      * so we rewind. Only check on the actual filename
618      */
619     if (*q != '/') {
620       while ((q >= file) && (*q != '/')) {
621         --q;
622         --p;
623       }
624     }
625     else if (*p != '/') {
626       while ((p >= temp) && (*p != '/')) {
627         --p;
628         --q;
629       }
630     }
631
632     r += BLI_strcpy_rlen(r, "//");
633
634     /* p now points to the slash that is at the beginning of the part
635      * where the path is different from the relative path.
636      * We count the number of directories we need to go up in the
637      * hierarchy to arrive at the common 'prefix' of the path
638      */
639     if (p < temp) {
640       p = temp;
641     }
642     while (p && p < lslash) {
643       if (*p == '/') {
644         r += BLI_strcpy_rlen(r, "../");
645       }
646       p++;
647     }
648
649     /* don't copy the slash at the beginning */
650     r += BLI_strcpy_rlen(r, q + 1);
651
652 #ifdef WIN32
653     BLI_str_replace_char(res + 2, '/', '\\');
654 #endif
655     strcpy(file, res);
656   }
657 }
658
659 /**
660  * Appends a suffix to the string, fitting it before the extension
661  *
662  * string = Foo.png, suffix = 123, separator = _
663  * Foo.png -> Foo_123.png
664  *
665  * \param string: original (and final) string
666  * \param maxlen: Maximum length of string
667  * \param suffix: String to append to the original string
668  * \param sep: Optional separator character
669  * \return  true if succeeded
670  */
671 bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char *sep)
672 {
673 #ifdef DEBUG_STRSIZE
674   memset(string, 0xff, sizeof(*string) * maxlen);
675 #endif
676   const size_t string_len = strlen(string);
677   const size_t suffix_len = strlen(suffix);
678   const size_t sep_len = strlen(sep);
679   ssize_t a;
680   char extension[FILE_MAX];
681   bool has_extension = false;
682
683   if (string_len + sep_len + suffix_len >= maxlen) {
684     return false;
685   }
686
687   for (a = string_len - 1; a >= 0; a--) {
688     if (string[a] == '.') {
689       has_extension = true;
690       break;
691     }
692     else if (ELEM(string[a], '/', '\\')) {
693       break;
694     }
695   }
696
697   if (!has_extension) {
698     a = string_len;
699   }
700
701   BLI_strncpy(extension, string + a, sizeof(extension));
702   sprintf(string + a, "%s%s%s", sep, suffix, extension);
703   return true;
704 }
705
706 /**
707  * Replaces path with the path of its parent directory, returning true if
708  * it was able to find a parent directory within the pathname.
709  */
710 bool BLI_parent_dir(char *path)
711 {
712   const char parent_dir[] = {'.', '.', SEP, '\0'}; /* "../" or "..\\" */
713   char tmp[FILE_MAX + 4];
714
715   BLI_join_dirfile(tmp, sizeof(tmp), path, parent_dir);
716   BLI_cleanup_dir(NULL, tmp); /* does all the work of normalizing the path for us */
717
718   if (!BLI_path_extension_check(tmp, parent_dir)) {
719     strcpy(path, tmp); /* We assume pardir is always shorter... */
720     return true;
721   }
722   else {
723     return false;
724   }
725 }
726
727 /**
728  * Looks for a sequence of "#" characters in the last slash-separated component of *path,
729  * returning the indexes of the first and one past the last character in the sequence in
730  * *char_start and *char_end respectively. Returns true if such a sequence was found.
731  */
732 static bool stringframe_chars(const char *path, int *char_start, int *char_end)
733 {
734   uint ch_sta, ch_end, i;
735   /* Insert current frame: file### -> file001 */
736   ch_sta = ch_end = 0;
737   for (i = 0; path[i] != '\0'; i++) {
738     if (path[i] == '\\' || path[i] == '/') {
739       ch_end = 0; /* this is a directory name, don't use any hashes we found */
740     }
741     else if (path[i] == '#') {
742       ch_sta = i;
743       ch_end = ch_sta + 1;
744       while (path[ch_end] == '#') {
745         ch_end++;
746       }
747       i = ch_end - 1; /* keep searching */
748
749       /* don't break, there may be a slash after this that invalidates the previous #'s */
750     }
751   }
752
753   if (ch_end) {
754     *char_start = ch_sta;
755     *char_end = ch_end;
756     return true;
757   }
758   else {
759     *char_start = -1;
760     *char_end = -1;
761     return false;
762   }
763 }
764
765 /**
766  * Ensure *path contains at least one "#" character in its last slash-separated
767  * component, appending one digits long if not.
768  */
769 static void ensure_digits(char *path, int digits)
770 {
771   char *file = (char *)BLI_last_slash(path);
772
773   if (file == NULL) {
774     file = path;
775   }
776
777   if (strrchr(file, '#') == NULL) {
778     int len = strlen(file);
779
780     while (digits--) {
781       file[len++] = '#';
782     }
783     file[len] = '\0';
784   }
785 }
786
787 /**
788  * Replaces "#" character sequence in last slash-separated component of *path
789  * with frame as decimal integer, with leading zeroes as necessary, to make digits digits.
790  */
791 bool BLI_path_frame(char *path, int frame, int digits)
792 {
793   int ch_sta, ch_end;
794
795   if (digits) {
796     ensure_digits(path, digits);
797   }
798
799   if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
800     char tmp[FILE_MAX];
801     BLI_snprintf(
802         tmp, sizeof(tmp), "%.*s%.*d%s", ch_sta, path, ch_end - ch_sta, frame, path + ch_end);
803     BLI_strncpy(path, tmp, FILE_MAX);
804     return true;
805   }
806   return false;
807 }
808
809 /**
810  * Replaces "#" character sequence in last slash-separated component of *path
811  * with sta and end as decimal integers, with leading zeroes as necessary, to make digits
812  * digits each, with a hyphen in-between.
813  */
814 bool BLI_path_frame_range(char *path, int sta, int end, int digits)
815 {
816   int ch_sta, ch_end;
817
818   if (digits) {
819     ensure_digits(path, digits);
820   }
821
822   if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
823     char tmp[FILE_MAX];
824     BLI_snprintf(tmp,
825                  sizeof(tmp),
826                  "%.*s%.*d-%.*d%s",
827                  ch_sta,
828                  path,
829                  ch_end - ch_sta,
830                  sta,
831                  ch_end - ch_sta,
832                  end,
833                  path + ch_end);
834     BLI_strncpy(path, tmp, FILE_MAX);
835     return true;
836   }
837   return false;
838 }
839
840 /**
841  * Get the frame from a filename formatted by blender's frame scheme
842  */
843 bool BLI_path_frame_get(char *path, int *r_frame, int *r_numdigits)
844 {
845   if (*path) {
846     char *file = (char *)BLI_last_slash(path);
847     char *c;
848     int len, numdigits;
849
850     numdigits = *r_numdigits = 0;
851
852     if (file == NULL) {
853       file = path;
854     }
855
856     /* first get the extension part */
857     len = strlen(file);
858
859     c = file + len;
860
861     /* isolate extension */
862     while (--c != file) {
863       if (*c == '.') {
864         c--;
865         break;
866       }
867     }
868
869     /* find start of number */
870     while (c != (file - 1) && isdigit(*c)) {
871       c--;
872       numdigits++;
873     }
874
875     if (numdigits) {
876       char prevchar;
877
878       c++;
879       prevchar = c[numdigits];
880       c[numdigits] = 0;
881
882       /* was the number really an extension? */
883       *r_frame = atoi(c);
884       c[numdigits] = prevchar;
885
886       *r_numdigits = numdigits;
887
888       return true;
889     }
890   }
891
892   return false;
893 }
894
895 void BLI_path_frame_strip(char *path, char *r_ext)
896 {
897   *r_ext = '\0';
898   if (*path == '\0') {
899     return;
900   }
901
902   char *file = (char *)BLI_last_slash(path);
903   char *c, *suffix;
904   int len;
905   int numdigits = 0;
906
907   if (file == NULL) {
908     file = path;
909   }
910
911   /* first get the extension part */
912   len = strlen(file);
913
914   c = file + len;
915
916   /* isolate extension */
917   while (--c != file) {
918     if (*c == '.') {
919       c--;
920       break;
921     }
922   }
923
924   suffix = c + 1;
925
926   /* find start of number */
927   while (c != (file - 1) && isdigit(*c)) {
928     c--;
929     numdigits++;
930   }
931
932   c++;
933
934   int suffix_length = len - (suffix - file);
935   BLI_strncpy(r_ext, suffix, suffix_length + 1);
936
937   /* replace the number with the suffix and terminate the string */
938   while (numdigits--) {
939     *c++ = '#';
940   }
941   *c = '\0';
942 }
943
944 /**
945  * Check if we have '#' chars, usable for #BLI_path_frame, #BLI_path_frame_range
946  */
947 bool BLI_path_frame_check_chars(const char *path)
948 {
949   int ch_sta, ch_end; /* dummy args */
950   return stringframe_chars(path, &ch_sta, &ch_end);
951 }
952
953 /**
954  * Creates a display string from path to be used menus and the user interface.
955  * Like bpy.path.display_name().
956  */
957 void BLI_path_to_display_name(char *display_name, int maxlen, const char *name)
958 {
959   /* Strip leading underscores and spaces. */
960   int strip_offset = 0;
961   while (ELEM(name[strip_offset], '_', ' ')) {
962     strip_offset++;
963   }
964
965   BLI_strncpy(display_name, name + strip_offset, maxlen);
966
967   /* Replace underscores with spaces. */
968   BLI_str_replace_char(display_name, '_', ' ');
969
970   /* Strip extension. */
971   BLI_path_extension_replace(display_name, maxlen, "");
972
973   /* Test if string has any upper case characters. */
974   bool all_lower = true;
975   for (int i = 0; display_name[i]; i++) {
976     if (isupper(display_name[i])) {
977       all_lower = false;
978       break;
979     }
980   }
981
982   if (all_lower) {
983     /* For full lowercase string, use title case. */
984     bool prevspace = true;
985     for (int i = 0; display_name[i]; i++) {
986       if (prevspace) {
987         display_name[i] = toupper(display_name[i]);
988       }
989
990       prevspace = isspace(display_name[i]);
991     }
992   }
993 }
994
995 /**
996  * If path begins with "//", strips that and replaces it with basepath directory.
997  *
998  * \note Also converts drive-letter prefix to something more sensible
999  * if this is a non-drive-letter-based system.
1000  *
1001  * \param path: The path to convert.
1002  * \param basepath: The directory to base relative paths with.
1003  * \return true if the path was relative (started with "//").
1004  */
1005 bool BLI_path_abs(char *path, const char *basepath)
1006 {
1007   const bool wasrelative = BLI_path_is_rel(path);
1008   char tmp[FILE_MAX];
1009   char base[FILE_MAX];
1010 #ifdef WIN32
1011
1012   /* without this: "" --> "C:\" */
1013   if (*path == '\0') {
1014     return wasrelative;
1015   }
1016
1017   /* we are checking here if we have an absolute path that is not in the current
1018    * blend file as a lib main - we are basically checking for the case that a
1019    * UNIX root '/' is passed.
1020    */
1021   if (!wasrelative && !BLI_path_is_abs(path)) {
1022     char *p = path;
1023     get_default_root(tmp);
1024     // get rid of the slashes at the beginning of the path
1025     while (*p == '\\' || *p == '/') {
1026       p++;
1027     }
1028     strcat(tmp, p);
1029   }
1030   else {
1031     BLI_strncpy(tmp, path, FILE_MAX);
1032   }
1033 #else
1034   BLI_strncpy(tmp, path, sizeof(tmp));
1035
1036   /* Check for loading a windows path on a posix system
1037    * in this case, there is no use in trying C:/ since it
1038    * will never exist on a unix os.
1039    *
1040    * Add a / prefix and lowercase the driveletter, remove the :
1041    * C:\foo.JPG -> /c/foo.JPG */
1042
1043   if (isalpha(tmp[0]) && tmp[1] == ':' && (tmp[2] == '\\' || tmp[2] == '/')) {
1044     tmp[1] = tolower(tmp[0]); /* replace ':' with driveletter */
1045     tmp[0] = '/';
1046     /* '\' the slash will be converted later */
1047   }
1048
1049 #endif
1050
1051   /* push slashes into unix mode - strings entering this part are
1052    * potentially messed up: having both back- and forward slashes.
1053    * Here we push into one conform direction, and at the end we
1054    * push them into the system specific dir. This ensures uniformity
1055    * of paths and solving some problems (and prevent potential future
1056    * ones) -jesterKing.
1057    * For UNC paths the first characters containing the UNC prefix
1058    * shouldn't be switched as we need to distinguish them from
1059    * paths relative to the .blend file -elubie */
1060   BLI_str_replace_char(tmp + BLI_path_unc_prefix_len(tmp), '\\', '/');
1061
1062   /* Paths starting with // will get the blend file as their base,
1063    * this isn't standard in any os but is used in blender all over the place */
1064   if (wasrelative) {
1065     const char *lslash;
1066     BLI_strncpy(base, basepath, sizeof(base));
1067
1068     /* file component is ignored, so don't bother with the trailing slash */
1069     BLI_cleanup_path(NULL, base);
1070     lslash = BLI_last_slash(base);
1071     BLI_str_replace_char(base + BLI_path_unc_prefix_len(base), '\\', '/');
1072
1073     if (lslash) {
1074       /* length up to and including last "/" */
1075       const int baselen = (int)(lslash - base) + 1;
1076       /* use path for temp storage here, we copy back over it right away */
1077       BLI_strncpy(path, tmp + 2, FILE_MAX); /* strip "//" */
1078
1079       memcpy(tmp, base, baselen); /* prefix with base up to last "/" */
1080       BLI_strncpy(tmp + baselen, path, sizeof(tmp) - baselen); /* append path after "//" */
1081       BLI_strncpy(path, tmp, FILE_MAX);                        /* return as result */
1082     }
1083     else {
1084       /* base doesn't seem to be a directory--ignore it and just strip "//" prefix on path */
1085       BLI_strncpy(path, tmp + 2, FILE_MAX);
1086     }
1087   }
1088   else {
1089     /* base ignored */
1090     BLI_strncpy(path, tmp, FILE_MAX);
1091   }
1092
1093 #ifdef WIN32
1094   /* skip first two chars, which in case of
1095    * absolute path will be drive:/blabla and
1096    * in case of relpath //blabla/. So relpath
1097    * // will be retained, rest will be nice and
1098    * shiny win32 backward slashes :) -jesterKing
1099    */
1100   BLI_str_replace_char(path + 2, '/', '\\');
1101 #endif
1102
1103   /* ensure this is after correcting for path switch */
1104   BLI_cleanup_path(NULL, path);
1105
1106   return wasrelative;
1107 }
1108
1109 /**
1110  * Expands path relative to the current working directory, if it was relative.
1111  * Returns true if such expansion was done.
1112  *
1113  * \note Should only be done with command line paths.
1114  * this is _not_ something blenders internal paths support like the "//" prefix
1115  */
1116 bool BLI_path_cwd(char *path, const size_t maxlen)
1117 {
1118 #ifdef DEBUG_STRSIZE
1119   memset(path, 0xff, sizeof(*path) * maxlen);
1120 #endif
1121   bool wasrelative = true;
1122   const int filelen = strlen(path);
1123
1124 #ifdef WIN32
1125   if ((filelen >= 3 && BLI_path_is_abs(path)) || BLI_path_is_unc(path)) {
1126     wasrelative = false;
1127   }
1128 #else
1129   if (filelen >= 2 && path[0] == '/') {
1130     wasrelative = false;
1131   }
1132 #endif
1133
1134   if (wasrelative) {
1135     char cwd[FILE_MAX];
1136     /* in case the full path to the blend isn't used */
1137     if (BLI_current_working_dir(cwd, sizeof(cwd))) {
1138       char origpath[FILE_MAX];
1139       BLI_strncpy(origpath, path, FILE_MAX);
1140       BLI_join_dirfile(path, maxlen, cwd, origpath);
1141     }
1142     else {
1143       printf("Could not get the current working directory - $PWD for an unknown reason.\n");
1144     }
1145   }
1146
1147   return wasrelative;
1148 }
1149
1150 #ifdef _WIN32
1151 /**
1152  * Tries appending each of the semicolon-separated extensions in the PATHEXT
1153  * environment variable (Windows-only) onto *name in turn until such a file is found.
1154  * Returns success/failure.
1155  */
1156 bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen)
1157 {
1158   bool retval = false;
1159   int type;
1160
1161   type = BLI_exists(name);
1162   if ((type == 0) || S_ISDIR(type)) {
1163     /* typically 3-5, ".EXE", ".BAT"... etc */
1164     const int ext_max = 12;
1165     const char *ext = BLI_getenv("PATHEXT");
1166     if (ext) {
1167       const int name_len = strlen(name);
1168       char *filename = alloca(name_len + ext_max);
1169       char *filename_ext;
1170       const char *ext_next;
1171
1172       /* null terminated in the loop */
1173       memcpy(filename, name, name_len);
1174       filename_ext = filename + name_len;
1175
1176       do {
1177         int ext_len;
1178         ext_next = strchr(ext, ';');
1179         ext_len = ext_next ? ((ext_next++) - ext) : strlen(ext);
1180
1181         if (LIKELY(ext_len < ext_max)) {
1182           memcpy(filename_ext, ext, ext_len);
1183           filename_ext[ext_len] = '\0';
1184
1185           type = BLI_exists(filename);
1186           if (type && (!S_ISDIR(type))) {
1187             retval = true;
1188             BLI_strncpy(name, filename, maxlen);
1189             break;
1190           }
1191         }
1192       } while ((ext = ext_next));
1193     }
1194   }
1195   else {
1196     retval = true;
1197   }
1198
1199   return retval;
1200 }
1201 #endif /* WIN32 */
1202
1203 /**
1204  * Search for a binary (executable)
1205  */
1206 bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *name)
1207 {
1208 #ifdef DEBUG_STRSIZE
1209   memset(fullname, 0xff, sizeof(*fullname) * maxlen);
1210 #endif
1211   const char *path;
1212   bool retval = false;
1213
1214 #ifdef _WIN32
1215   const char separator = ';';
1216 #else
1217   const char separator = ':';
1218 #endif
1219
1220   path = BLI_getenv("PATH");
1221   if (path) {
1222     char filename[FILE_MAX];
1223     const char *temp;
1224
1225     do {
1226       temp = strchr(path, separator);
1227       if (temp) {
1228         memcpy(filename, path, temp - path);
1229         filename[temp - path] = 0;
1230         path = temp + 1;
1231       }
1232       else {
1233         BLI_strncpy(filename, path, sizeof(filename));
1234       }
1235
1236       BLI_path_append(filename, maxlen, name);
1237       if (
1238 #ifdef _WIN32
1239           BLI_path_program_extensions_add_win32(filename, maxlen)
1240 #else
1241           BLI_exists(filename)
1242 #endif
1243       ) {
1244         BLI_strncpy(fullname, filename, maxlen);
1245         retval = true;
1246         break;
1247       }
1248     } while (temp);
1249   }
1250
1251   if (retval == false) {
1252     *fullname = '\0';
1253   }
1254
1255   return retval;
1256 }
1257
1258 /**
1259  * Sets the specified environment variable to the specified value,
1260  * and clears it if val == NULL.
1261  */
1262 void BLI_setenv(const char *env, const char *val)
1263 {
1264   /* free windows */
1265
1266 #if (defined(WIN32) || defined(WIN64))
1267   uputenv(env, val);
1268
1269 #else
1270   /* linux/osx/bsd */
1271   if (val) {
1272     setenv(env, val, 1);
1273   }
1274   else {
1275     unsetenv(env);
1276   }
1277 #endif
1278 }
1279
1280 /**
1281  * Only set an env var if already not there.
1282  * Like Unix setenv(env, val, 0);
1283  *
1284  * (not used anywhere).
1285  */
1286 void BLI_setenv_if_new(const char *env, const char *val)
1287 {
1288   if (BLI_getenv(env) == NULL) {
1289     BLI_setenv(env, val);
1290   }
1291 }
1292
1293 /**
1294  * Get an env var, result has to be used immediately.
1295  */
1296 const char *BLI_getenv(const char *env)
1297 {
1298 #ifdef _MSC_VER
1299   static char buffer[32767]; /* 32767 is the total size of the environment block on windows*/
1300   if (GetEnvironmentVariableA(env, buffer, sizeof(buffer))) {
1301     return buffer;
1302   }
1303   else {
1304     return NULL;
1305   }
1306 #else
1307   return getenv(env);
1308 #endif
1309 }
1310
1311 /**
1312  * Strips off nonexistent (or non-accessible) subdirectories from the end of *dir,
1313  * leaving the path of the lowest-level directory that does exist and we can read.
1314  */
1315 void BLI_make_exist(char *dir)
1316 {
1317   bool valid_path = true;
1318
1319   /* Loop as long as cur path is not a dir, and we can get a parent path. */
1320   while ((BLI_access(dir, R_OK) != 0) && (valid_path = BLI_parent_dir(dir))) {
1321     /* pass */
1322   }
1323
1324   /* If we could not find an existing dir, use default root... */
1325   if (!valid_path || !dir[0]) {
1326 #ifdef WIN32
1327     get_default_root(dir);
1328 #else
1329     strcpy(dir, "/");
1330 #endif
1331   }
1332 }
1333
1334 /**
1335  * Ensures that the parent directory of *name exists.
1336  *
1337  * \return true on success (i.e. given path now exists on FS), false otherwise.
1338  */
1339 bool BLI_make_existing_file(const char *name)
1340 {
1341   char di[FILE_MAX];
1342   BLI_split_dir_part(name, di, sizeof(di));
1343
1344   /* make if the dir doesn't exist */
1345   return BLI_dir_create_recursive(di);
1346 }
1347
1348 /**
1349  * Returns in *string the concatenation of *dir and *file (also with *relabase on the
1350  * front if specified and *dir begins with "//"). Normalizes all occurrences of path
1351  * separators, including ensuring there is exactly one between the copies of *dir and *file,
1352  * and between the copies of *relabase and *dir.
1353  *
1354  * \param relabase: Optional prefix to substitute for "//" on front of *dir
1355  * \param string: Area to return result
1356  */
1357 void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file)
1358 {
1359   int sl;
1360
1361   if (string) {
1362     /* ensure this is always set even if dir/file are NULL */
1363     string[0] = '\0';
1364
1365     if (ELEM(NULL, dir, file)) {
1366       return; /* We don't want any NULLs */
1367     }
1368   }
1369   else {
1370     return; /* string is NULL, probably shouldnt happen but return anyway */
1371   }
1372
1373   /* Resolve relative references */
1374   if (relabase && dir[0] == '/' && dir[1] == '/') {
1375     char *lslash;
1376
1377     /* Get the file name, chop everything past the last slash (ie. the filename) */
1378     strcpy(string, relabase);
1379
1380     lslash = (char *)BLI_last_slash(string);
1381     if (lslash) {
1382       *(lslash + 1) = 0;
1383     }
1384
1385     dir += 2; /* Skip over the relative reference */
1386   }
1387 #ifdef WIN32
1388   else {
1389     if (BLI_strnlen(dir, 3) >= 2 && dir[1] == ':') {
1390       BLI_strncpy(string, dir, 3);
1391       dir += 2;
1392     }
1393     else if (BLI_strnlen(dir, 3) >= 2 && BLI_path_is_unc(dir)) {
1394       string[0] = 0;
1395     }
1396     else { /* no drive specified */
1397            /* first option: get the drive from the relabase if it has one */
1398       if (relabase && BLI_strnlen(relabase, 3) >= 2 && relabase[1] == ':') {
1399         BLI_strncpy(string, relabase, 3);
1400         string[2] = '\\';
1401         string[3] = '\0';
1402       }
1403       else { /* we're out of luck here, guessing the first valid drive, usually c:\ */
1404         get_default_root(string);
1405       }
1406
1407       /* ignore leading slashes */
1408       while (*dir == '/' || *dir == '\\') {
1409         dir++;
1410       }
1411     }
1412   }
1413 #endif
1414
1415   strcat(string, dir);
1416
1417   /* Make sure string ends in one (and only one) slash */
1418   /* first trim all slashes from the end of the string */
1419   sl = strlen(string);
1420   while (sl > 0 && (string[sl - 1] == '/' || string[sl - 1] == '\\')) {
1421     string[sl - 1] = '\0';
1422     sl--;
1423   }
1424   /* since we've now removed all slashes, put back one slash at the end. */
1425   strcat(string, "/");
1426
1427   while (*file && (*file == '/' || *file == '\\')) {
1428     /* Trim slashes from the front of file */
1429     file++;
1430   }
1431
1432   strcat(string, file);
1433
1434   /* Push all slashes to the system preferred direction */
1435   BLI_path_native_slash(string);
1436 }
1437
1438 static bool path_extension_check_ex(const char *str,
1439                                     const size_t str_len,
1440                                     const char *ext,
1441                                     const size_t ext_len)
1442 {
1443   BLI_assert(strlen(str) == str_len);
1444   BLI_assert(strlen(ext) == ext_len);
1445
1446   return (((str_len == 0 || ext_len == 0 || ext_len >= str_len) == 0) &&
1447           (BLI_strcasecmp(ext, str + str_len - ext_len) == 0));
1448 }
1449
1450 /* does str end with ext. */
1451 bool BLI_path_extension_check(const char *str, const char *ext)
1452 {
1453   return path_extension_check_ex(str, strlen(str), ext, strlen(ext));
1454 }
1455
1456 bool BLI_path_extension_check_n(const char *str, ...)
1457 {
1458   const size_t str_len = strlen(str);
1459
1460   va_list args;
1461   const char *ext;
1462   bool ret = false;
1463
1464   va_start(args, str);
1465
1466   while ((ext = (const char *)va_arg(args, void *))) {
1467     if (path_extension_check_ex(str, str_len, ext, strlen(ext))) {
1468       ret = true;
1469       break;
1470     }
1471   }
1472
1473   va_end(args);
1474
1475   return ret;
1476 }
1477
1478 /* does str end with any of the suffixes in *ext_array. */
1479 bool BLI_path_extension_check_array(const char *str, const char **ext_array)
1480 {
1481   const size_t str_len = strlen(str);
1482   int i = 0;
1483
1484   while (ext_array[i]) {
1485     if (path_extension_check_ex(str, str_len, ext_array[i], strlen(ext_array[i]))) {
1486       return true;
1487     }
1488
1489     i++;
1490   }
1491   return false;
1492 }
1493
1494 /**
1495  * Semicolon separated wildcards, eg:
1496  *  '*.zip;*.py;*.exe'
1497  * does str match any of the semicolon-separated glob patterns in fnmatch.
1498  */
1499 bool BLI_path_extension_check_glob(const char *str, const char *ext_fnmatch)
1500 {
1501   const char *ext_step = ext_fnmatch;
1502   char pattern[16];
1503
1504   while (ext_step[0]) {
1505     const char *ext_next;
1506     size_t len_ext;
1507
1508     if ((ext_next = strchr(ext_step, ';'))) {
1509       len_ext = ext_next - ext_step + 1;
1510       BLI_strncpy(pattern, ext_step, (len_ext > sizeof(pattern)) ? sizeof(pattern) : len_ext);
1511     }
1512     else {
1513       len_ext = BLI_strncpy_rlen(pattern, ext_step, sizeof(pattern));
1514     }
1515
1516     if (fnmatch(pattern, str, FNM_CASEFOLD) == 0) {
1517       return true;
1518     }
1519     ext_step += len_ext;
1520   }
1521
1522   return false;
1523 }
1524
1525 /**
1526  * Does basic validation of the given glob string, to prevent common issues from string truncation.
1527  *
1528  * For now, only forbids last group to be a wildcard-only one, if there are more than one group
1529  * (i.e. things like "*.txt;*.cpp;*" are changed to "*.txt;*.cpp;")
1530  *
1531  * \returns true if it had to modify given \a ext_fnmatch pattern.
1532  */
1533 bool BLI_path_extension_glob_validate(char *ext_fnmatch)
1534 {
1535   bool only_wildcards = false;
1536
1537   for (size_t i = strlen(ext_fnmatch); i-- > 0;) {
1538     if (ext_fnmatch[i] == ';') {
1539       /* Group separator, we truncate here if we only had wildcards so far.
1540        * Otherwise, all is sound and fine. */
1541       if (only_wildcards) {
1542         ext_fnmatch[i] = '\0';
1543         return true;
1544       }
1545       return false;
1546     }
1547     if (!ELEM(ext_fnmatch[i], '?', '*')) {
1548       /* Non-wildcard char, we can break here and consider the pattern valid. */
1549       return false;
1550     }
1551     /* So far, only wildcards in last group of the pattern... */
1552     only_wildcards = true;
1553   }
1554   /* Only one group in the pattern, so even if its only made of wildcard(s),
1555    * it is assumed vaid. */
1556   return false;
1557 }
1558
1559 /**
1560  * Removes any existing extension on the end of \a path and appends \a ext.
1561  * \return false if there was no room.
1562  */
1563 bool BLI_path_extension_replace(char *path, size_t maxlen, const char *ext)
1564 {
1565 #ifdef DEBUG_STRSIZE
1566   memset(path, 0xff, sizeof(*path) * maxlen);
1567 #endif
1568   const size_t path_len = strlen(path);
1569   const size_t ext_len = strlen(ext);
1570   ssize_t a;
1571
1572   for (a = path_len - 1; a >= 0; a--) {
1573     if (ELEM(path[a], '.', '/', '\\')) {
1574       break;
1575     }
1576   }
1577
1578   if ((a < 0) || (path[a] != '.')) {
1579     a = path_len;
1580   }
1581
1582   if (a + ext_len >= maxlen) {
1583     return false;
1584   }
1585
1586   memcpy(path + a, ext, ext_len + 1);
1587   return true;
1588 }
1589
1590 /**
1591  * Strip's trailing '.'s and adds the extension only when needed
1592  */
1593 bool BLI_path_extension_ensure(char *path, size_t maxlen, const char *ext)
1594 {
1595 #ifdef DEBUG_STRSIZE
1596   memset(path, 0xff, sizeof(*path) * maxlen);
1597 #endif
1598   const size_t path_len = strlen(path);
1599   const size_t ext_len = strlen(ext);
1600   ssize_t a;
1601
1602   /* first check the extension is already there */
1603   if ((ext_len <= path_len) && (STREQ(path + (path_len - ext_len), ext))) {
1604     return true;
1605   }
1606
1607   for (a = path_len - 1; a >= 0; a--) {
1608     if (path[a] == '.') {
1609       path[a] = '\0';
1610     }
1611     else {
1612       break;
1613     }
1614   }
1615   a++;
1616
1617   if (a + ext_len >= maxlen) {
1618     return false;
1619   }
1620
1621   memcpy(path + a, ext, ext_len + 1);
1622   return true;
1623 }
1624
1625 bool BLI_ensure_filename(char *filepath, size_t maxlen, const char *filename)
1626 {
1627 #ifdef DEBUG_STRSIZE
1628   memset(filepath, 0xff, sizeof(*filepath) * maxlen);
1629 #endif
1630   char *c = (char *)BLI_last_slash(filepath);
1631   if (!c || ((c - filepath) < maxlen - (strlen(filename) + 1))) {
1632     strcpy(c ? &c[1] : filepath, filename);
1633     return true;
1634   }
1635   return false;
1636 }
1637
1638 /**
1639  * Converts `/foo/bar.txt` to "/foo/" and `bar.txt`
1640  *
1641  * - Wont change \a string.
1642  * - Wont create any directories.
1643  * - Doesn't use CWD, or deal with relative paths.
1644  * - Only fill's in \a dir and \a file when they are non NULL.
1645  * */
1646 void BLI_split_dirfile(
1647     const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen)
1648 {
1649 #ifdef DEBUG_STRSIZE
1650   memset(dir, 0xff, sizeof(*dir) * dirlen);
1651   memset(file, 0xff, sizeof(*file) * filelen);
1652 #endif
1653   const char *lslash_str = BLI_last_slash(string);
1654   const size_t lslash = lslash_str ? (size_t)(lslash_str - string) + 1 : 0;
1655
1656   if (dir) {
1657     if (lslash) {
1658       /* +1 to include the slash and the last char */
1659       BLI_strncpy(dir, string, MIN2(dirlen, lslash + 1));
1660     }
1661     else {
1662       dir[0] = '\0';
1663     }
1664   }
1665
1666   if (file) {
1667     BLI_strncpy(file, string + lslash, filelen);
1668   }
1669 }
1670
1671 /**
1672  * Copies the parent directory part of string into *dir, max length dirlen.
1673  */
1674 void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
1675 {
1676   BLI_split_dirfile(string, dir, NULL, dirlen, 0);
1677 }
1678
1679 /**
1680  * Copies the leaf filename part of string into *file, max length filelen.
1681  */
1682 void BLI_split_file_part(const char *string, char *file, const size_t filelen)
1683 {
1684   BLI_split_dirfile(string, NULL, file, 0, filelen);
1685 }
1686
1687 /**
1688  * Append a filename to a dir, ensuring slash separates.
1689  */
1690 void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file)
1691 {
1692   size_t dirlen = BLI_strnlen(dst, maxlen);
1693
1694   /* inline BLI_add_slash */
1695   if ((dirlen > 0) && (dst[dirlen - 1] != SEP)) {
1696     dst[dirlen++] = SEP;
1697     dst[dirlen] = '\0';
1698   }
1699
1700   if (dirlen >= maxlen) {
1701     return; /* fills the path */
1702   }
1703
1704   BLI_strncpy(dst + dirlen, file, maxlen - dirlen);
1705 }
1706
1707 /**
1708  * Simple appending of filename to dir, does not check for valid path!
1709  * Puts result into *dst, which may be same area as *dir.
1710  */
1711 void BLI_join_dirfile(char *__restrict dst,
1712                       const size_t maxlen,
1713                       const char *__restrict dir,
1714                       const char *__restrict file)
1715 {
1716 #ifdef DEBUG_STRSIZE
1717   memset(dst, 0xff, sizeof(*dst) * maxlen);
1718 #endif
1719   size_t dirlen = BLI_strnlen(dir, maxlen);
1720
1721   /* args can't match */
1722   BLI_assert(!ELEM(dst, dir, file));
1723
1724   if (dirlen == maxlen) {
1725     memcpy(dst, dir, dirlen);
1726     dst[dirlen - 1] = '\0';
1727     return; /* dir fills the path */
1728   }
1729   else {
1730     memcpy(dst, dir, dirlen + 1);
1731   }
1732
1733   if (dirlen + 1 >= maxlen) {
1734     return; /* fills the path */
1735   }
1736
1737   /* inline BLI_add_slash */
1738   if ((dirlen > 0) && !ELEM(dst[dirlen - 1], SEP, ALTSEP)) {
1739     dst[dirlen++] = SEP;
1740     dst[dirlen] = '\0';
1741   }
1742
1743   if (dirlen >= maxlen) {
1744     return; /* fills the path */
1745   }
1746
1747   BLI_strncpy(dst + dirlen, file, maxlen - dirlen);
1748 }
1749
1750 /**
1751  * Join multiple strings into a path, ensuring only a single path separator between each,
1752  * and trailing slash is kept.
1753  *
1754  * \note If you want a trailing slash, add ``SEP_STR`` as the last path argument,
1755  * duplicate slashes will be cleaned up.
1756  */
1757 size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *path, ...)
1758 {
1759 #ifdef DEBUG_STRSIZE
1760   memset(dst, 0xff, sizeof(*dst) * dst_len);
1761 #endif
1762   if (UNLIKELY(dst_len == 0)) {
1763     return 0;
1764   }
1765   const size_t dst_last = dst_len - 1;
1766   size_t ofs = BLI_strncpy_rlen(dst, path, dst_len);
1767
1768   if (ofs == dst_last) {
1769     return ofs;
1770   }
1771
1772   /* remove trailing slashes, unless there are _only_ trailing slashes
1773    * (allow "//" as the first argument). */
1774   bool has_trailing_slash = false;
1775   if (ofs != 0) {
1776     size_t len = ofs;
1777     while ((len != 0) && ELEM(path[len - 1], SEP, ALTSEP)) {
1778       len -= 1;
1779     }
1780     if (len != 0) {
1781       ofs = len;
1782     }
1783     has_trailing_slash = (path[len] != '\0');
1784   }
1785
1786   va_list args;
1787   va_start(args, path);
1788   while ((path = (const char *)va_arg(args, const char *))) {
1789     has_trailing_slash = false;
1790     const char *path_init = path;
1791     while (ELEM(path[0], SEP, ALTSEP)) {
1792       path++;
1793     }
1794     size_t len = strlen(path);
1795     if (len != 0) {
1796       while ((len != 0) && ELEM(path[len - 1], SEP, ALTSEP)) {
1797         len -= 1;
1798       }
1799
1800       if (len != 0) {
1801         /* the very first path may have a slash at the end */
1802         if (ofs && !ELEM(dst[ofs - 1], SEP, ALTSEP)) {
1803           dst[ofs++] = SEP;
1804           if (ofs == dst_last) {
1805             break;
1806           }
1807         }
1808         has_trailing_slash = (path[len] != '\0');
1809         if (ofs + len >= dst_last) {
1810           len = dst_last - ofs;
1811         }
1812         memcpy(&dst[ofs], path, len);
1813         ofs += len;
1814         if (ofs == dst_last) {
1815           break;
1816         }
1817       }
1818     }
1819     else {
1820       has_trailing_slash = (path_init != path);
1821     }
1822   }
1823   va_end(args);
1824
1825   if (has_trailing_slash) {
1826     if ((ofs != dst_last) && (ofs != 0) && (ELEM(dst[ofs - 1], SEP, ALTSEP) == 0)) {
1827       dst[ofs++] = SEP;
1828     }
1829   }
1830
1831   BLI_assert(ofs <= dst_last);
1832   dst[ofs] = '\0';
1833
1834   return ofs;
1835 }
1836
1837 /**
1838  * like pythons os.path.basename()
1839  *
1840  * \return The pointer into \a path string immediately after last slash,
1841  * or start of \a path if none found.
1842  */
1843 const char *BLI_path_basename(const char *path)
1844 {
1845   const char *const filename = BLI_last_slash(path);
1846   return filename ? filename + 1 : path;
1847 }
1848
1849 /**
1850  * Get an element of the path at an index, eg:
1851  * "/some/path/file.txt" where an index of...
1852  * - 0 or -3: "some"
1853  * - 1 or -2: "path"
1854  * - 2 or -1: "file.txt"
1855  *
1856  * Ignores multiple slashes at any point in the path (including start/end).
1857  */
1858 bool BLI_path_name_at_index(const char *__restrict path,
1859                             const int index,
1860                             int *__restrict r_offset,
1861                             int *__restrict r_len)
1862 {
1863   if (index >= 0) {
1864     int index_step = 0;
1865     int prev = -1;
1866     int i = 0;
1867     while (true) {
1868       const char c = path[i];
1869       if (ELEM(c, SEP, ALTSEP, '\0')) {
1870         if (prev + 1 != i) {
1871           prev += 1;
1872           if (index_step == index) {
1873             *r_offset = prev;
1874             *r_len = i - prev;
1875             /* printf("!!! %d %d\n", start, end); */
1876             return true;
1877           }
1878           index_step += 1;
1879         }
1880         if (c == '\0') {
1881           break;
1882         }
1883         prev = i;
1884       }
1885       i += 1;
1886     }
1887     return false;
1888   }
1889   else {
1890     /* negative number, reverse where -1 is the last element */
1891     int index_step = -1;
1892     int prev = strlen(path);
1893     int i = prev - 1;
1894     while (true) {
1895       const char c = i >= 0 ? path[i] : '\0';
1896       if (ELEM(c, SEP, ALTSEP, '\0')) {
1897         if (prev - 1 != i) {
1898           i += 1;
1899           if (index_step == index) {
1900             *r_offset = i;
1901             *r_len = prev - i;
1902             return true;
1903           }
1904           index_step -= 1;
1905         }
1906         if (c == '\0') {
1907           break;
1908         }
1909         prev = i;
1910       }
1911       i -= 1;
1912     }
1913     return false;
1914   }
1915 }
1916
1917 /**
1918  * Returns pointer to the leftmost path separator in string. Not actually used anywhere.
1919  */
1920 const char *BLI_first_slash(const char *string)
1921 {
1922   const char *const ffslash = strchr(string, '/');
1923   const char *const fbslash = strchr(string, '\\');
1924
1925   if (!ffslash) {
1926     return fbslash;
1927   }
1928   else if (!fbslash) {
1929     return ffslash;
1930   }
1931
1932   return (ffslash < fbslash) ? ffslash : fbslash;
1933 }
1934
1935 /**
1936  * Returns pointer to the rightmost path separator in string.
1937  */
1938 const char *BLI_last_slash(const char *string)
1939 {
1940   const char *const lfslash = strrchr(string, '/');
1941   const char *const lbslash = strrchr(string, '\\');
1942
1943   if (!lfslash) {
1944     return lbslash;
1945   }
1946   else if (!lbslash) {
1947     return lfslash;
1948   }
1949
1950   return (lfslash > lbslash) ? lfslash : lbslash;
1951 }
1952
1953 /**
1954  * Appends a slash to string if there isn't one there already.
1955  * Returns the new length of the string.
1956  */
1957 int BLI_add_slash(char *string)
1958 {
1959   int len = strlen(string);
1960   if (len == 0 || string[len - 1] != SEP) {
1961     string[len] = SEP;
1962     string[len + 1] = '\0';
1963     return len + 1;
1964   }
1965   return len;
1966 }
1967
1968 /**
1969  * Removes the last slash and everything after it to the end of string, if there is one.
1970  */
1971 void BLI_del_slash(char *string)
1972 {
1973   int len = strlen(string);
1974   while (len) {
1975     if (string[len - 1] == SEP) {
1976       string[len - 1] = '\0';
1977       len--;
1978     }
1979     else {
1980       break;
1981     }
1982   }
1983 }
1984
1985 /**
1986  * Changes to the path separators to the native ones for this OS.
1987  */
1988 void BLI_path_native_slash(char *path)
1989 {
1990 #ifdef WIN32
1991   if (path && BLI_strnlen(path, 3) > 2) {
1992     BLI_str_replace_char(path + 2, '/', '\\');
1993   }
1994 #else
1995   BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), '\\', '/');
1996 #endif
1997 }