libretro: adjust psxclock description
[pcsx_rearmed.git] / deps / libretro-common / file / file_path.c
1 /* Copyright  (C) 2010-2020 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (file_path.c).
5  * ---------------------------------------------------------------------------------------
6  *
7  * Permission is hereby granted, free of charge,
8  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <locale.h>
28
29 #include <sys/stat.h>
30
31 #include <boolean.h>
32 #include <file/file_path.h>
33 #include <retro_miscellaneous.h>
34 #include <string/stdstring.h>
35 #include <time/rtime.h>
36
37 /* TODO: There are probably some unnecessary things on this huge include list now but I'm too afraid to touch it */
38 #ifdef __APPLE__
39 #include <CoreFoundation/CoreFoundation.h>
40 #endif
41 #ifdef __HAIKU__
42 #include <kernel/image.h>
43 #endif
44 #ifndef __MACH__
45 #include <compat/strl.h>
46 #include <compat/posix_string.h>
47 #endif
48 #include <retro_miscellaneous.h>
49 #include <encodings/utf.h>
50
51 #ifdef _WIN32
52 #include <direct.h>
53 #else
54 #include <unistd.h> /* stat() is defined here */
55 #endif
56
57 #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
58 #ifdef __WINRT__
59 #include <uwp/uwp_func.h>
60 #endif
61 #endif
62
63 /* Assume W-functions do not work below Win2K and Xbox platforms */
64 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
65
66 #ifndef LEGACY_WIN32
67 #define LEGACY_WIN32
68 #endif
69
70 #endif
71
72 /* Time format strings with AM-PM designation require special
73  * handling due to platform dependence */
74 void strftime_am_pm(char *s, size_t len, const char* format,
75       const void *ptr)
76 {
77    char *local = NULL;
78    const struct tm *timeptr = (const struct tm*)ptr;
79
80    /* Ensure correct locale is set
81     * > Required for localised AM/PM strings */
82    setlocale(LC_TIME, "");
83
84    strftime(s, len, format, timeptr);
85 #if !(defined(__linux__) && !defined(ANDROID))
86    if ((local = local_to_utf8_string_alloc(s)))
87    {
88       if (!string_is_empty(local))
89          strlcpy(s, local, len);
90
91       free(local);
92       local = NULL;
93    }
94 #endif
95 }
96
97 /**
98  * Create a new linked list with one node in it
99  * The path on this node will be set to NULL
100 **/
101 struct path_linked_list* path_linked_list_new(void)
102 {
103    struct path_linked_list* paths_list = (struct path_linked_list*)malloc(sizeof(*paths_list));
104    paths_list->next = NULL;
105    paths_list->path = NULL;
106    return paths_list;
107 }
108
109 /**
110  * path_linked_list_free:
111  *
112  * Free the entire linked list
113  **/
114 void path_linked_list_free(struct path_linked_list *in_path_linked_list)
115 {
116    struct path_linked_list *node_tmp = (struct path_linked_list*)in_path_linked_list;
117    while (node_tmp)
118    {
119       struct path_linked_list *hold = NULL;
120       if (node_tmp->path)
121          free(node_tmp->path);
122       hold     = (struct path_linked_list*)node_tmp;
123       node_tmp = node_tmp->next;
124       if (hold)
125          free(hold);
126    }
127 }
128
129 /**
130  * path_linked_list_add_path:
131  *
132  * Add a node to the linked list with this path
133  * If the first node's path if it's not yet set the path
134  * on this node instead
135 **/
136 void path_linked_list_add_path(struct path_linked_list *in_path_linked_list,
137       char *path)
138 {
139     /* If the first item does not have a path this is
140       a list which has just been created, so we just fill 
141       the path for the first item
142    */
143    if (!in_path_linked_list->path)
144       in_path_linked_list->path = strdup(path);
145    else
146    {  
147       struct path_linked_list *node = (struct path_linked_list*) malloc(sizeof(*node));
148
149       if (node)
150       {
151          struct path_linked_list *head = in_path_linked_list;
152
153          node->next                    = NULL;
154          node->path                    = strdup(path);
155
156          if (head)
157          {
158             while (head->next)
159                head        = head->next;
160
161             head->next     = node;
162          }
163          else
164             in_path_linked_list = node;
165       }
166    }
167 }
168
169 /**
170  * path_get_archive_delim:
171  * @path               : path
172  *
173  * Find delimiter of an archive file. Only the first '#'
174  * after a compression extension is considered.
175  *
176  * @return pointer to the delimiter in the path if it contains
177  * a path inside a compressed file, otherwise NULL.
178  **/
179 const char *path_get_archive_delim(const char *path)
180 {
181    char buf[5];
182    /* Find delimiter position
183     * > Since filenames may contain '#' characters,
184     *   must loop until we find the first '#' that
185     *   is directly *after* a compression extension */
186    const char *delim      = strchr(path, '#');
187
188    while (delim)
189    {
190       /* Check whether this is a known archive type
191        * > Note: The code duplication here is
192        *   deliberate, to maximise performance */
193       if (delim - path > 4)
194       {
195          strlcpy(buf, delim - 4, sizeof(buf));
196          buf[4] = '\0';
197
198          string_to_lower(buf);
199
200          /* Check if this is a '.zip', '.apk' or '.7z' file */
201          if (string_is_equal(buf,     ".zip") ||
202              string_is_equal(buf,     ".apk") ||
203              string_is_equal(buf + 1, ".7z"))
204             return delim;
205       }
206       else if (delim - path > 3)
207       {
208          strlcpy(buf, delim - 3, sizeof(buf));
209          buf[3] = '\0';
210
211          string_to_lower(buf);
212
213          /* Check if this is a '.7z' file */
214          if (string_is_equal(buf, ".7z"))
215             return delim;
216       }
217
218       delim++;
219       delim = strchr(delim, '#');
220    }
221
222    return NULL;
223 }
224
225 /**
226  * path_get_extension:
227  * @path               : path
228  *
229  * Gets extension of file. Only '.'s
230  * after the last slash are considered.
231  *
232  * @return extension part from the path.
233  **/
234 const char *path_get_extension(const char *path)
235 {
236    const char *ext;
237    if (!string_is_empty(path) && ((ext = (char*)strrchr(path_basename(path), '.'))))
238       return ext + 1;
239    return "";
240 }
241
242 /**
243  * path_get_extension_mutable:
244  * @path               : path
245  *
246  * Specialized version of path_get_extension(). Return
247  * value is mutable.
248  *
249  * Gets extension of file. Only '.'s
250  * after the last slash are considered.
251  *
252  * @return extension part from the path.
253  **/
254 char *path_get_extension_mutable(const char *path)
255 {
256    char *ext = NULL;
257    if (!string_is_empty(path) && ((ext = (char*)strrchr(path_basename(path), '.'))))
258       return ext;
259    return NULL;
260 }
261
262 /**
263  * path_remove_extension:
264  * @path               : path
265  *
266  * Mutates path by removing its extension. Removes all
267  * text after and including the last '.'.
268  * Only '.'s after the last slash are considered.
269  *
270  * @return
271  * 1) If path has an extension, returns path with the
272  *    extension removed.
273  * 2) If there is no extension, returns NULL.
274  * 3) If path is empty or NULL, returns NULL
275  **/
276 char *path_remove_extension(char *path)
277 {
278    char *last = !string_is_empty(path)
279       ? (char*)strrchr(path_basename(path), '.') : NULL;
280    if (!last)
281       return NULL;
282    if (*last)
283       *last = '\0';
284    return path;
285 }
286
287 /**
288  * path_is_compressed_file:
289  * @path               : path
290  *
291  * Checks if path is a compressed file.
292  *
293  * @return true if path is a compressed file, otherwise false.
294  **/
295 bool path_is_compressed_file(const char* path)
296 {
297    const char *ext = path_get_extension(path);
298    if (!string_is_empty(ext))
299       return (   string_is_equal_noncase(ext, "zip")
300               || string_is_equal_noncase(ext, "apk")
301               || string_is_equal_noncase(ext, "7z"));
302    return false;
303 }
304
305 /**
306  * fill_pathname:
307  * @out_path           : output path
308  * @in_path            : input  path
309  * @replace            : what to replace
310  * @size               : buffer size of output path
311  *
312  * FIXME: Verify
313  *
314  * Replaces filename extension with 'replace' and outputs result to out_path.
315  * The extension here is considered to be the string from the last '.'
316  * to the end.
317  *
318  * Only '.'s after the last slash are considered as extensions.
319  * If no '.' is present, in_path and replace will simply be concatenated.
320  * 'size' is buffer size of 'out_path'.
321  * E.g.: in_path = "/foo/bar/baz/boo.c", replace = ".asm" =>
322  * out_path = "/foo/bar/baz/boo.asm"
323  * E.g.: in_path = "/foo/bar/baz/boo.c", replace = ""     =>
324  * out_path = "/foo/bar/baz/boo"
325  *
326  * @return Length of the string copied into @out
327  */
328 size_t fill_pathname(char *out_path, const char *in_path,
329       const char *replace, size_t size)
330 {
331    char tmp_path[PATH_MAX_LENGTH];
332    char *tok   = NULL;
333    strlcpy(tmp_path, in_path, sizeof(tmp_path));
334    if ((tok = (char*)strrchr(path_basename(tmp_path), '.')))
335       *tok = '\0';
336
337    strlcpy(out_path, tmp_path, size);
338    return strlcat(out_path, replace, size);
339 }
340
341
342 /**
343  * find_last_slash:
344  * @str                : path
345  * @size               : size of path
346  *
347  * Find last slash in path. Tries to find
348  * a backslash on Windows too which takes precedence
349  * over regular slash.
350
351  * @return pointer to last slash/backslash found in @str.
352  **/
353 char *find_last_slash(const char *str)
354 {
355    const char *slash     = strrchr(str, '/');
356 #ifdef _WIN32
357    const char *backslash = strrchr(str, '\\');
358
359    if (!slash || (backslash > slash))
360       return (char*)backslash;
361 #endif
362    return (char*)slash;
363 }
364
365 /**
366  * fill_pathname_slash:
367  * @path               : path
368  * @size               : size of path
369  *
370  * Assumes path is a directory. Appends a slash
371  * if not already there.
372  **/
373 void fill_pathname_slash(char *path, size_t size)
374 {
375    size_t path_len;
376    const char *last_slash = find_last_slash(path);
377
378    if (!last_slash)
379    {
380       strlcat(path, PATH_DEFAULT_SLASH(), size);
381       return;
382    }
383
384    path_len               = strlen(path);
385    /* Try to preserve slash type. */
386    if (last_slash != (path + path_len - 1))
387    {
388       path[path_len]   = last_slash[0];
389       path[path_len+1] = '\0';
390    }
391 }
392
393 /**
394  * fill_pathname_dir:
395  * @in_dir             : input directory path
396  * @in_basename        : input basename to be appended to @in_dir
397  * @replace            : replacement to be appended to @in_basename
398  * @size               : size of buffer
399  *
400  * Appends basename of 'in_basename', to 'in_dir', along with 'replace'.
401  * Basename of in_basename is the string after the last '/' or '\\',
402  * i.e the filename without directories.
403  *
404  * If in_basename has no '/' or '\\', the whole 'in_basename' will be used.
405  * 'size' is buffer size of 'in_dir'.
406  *
407  * E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
408  * replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
409  **/
410 size_t fill_pathname_dir(char *in_dir, const char *in_basename,
411       const char *replace, size_t size)
412 {
413    const char *base = NULL;
414
415    fill_pathname_slash(in_dir, size);
416    base = path_basename(in_basename);
417    strlcat(in_dir, base, size);
418    return strlcat(in_dir, replace, size);
419 }
420
421 /**
422  * fill_pathname_base:
423  * @out                : output path
424  * @in_path            : input path
425  * @size               : size of output path
426  *
427  * Copies basename of @in_path into @out_path.
428  *
429  * @return Length of the string copied into @out
430  **/
431 size_t fill_pathname_base(char *out, const char *in_path, size_t size)
432 {
433    const char     *ptr = path_basename(in_path);
434    if (ptr)
435       return strlcpy(out, ptr, size);
436    return strlcpy(out, in_path, size);
437 }
438
439 /**
440  * fill_pathname_basedir:
441  * @out_dir            : output directory
442  * @in_path            : input path
443  * @size               : size of output directory
444  *
445  * Copies base directory of @in_path into @out_path.
446  * If in_path is a path without any slashes (relative current directory),
447  * @out_path will get path "./".
448  **/
449 void fill_pathname_basedir(char *out_dir,
450       const char *in_path, size_t size)
451 {
452    if (out_dir != in_path)
453       strlcpy(out_dir, in_path, size);
454    path_basedir(out_dir);
455 }
456
457 /**
458  * fill_pathname_parent_dir_name:
459  * @out_dir            : output directory
460  * @in_dir             : input directory
461  * @size               : size of output directory
462  *
463  * Copies only the parent directory name of @in_dir into @out_dir.
464  * The two buffers must not overlap. Removes trailing '/'.
465  *
466  * @return true on success, false if a slash was not found in the path.
467  **/
468 bool fill_pathname_parent_dir_name(char *out_dir,
469       const char *in_dir, size_t size)
470 {
471    char *temp   = strdup(in_dir);
472    char *last   = find_last_slash(temp);
473
474    if (last && last[1] == 0)
475    {
476       *last     = '\0';
477       last      = find_last_slash(temp);
478    }
479
480    /* Cut the last part of the string (the filename) after the slash,
481       leaving the directory name (or nested directory names) only. */
482    if (last)
483       *last     = '\0';
484
485    /* Point in_dir to the address of the last slash. */
486    /* If find_last_slash returns NULL, it means there was no slash in temp,
487       so use temp as-is. */
488    if (!(in_dir = find_last_slash(temp)))
489        in_dir   = temp;
490
491    if (in_dir && in_dir[1])
492    {
493        /* If path starts with an slash, eliminate it. */
494        if (path_is_absolute(in_dir))
495            strlcpy(out_dir, in_dir + 1, size);
496        else
497            strlcpy(out_dir, in_dir, size);
498        free(temp);
499        return true;
500    }
501
502    free(temp);
503    return false;
504 }
505
506 /**
507  * fill_pathname_parent_dir:
508  * @out_dir            : output directory
509  * @in_dir             : input directory
510  * @size               : size of output directory
511  *
512  * Copies parent directory of @in_dir into @out_dir.
513  * Assumes @in_dir is a directory. Keeps trailing '/'.
514  * If the path was already at the root directory,
515  * @out_dir will be an empty string.
516  **/
517 void fill_pathname_parent_dir(char *out_dir,
518       const char *in_dir, size_t size)
519 {
520    size_t len = 0;
521    if (out_dir != in_dir)
522       len = strlcpy(out_dir, in_dir, size);
523    else
524       len = strlen(out_dir);
525    path_parent_dir(out_dir, len);
526 }
527
528 /**
529  * fill_dated_filename:
530  * @out_filename       : output filename
531  * @ext                : extension of output filename
532  * @size               : buffer size of output filename
533  *
534  * Creates a 'dated' filename prefixed by 'RetroArch', and
535  * concatenates extension (@ext) to it.
536  *
537  * E.g.:
538  * out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
539  **/
540 size_t fill_dated_filename(char *out_filename,
541       const char *ext, size_t size)
542 {
543    time_t cur_time = time(NULL);
544    struct tm tm_;
545
546    rtime_localtime(&cur_time, &tm_);
547
548    strftime(out_filename, size,
549          "RetroArch-%m%d-%H%M%S", &tm_);
550    return strlcat(out_filename, ext, size);
551 }
552
553 /**
554  * fill_str_dated_filename:
555  * @out_filename       : output filename
556  * @in_str             : input string
557  * @ext                : extension of output filename
558  * @size               : buffer size of output filename
559  *
560  * Creates a 'dated' filename prefixed by the string @in_str, and
561  * concatenates extension (@ext) to it.
562  *
563  * E.g.:
564  * out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
565  *
566  * @return Length of the string copied into @out_path
567  **/
568 size_t fill_str_dated_filename(char *out_filename,
569       const char *in_str, const char *ext, size_t size)
570 {
571    char format[NAME_MAX_LENGTH];
572    struct tm tm_;
573    time_t cur_time = time(NULL);
574
575    rtime_localtime(&cur_time, &tm_);
576
577    strlcpy(out_filename, in_str, size);
578    if (string_is_empty(ext))
579    {
580       strftime(format, sizeof(format), "-%y%m%d-%H%M%S", &tm_);
581       return strlcat(out_filename, format, size);
582    }
583    strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", &tm_);
584    strlcat(out_filename, format, size);
585    return strlcat(out_filename, ext, size);
586 }
587
588 /**
589  * path_basedir:
590  * @path               : path
591  *
592  * Extracts base directory by mutating path.
593  * Keeps trailing '/'.
594  **/
595 void path_basedir(char *path)
596 {
597    char *last = NULL;
598    if (!path || path[0] == '\0' || path[1] == '\0')
599       return;
600
601    if ((last = find_last_slash(path)))
602       last[1] = '\0';
603    else
604    {
605       path[0] = '.';
606       path[1] = PATH_DEFAULT_SLASH_C();
607       path[2] = '\0';
608    }
609 }
610
611 /**
612  * path_parent_dir:
613  * @path               : path
614  * @len                : length of @path
615  *
616  * Extracts parent directory by mutating path.
617  * Assumes that path is a directory. Keeps trailing '/'.
618  * If the path was already at the root directory, returns empty string
619  **/
620 void path_parent_dir(char *path, size_t len)
621 {
622    if (!path)
623       return;
624    
625    if (len && PATH_CHAR_IS_SLASH(path[len - 1]))
626    {
627       bool path_was_absolute = path_is_absolute(path);
628
629       path[len - 1] = '\0';
630
631       if (path_was_absolute && !find_last_slash(path))
632       {
633          /* We removed the only slash from what used to be an absolute path.
634           * On Linux, this goes from "/" to an empty string and everything works fine,
635           * but on Windows, we went from C:\ to C:, which is not a valid path and that later
636           * gets errornously treated as a relative one by path_basedir and returns "./".
637           * What we really wanted is an empty string. */
638          path[0] = '\0';
639          return;
640       }
641    }
642    path_basedir(path);
643 }
644
645 /**
646  * path_basename:
647  * @path               : path
648  *
649  * Get basename from @path.
650  *
651  * @return basename from path.
652  **/
653 const char *path_basename(const char *path)
654 {
655    /* We cut at the first compression-related hash */
656    const char *delim = path_get_archive_delim(path);
657    if (delim)
658       return delim + 1;
659
660    {
661       /* We cut at the last slash */
662       const char *last  = find_last_slash(path);
663       if (last)
664          return last + 1;
665    }
666
667    return path;
668 }
669
670 /* Specialized version */
671 /**
672  * path_basename_nocompression:
673  * @path               : path
674  *
675  * Specialized version of path_basename().
676  * Get basename from @path.
677  *
678  * @return basename from path.
679  **/
680 const char *path_basename_nocompression(const char *path)
681 {
682    /* We cut at the last slash */
683    const char *last  = find_last_slash(path);
684    if (last)
685       return last + 1;
686    return path;
687 }
688
689 /**
690  * path_is_absolute:
691  * @path               : path
692  *
693  * Checks if @path is an absolute path or a relative path.
694  *
695  * @return true if path is absolute, false if path is relative.
696  **/
697 bool path_is_absolute(const char *path)
698 {
699    if (string_is_empty(path))
700       return false;
701
702    if (path[0] == '/')
703       return true;
704
705 #if defined(_WIN32)
706    /* Many roads lead to Rome...
707     * Note: Drive letter can only be 1 character long */
708    return ( string_starts_with_size(path,     "\\\\", STRLEN_CONST("\\\\"))
709          || string_starts_with_size(path + 1, ":/",   STRLEN_CONST(":/")) 
710          || string_starts_with_size(path + 1, ":\\",  STRLEN_CONST(":\\")));
711 #elif defined(__wiiu__) || defined(VITA)
712    {
713       const char *seperator = strchr(path, ':');
714       return (seperator && (seperator[1] == '/'));
715    }
716 #endif
717
718    return false;
719 }
720
721 /**
722  * path_resolve_realpath:
723  * @buf                : input and output buffer for path
724  * @size               : size of buffer
725  * @resolve_symlinks   : whether to resolve symlinks or not
726  *
727  * Resolves use of ".", "..", multiple slashes etc in absolute paths.
728  *
729  * Relative paths are rebased on the current working dir.
730  *
731  * @return @buf if successful, NULL otherwise.
732  * Note: Not implemented on consoles
733  * Note: Symlinks are only resolved on Unix-likes
734  * Note: The current working dir might not be what you expect,
735  *       e.g. on Android it is "/"
736  *       Use of fill_pathname_resolve_relative() should be prefered
737  **/
738 char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks)
739 {
740 #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
741 #ifdef _WIN32
742    char *ret = NULL;
743    wchar_t *rel_path = utf8_to_utf16_string_alloc(buf);
744
745    if (rel_path)
746    {
747       wchar_t abs_path[PATH_MAX_LENGTH];
748
749       if (_wfullpath(abs_path, rel_path, PATH_MAX_LENGTH))
750       {
751          char *tmp = utf16_to_utf8_string_alloc(abs_path);
752
753          if (tmp)
754          {
755             strlcpy(buf, tmp, size);
756             free(tmp);
757             ret = buf;
758          }
759       }
760
761       free(rel_path);
762    }
763
764    return ret;
765 #else
766    char tmp[PATH_MAX_LENGTH];
767    size_t t;
768    char *p;
769    const char *next;
770    const char *buf_end;
771
772    if (resolve_symlinks)
773    {
774       strlcpy(tmp, buf, sizeof(tmp));
775
776       /* NOTE: realpath() expects at least PATH_MAX_LENGTH bytes in buf.
777        * Technically, PATH_MAX_LENGTH needn't be defined, but we rely on it anyways.
778        * POSIX 2008 can automatically allocate for you,
779        * but don't rely on that. */
780       if (!realpath(tmp, buf))
781       {
782          strlcpy(buf, tmp, size);
783          return NULL;
784       }
785
786       return buf;
787    }
788
789    t       = 0; /* length of output */
790    buf_end = buf + strlen(buf);
791
792    if (!path_is_absolute(buf))
793    {
794       size_t len;
795       /* rebase on working directory */
796       if (!getcwd(tmp, PATH_MAX_LENGTH-1))
797          return NULL;
798
799       len = strlen(tmp);
800       t += len;
801
802       if (tmp[len-1] != '/')
803          tmp[t++] = '/';
804
805       if (string_is_empty(buf))
806          goto end;
807
808       p = buf;
809    }
810    else
811    {
812       /* UNIX paths can start with multiple '/', copy those */
813       for (p = buf; *p == '/'; p++)
814          tmp[t++] = '/';
815    }
816
817    /* p points to just after a slash while 'next' points to the next slash
818     * if there are no slashes, they point relative to where one would be */
819    do
820    {
821       if (!(next = strchr(p, '/')))
822          next = buf_end;
823
824       if ((next - p == 2 && p[0] == '.' && p[1] == '.'))
825       {
826          p += 3;
827
828          /* fail for illegal /.., //.. etc */
829          if (t == 1 || tmp[t-2] == '/')
830             return NULL;
831
832          /* delete previous segment in tmp by adjusting size t
833           * tmp[t-1] == '/', find '/' before that */
834          t = t-2;
835          while (tmp[t] != '/')
836             t--;
837          t++;
838       }
839       else if (next - p == 1 && p[0] == '.')
840          p += 2;
841       else if (next - p == 0)
842          p += 1;
843       else
844       {
845          /* fail when truncating */
846          if (t + next-p+1 > PATH_MAX_LENGTH-1)
847             return NULL;
848
849          while (p <= next)
850             tmp[t++] = *p++;
851       }
852    }while(next < buf_end);
853    
854
855 end:
856    tmp[t] = '\0';
857    strlcpy(buf, tmp, size);
858    return buf;
859 #endif
860 #endif
861    return NULL;
862 }
863
864 /**
865  * path_relative_to:
866  * @out                : buffer to write the relative path to
867  * @path               : path to be expressed relatively
868  * @base               : base directory to start out on
869  * @size               : size of output buffer
870  *
871  * Turns @path into a path relative to @base and writes it to @out.
872  *
873  * @base is assumed to be a base directory, i.e. a path ending with '/' or '\'.
874  * Both @path and @base are assumed to be absolute paths without "." or "..".
875  *
876  * E.g. path /a/b/e/f.cg with base /a/b/c/d/ turns into ../../e/f.cg
877  *
878  * @return Length of the string copied into @out
879  **/
880 size_t path_relative_to(char *out,
881       const char *path, const char *base, size_t size)
882 {
883    size_t i, j;
884    const char *trimmed_path, *trimmed_base;
885
886 #ifdef _WIN32
887    /* For different drives, return absolute path */
888    if (
889             path
890          && base
891          && path[0] != '\0' 
892          && path[1] != '\0'
893          && base[0] != '\0'
894          && base[1] != '\0'
895          && path[1] == ':' 
896          && base[1] == ':'
897          && path[0] != base[0])
898       return strlcpy(out, path, size);
899 #endif
900
901    /* Trim common beginning */
902    for (i = 0, j = 0; path[i] && base[i] && path[i] == base[i]; i++)
903       if (path[i] == PATH_DEFAULT_SLASH_C())
904          j = i + 1;
905
906    trimmed_path = path+j;
907    trimmed_base = base+i;
908
909    /* Each segment of base turns into ".." */
910    out[0] = '\0';
911    for (i = 0; trimmed_base[i]; i++)
912       if (trimmed_base[i] == PATH_DEFAULT_SLASH_C())
913          strlcat(out, ".." PATH_DEFAULT_SLASH(), size);
914
915    return strlcat(out, trimmed_path, size);
916 }
917
918 /**
919  * fill_pathname_resolve_relative:
920  * @out_path           : output path
921  * @in_refpath         : input reference path
922  * @in_path            : input path
923  * @size               : size of @out_path
924  *
925  * Joins basedir of @in_refpath together with @in_path.
926  * If @in_path is an absolute path, out_path = in_path.
927  * E.g.: in_refpath = "/foo/bar/baz.a", in_path = "foobar.cg",
928  * out_path = "/foo/bar/foobar.cg".
929  **/
930 void fill_pathname_resolve_relative(char *out_path,
931       const char *in_refpath, const char *in_path, size_t size)
932 {
933    if (path_is_absolute(in_path))
934    {
935       strlcpy(out_path, in_path, size);
936       return;
937    }
938
939    if (out_path != in_refpath)
940       strlcpy(out_path, in_refpath, size);
941    path_basedir(out_path);
942    strlcat(out_path, in_path, size);
943    path_resolve_realpath(out_path, size, false);
944 }
945
946 /**
947  * fill_pathname_join:
948  * @out_path           : output path
949  * @dir                : directory
950  * @path               : path
951  * @size               : size of output path
952  *
953  * Joins a directory (@dir) and path (@path) together.
954  * Makes sure not to get  two consecutive slashes
955  * between directory and path.
956  *
957  * Deprecated. Use fill_pathname_join_special() instead
958  * if you can ensure @dir and @out_path won't overlap.
959  *
960  * @return Length of the string copied into @out_path
961  **/
962 size_t fill_pathname_join(char *out_path,
963       const char *dir, const char *path, size_t size)
964 {
965    if (out_path != dir)
966       strlcpy(out_path, dir, size);
967    if (*out_path)
968       fill_pathname_slash(out_path, size);
969    return strlcat(out_path, path, size);
970 }
971
972 /**
973  * fill_pathname_join_special:
974  * @out_path           : output path
975  * @dir                : directory. Cannot be identical to @out_path
976  * @path               : path
977  * @size               : size of output path
978  *
979  *
980  * Specialized version of fill_pathname_join.
981  * Unlike fill_pathname_join(),
982  * @dir and @out_path CANNOT be identical.
983  *
984  * Joins a directory (@dir) and path (@path) together.
985  * Makes sure not to get  two consecutive slashes
986  * between directory and path.
987  *
988  * @return Length of the string copied into @out_path
989  **/
990 size_t fill_pathname_join_special(char *out_path,
991       const char *dir, const char *path, size_t size)
992 {
993    size_t len = strlcpy(out_path, dir, size);
994
995    if (*out_path)
996    {
997       const char *last_slash = find_last_slash(out_path);
998       if (last_slash)
999       {
1000          /* Try to preserve slash type. */
1001          if (last_slash != (out_path + len - 1))
1002          {
1003             out_path[len]   = last_slash[0];
1004             out_path[len+1] = '\0';
1005          }
1006       }
1007       else
1008       {
1009          out_path[len]      = PATH_DEFAULT_SLASH_C();
1010          out_path[len+1]    = '\0';
1011       }
1012    }
1013
1014    return strlcat(out_path, path, size);
1015 }
1016
1017 size_t fill_pathname_join_special_ext(char *out_path,
1018       const char *dir,  const char *path,
1019       const char *last, const char *ext,
1020       size_t size)
1021 {
1022    fill_pathname_join(out_path, dir, path, size);
1023    if (*out_path)
1024       fill_pathname_slash(out_path, size);
1025
1026    strlcat(out_path, last, size);
1027    return strlcat(out_path, ext, size);
1028 }
1029
1030 /**
1031  * fill_pathname_join_delim:
1032  * @out_path           : output path
1033  * @dir                : directory
1034  * @path               : path
1035  * @delim              : delimiter
1036  * @size               : size of output path
1037  *
1038  * Joins a directory (@dir) and path (@path) together
1039  * using the given delimiter (@delim).
1040  **/
1041 size_t fill_pathname_join_delim(char *out_path, const char *dir,
1042       const char *path, const char delim, size_t size)
1043 {
1044    size_t copied;
1045    /* behavior of strlcpy is undefined if dst and src overlap */
1046    if (out_path == dir)
1047       copied          = strlen(dir);
1048    else
1049       copied          = strlcpy(out_path, dir, size);
1050
1051    out_path[copied]   = delim;
1052    out_path[copied+1] = '\0';
1053
1054    if (path)
1055       return strlcat(out_path, path, size);
1056    return copied;
1057 }
1058
1059 size_t fill_pathname_expand_special(char *out_path,
1060       const char *in_path, size_t size)
1061 {
1062 #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
1063    char *app_dir = NULL;
1064    if (in_path[0] == '~')
1065    {
1066       app_dir    = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
1067       fill_pathname_home_dir(app_dir, PATH_MAX_LENGTH * sizeof(char));
1068    }
1069    else if (in_path[0] == ':')
1070    {
1071       app_dir    = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
1072       app_dir[0] = '\0';
1073       fill_pathname_application_dir(app_dir, PATH_MAX_LENGTH * sizeof(char));
1074    }
1075
1076    if (app_dir)
1077    {
1078       if (*app_dir)
1079       {
1080          size_t src_size = strlcpy(out_path, app_dir, size);
1081
1082          out_path       += src_size;
1083          size           -= src_size;
1084
1085          if (!PATH_CHAR_IS_SLASH(out_path[-1]))
1086          {
1087             src_size  = strlcpy(out_path, PATH_DEFAULT_SLASH(), size);
1088
1089             out_path += src_size;
1090             size     -= src_size;
1091          }
1092
1093          in_path += 2;
1094       }
1095
1096       free(app_dir);
1097    }
1098 #endif
1099    return strlcpy(out_path, in_path, size);
1100 }
1101
1102 size_t fill_pathname_abbreviate_special(char *out_path,
1103       const char *in_path, size_t size)
1104 {
1105 #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
1106    unsigned i;
1107    const char *candidates[3];
1108    const char *notations[3];
1109    char application_dir[PATH_MAX_LENGTH];
1110    char home_dir[PATH_MAX_LENGTH];
1111
1112    application_dir[0] = '\0';
1113
1114    /* application_dir could be zero-string. Safeguard against this.
1115     *
1116     * Keep application dir in front of home, moving app dir to a
1117     * new location inside home would break otherwise. */
1118
1119    /* ugly hack - use application_dir pointer
1120     * before filling it in. C89 reasons */
1121    candidates[0] = application_dir;
1122    candidates[1] = home_dir;
1123    candidates[2] = NULL;
1124
1125    notations [0] = ":";
1126    notations [1] = "~";
1127    notations [2] = NULL;
1128
1129    fill_pathname_application_dir(application_dir, sizeof(application_dir));
1130    fill_pathname_home_dir(home_dir, sizeof(home_dir));
1131
1132    for (i = 0; candidates[i]; i++)
1133    {
1134       if (!string_is_empty(candidates[i]) &&
1135           string_starts_with(in_path, candidates[i]))
1136       {
1137          size_t src_size  = strlcpy(out_path, notations[i], size);
1138
1139          out_path        += src_size;
1140          size            -= src_size;
1141          in_path         += strlen(candidates[i]);
1142
1143          if (!PATH_CHAR_IS_SLASH(*in_path))
1144          {
1145             strcpy_literal(out_path, PATH_DEFAULT_SLASH());
1146             out_path++;
1147             size--;
1148          }
1149
1150          break; /* Don't allow more abbrevs to take place. */
1151       }
1152    }
1153 #endif
1154    return strlcpy(out_path, in_path, size);
1155 }
1156
1157 /**
1158  * pathname_conform_slashes_to_os:
1159  *
1160  * @path               : path
1161  *
1162  * Leaf function.
1163  *
1164  * Changes the slashes to the correct kind for the os 
1165  * So forward slash on linux and backslash on Windows
1166  **/
1167 void pathname_conform_slashes_to_os(char *path)
1168 {
1169    /* Conform slashes to os standard so we get proper matching */
1170    char *p;
1171    for (p = path; *p; p++)
1172       if (*p == '/' || *p == '\\')
1173          *p = PATH_DEFAULT_SLASH_C();
1174 }
1175
1176 /**
1177  * pathname_make_slashes_portable:
1178  * @path               : path
1179  *
1180  * Leaf function.
1181  *
1182  * Change all slashes to forward so they are more 
1183  * portable between Windows and Linux
1184  **/
1185 void pathname_make_slashes_portable(char *path)
1186 {
1187    /* Conform slashes to os standard so we get proper matching */
1188    char *p;
1189    for (p = path; *p; p++)
1190       if (*p == '/' || *p == '\\')
1191          *p = '/';
1192 }
1193
1194 /**
1195  * get_pathname_num_slashes:
1196  * @in_path            : input path
1197  *
1198  * Leaf function.
1199  *
1200  * Get the number of slashes in a path.
1201  *
1202  * @return number of slashes found in @in_path.
1203  **/
1204 static int get_pathname_num_slashes(const char *in_path)
1205 {
1206    int num_slashes = 0;
1207    int i = 0;
1208
1209    for (i = 0; i < PATH_MAX_LENGTH; i++)
1210    {
1211       if (PATH_CHAR_IS_SLASH(in_path[i]))
1212          num_slashes++;
1213       if (in_path[i] == '\0')
1214          break;
1215    }
1216
1217    return num_slashes;
1218 }
1219
1220 /**
1221  * fill_pathname_abbreviated_or_relative:
1222  *
1223  * Fills the supplied path with either the abbreviated path or 
1224  * the relative path, which ever one has less depth / number of slashes
1225  * 
1226  * If lengths of abbreviated and relative paths are the same,
1227  * the relative path will be used
1228  * @in_path can be an absolute, relative or abbreviated path
1229  *
1230  * @return Length of the string copied into @out_path
1231  **/
1232 size_t fill_pathname_abbreviated_or_relative(char *out_path,
1233       const char *in_refpath, const char *in_path, size_t size)
1234 {
1235    char in_path_conformed[PATH_MAX_LENGTH];
1236    char in_refpath_conformed[PATH_MAX_LENGTH];
1237    char expanded_path[PATH_MAX_LENGTH];
1238    char absolute_path[PATH_MAX_LENGTH];
1239    char relative_path[PATH_MAX_LENGTH];
1240    char abbreviated_path[PATH_MAX_LENGTH];
1241    
1242    expanded_path[0]        = '\0';
1243    absolute_path[0]        = '\0';
1244    relative_path[0]        = '\0';
1245    abbreviated_path[0]     = '\0';
1246
1247    strlcpy(in_path_conformed, in_path, sizeof(in_path_conformed));
1248    strlcpy(in_refpath_conformed, in_refpath, sizeof(in_refpath_conformed));
1249
1250    pathname_conform_slashes_to_os(in_path_conformed);
1251    pathname_conform_slashes_to_os(in_refpath_conformed);
1252
1253    /* Expand paths which start with :\ to an absolute path */
1254    fill_pathname_expand_special(expanded_path,
1255          in_path_conformed, sizeof(expanded_path));
1256
1257    /* Get the absolute path if it is not already */
1258    if (path_is_absolute(expanded_path))
1259       strlcpy(absolute_path, expanded_path, PATH_MAX_LENGTH);
1260    else
1261       fill_pathname_resolve_relative(absolute_path,
1262             in_refpath_conformed, in_path_conformed, PATH_MAX_LENGTH);
1263
1264    pathname_conform_slashes_to_os(absolute_path);
1265
1266    /* Get the relative path and see how many directories long it is */
1267    path_relative_to(relative_path, absolute_path,
1268          in_refpath_conformed, sizeof(relative_path));
1269
1270    /* Get the abbreviated path and see how many directories long it is */
1271    fill_pathname_abbreviate_special(abbreviated_path,
1272          absolute_path, sizeof(abbreviated_path));
1273
1274    /* Use the shortest path, preferring the relative path*/
1275    if (  get_pathname_num_slashes(relative_path) <= 
1276          get_pathname_num_slashes(abbreviated_path))
1277       return strlcpy(out_path, relative_path, size);
1278    return strlcpy(out_path, abbreviated_path, size);
1279 }
1280
1281 /**
1282  * path_basedir:
1283  * @path               : path
1284  *
1285  * Extracts base directory by mutating path.
1286  * Keeps trailing '/'.
1287  **/
1288 void path_basedir_wrapper(char *path)
1289 {
1290    char *last = NULL;
1291    if (!path || path[0] == '\0' || path[1] == '\0')
1292       return;
1293
1294 #ifdef HAVE_COMPRESSION
1295    /* We want to find the directory with the archive in basedir. */
1296    if ((last = (char*)path_get_archive_delim(path)))
1297       *last = '\0';
1298 #endif
1299
1300    if ((last = find_last_slash(path)))
1301       last[1] = '\0';
1302    else
1303    {
1304       path[0] = '.';
1305       path[1] = PATH_DEFAULT_SLASH_C();
1306       path[2] = '\0';
1307    }
1308 }
1309
1310 #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
1311 void fill_pathname_application_path(char *s, size_t len)
1312 {
1313    size_t i;
1314 #ifdef __APPLE__
1315   CFBundleRef bundle = CFBundleGetMainBundle();
1316 #endif
1317 #ifdef _WIN32
1318    DWORD ret = 0;
1319    wchar_t wstr[PATH_MAX_LENGTH] = {0};
1320 #endif
1321 #ifdef __HAIKU__
1322    image_info info;
1323    int32_t cookie = 0;
1324 #endif
1325    (void)i;
1326
1327    if (!len)
1328       return;
1329
1330 #if defined(_WIN32)
1331 #ifdef LEGACY_WIN32
1332    ret    = GetModuleFileNameA(NULL, s, len);
1333 #else
1334    ret    = GetModuleFileNameW(NULL, wstr, ARRAY_SIZE(wstr));
1335
1336    if (*wstr)
1337    {
1338       char *str = utf16_to_utf8_string_alloc(wstr);
1339
1340       if (str)
1341       {
1342          strlcpy(s, str, len);
1343          free(str);
1344       }
1345    }
1346 #endif
1347    s[ret] = '\0';
1348 #elif defined(__APPLE__)
1349    if (bundle)
1350    {
1351       CFURLRef bundle_url     = CFBundleCopyBundleURL(bundle);
1352       CFStringRef bundle_path = CFURLCopyPath(bundle_url);
1353       CFStringGetCString(bundle_path, s, len, kCFStringEncodingUTF8);
1354 #ifdef HAVE_COCOATOUCH
1355       {
1356          /* This needs to be done so that the path becomes 
1357           * /private/var/... and this
1358           * is used consistently throughout for the iOS bundle path */
1359          char resolved_bundle_dir_buf[PATH_MAX_LENGTH] = {0};
1360          if (realpath(s, resolved_bundle_dir_buf))
1361          {
1362             size_t _len = strlcpy(s, resolved_bundle_dir_buf, len - 1);
1363             s[_len  ]   = '/';
1364             s[_len+1]   = '\0';
1365          }
1366       }
1367 #endif
1368
1369       CFRelease(bundle_path);
1370       CFRelease(bundle_url);
1371 #ifndef HAVE_COCOATOUCH
1372       /* Not sure what this does but it breaks 
1373        * stuff for iOS, so skipping */
1374       strlcat(s, "nobin", len);
1375 #endif
1376       return;
1377    }
1378 #elif defined(__HAIKU__)
1379    while (get_next_image_info(0, &cookie, &info) == B_OK)
1380    {
1381       if (info.type == B_APP_IMAGE)
1382       {
1383          strlcpy(s, info.name, len);
1384          return;
1385       }
1386    }
1387 #elif defined(__QNX__)
1388    char *buff = malloc(len);
1389
1390    if (_cmdname(buff))
1391       strlcpy(s, buff, len);
1392
1393    free(buff);
1394 #else
1395    {
1396       pid_t pid;
1397       static const char *exts[] = { "exe", "file", "path/a.out" };
1398       char link_path[255];
1399
1400       link_path[0] = *s = '\0';
1401       pid       = getpid();
1402
1403       /* Linux, BSD and Solaris paths. Not standardized. */
1404       for (i = 0; i < ARRAY_SIZE(exts); i++)
1405       {
1406          ssize_t ret;
1407
1408          snprintf(link_path, sizeof(link_path), "/proc/%u/%s",
1409                (unsigned)pid, exts[i]);
1410
1411          if ((ret = readlink(link_path, s, len - 1)) >= 0)
1412          {
1413             s[ret] = '\0';
1414             return;
1415          }
1416       }
1417    }
1418 #endif
1419 }
1420
1421 void fill_pathname_application_dir(char *s, size_t len)
1422 {
1423 #ifdef __WINRT__
1424    strlcpy(s, uwp_dir_install, len);
1425 #else
1426    fill_pathname_application_path(s, len);
1427    path_basedir_wrapper(s);
1428 #endif
1429 }
1430
1431 void fill_pathname_home_dir(char *s, size_t len)
1432 {
1433 #ifdef __WINRT__
1434    const char *home = uwp_dir_data;
1435 #else
1436    const char *home = getenv("HOME");
1437 #endif
1438    if (home)
1439       strlcpy(s, home, len);
1440    else
1441       *s = 0;
1442 }
1443 #endif
1444
1445 bool is_path_accessible_using_standard_io(const char *path)
1446 {
1447 #ifdef __WINRT__
1448    return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES;
1449 #else
1450    return true;
1451 #endif
1452 }