1 /* vim:expandtab:ts=2 sw=2:
2 */
3 #include <stdlib.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <string.h>
7 #ifndef _MSC_VER
8 #include <unistd.h>
9 #endif
10 #if !defined(WIN32)
11 #include <limits.h>
12 #endif
13 
14 #if defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED < 1060)
15 #include "gfx2mem.h"
16 #endif
17 
18 #if defined(__AROS__) || defined(__BEOS__) || defined(__MORPHOS__) || defined(__GP2X__) || defined(__WIZ__) || defined(__CAANOO__) || defined(__amigaos__) || defined(__SWITCH__)
19 
20 #include "io.h"
21 #include "gfx2log.h"
22 #include "gfx2mem.h"
23 // These platforms don't have realpath().
24 // Our custom implementation uses chdir() and relies on
25 // errno == ENOTDIR when trying to change directory to a file
Realpath(const char * _path)26 char *Realpath(const char *_path)
27 {
28   char * rpath = NULL;
29   char * current_dir_save;
30 
31   // backup current directory
32   current_dir_save = Get_current_directory(NULL, NULL, 0);
33   if (current_dir_save == NULL) {
34     // error
35     return NULL;
36   }
37 
38   if (chdir(_path) < 0) {
39     if (errno == ENOTDIR) {
40       const char * position;
41       const char * filename;
42       char * directory;
43       position = Find_last_separator(_path);
44       if (position != NULL) {
45         size_t dirlen = position - _path;
46         filename = position + 1;
47         directory = GFX2_malloc(dirlen);
48         if (directory != NULL) {
49           memcpy(directory, _path, dirlen);
50           directory[dirlen] = '\0';
51           GFX2_Log(GFX2_DEBUG, "Directory : \"%s\", filename : \"%s\"\n", directory, filename);
52           if (chdir(directory) == 0) {
53             char * dirpart = Get_current_directory(NULL, NULL, 0);
54             if (dirpart != NULL) {
55               size_t len = strlen(dirpart) + strlen(filename) + 2;
56               rpath = GFX2_malloc(len);
57               if (rpath != NULL) {
58                 snprintf(rpath, len, "%s%s%s", dirpart, PATH_SEPARATOR, filename);
59               }
60               free(dirpart);
61             }
62           } else {
63             GFX2_Log(GFX2_ERROR, "chdir(\"%s\") : %s\n", directory, strerror(errno));
64           }
65           free(directory);
66         }
67       }
68     } else {
69       GFX2_Log(GFX2_ERROR, "chdir(\"%s\") : %s\n", _path, strerror(errno));
70     }
71   } else {
72     // _path is a directory
73     rpath = Get_current_directory(NULL, NULL, 0);
74   }
75 
76   // "restore" current directory
77   chdir(current_dir_save);
78   free(current_dir_save);
79   return rpath;
80 }
81 
82 #elif defined(__WIN32__) || defined(WIN32)
83 // MSVC / Mingw has a working equivalent. It only has reversed arguments.
Realpath(const char * _path)84 char *Realpath(const char *_path)
85 {
86   return _fullpath(NULL, _path, 260);
87 }
88 #else
89 
90 // Use the stdlib function.
Realpath(const char * _path)91     char *Realpath(const char *_path)
92     {
93       /// POSIX 2004 states :
94       /// If resolved_name is a null pointer, the behavior of realpath()
95       /// is implementation-defined.
96       ///
97       /// but POSIX 2008 :
98       /// If resolved_name is a null pointer, the generated pathname shall
99       /// be stored as a null-terminated string in a buffer allocated as if
100       /// by a call to malloc().
101       ///
102       /// So we assume all platforms now support passing NULL.
103       /// If you find a platform where this is not the case,
104       /// please add a new implementation with ifdef's.
105       char * resolved_path = NULL;
106 #if defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED < 1060)
107       // realpath() accept NULL as 2nd argument since OSX 10.6
108       resolved_path = GFX2_malloc(PATH_MAX);
109       if (resolved_path == NULL)
110         return NULL;
111 #endif
112       return realpath(_path, resolved_path);
113     }
114 #endif
115