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