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