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