1 /* Copyright  (C) 2010-2017 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (retro_dirent.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 <stdlib.h>
24 #include <stdio.h>
25 
26 #include <retro_common.h>
27 
28 #include <boolean.h>
29 #include <retro_dirent.h>
30 
31 #if defined(_WIN32)
32 #  ifdef _MSC_VER
33 #    define setmode _setmode
34 #  endif
35 #include <sys/stat.h>
36 #  ifdef _XBOX
37 #    include <xtl.h>
38 #    define INVALID_FILE_ATTRIBUTES -1
39 #  else
40 #    include <io.h>
41 #    include <fcntl.h>
42 #    include <direct.h>
43 #    include <windows.h>
44 #  endif
45 #elif defined(VITA)
46 #  include <psp2/io/fcntl.h>
47 #  include <psp2/io/dirent.h>
48 #include <psp2/io/stat.h>
49 #else
50 #  if defined(PSP)
51 #    include <pspiofilemgr.h>
52 #  endif
53 #  include <sys/types.h>
54 #  include <sys/stat.h>
55 #  include <dirent.h>
56 #  include <unistd.h>
57 #endif
58 
59 #ifdef __CELLOS_LV2__
60 #include <cell/cell_fs.h>
61 #endif
62 
63 #if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
64 #include <unistd.h> /* stat() is defined here */
65 #endif
66 
67 struct RDIR
68 {
69 #if defined(_WIN32)
70    WIN32_FIND_DATA entry;
71    HANDLE directory;
72    bool next;
73    char path[PATH_MAX_LENGTH];
74 #elif defined(VITA) || defined(PSP)
75    SceUID directory;
76    SceIoDirent entry;
77 #elif defined(__CELLOS_LV2__)
78    CellFsErrno error;
79    int directory;
80    CellFsDirent entry;
81 #else
82    DIR *directory;
83    const struct dirent *entry;
84 #endif
85 };
86 
retro_opendir(const char * name)87 struct RDIR *retro_opendir(const char *name)
88 {
89 #if defined(_WIN32)
90    char path_buf[1024];
91 #endif
92    struct RDIR *rdir = (struct RDIR*)calloc(1, sizeof(*rdir));
93 
94    if (!rdir)
95       return NULL;
96 
97 #if defined(_WIN32)
98    path_buf[0] = '\0';
99    snprintf(path_buf, sizeof(path_buf), "%s\\*", name);
100    rdir->directory = FindFirstFile(path_buf, &rdir->entry);
101 #elif defined(VITA) || defined(PSP)
102    rdir->directory = sceIoDopen(name);
103 #elif defined(_3DS)
104    rdir->directory = (name && *name)? opendir(name) : NULL;
105    rdir->entry     = NULL;
106 #elif defined(__CELLOS_LV2__)
107    rdir->error = cellFsOpendir(name, &rdir->directory);
108 #else
109    rdir->directory = opendir(name);
110    rdir->entry     = NULL;
111 #endif
112 
113    return rdir;
114 }
115 
retro_dirent_error(struct RDIR * rdir)116 bool retro_dirent_error(struct RDIR *rdir)
117 {
118 #if defined(_WIN32)
119    return (rdir->directory == INVALID_HANDLE_VALUE);
120 #elif defined(VITA) || defined(PSP)
121    return (rdir->directory < 0);
122 #elif defined(__CELLOS_LV2__)
123    return (rdir->error != CELL_FS_SUCCEEDED);
124 #else
125    return !(rdir->directory);
126 #endif
127 }
128 
retro_readdir(struct RDIR * rdir)129 int retro_readdir(struct RDIR *rdir)
130 {
131 #if defined(_WIN32)
132    if(rdir->next)
133       return (FindNextFile(rdir->directory, &rdir->entry) != 0);
134 
135    rdir->next = true;
136    return (rdir->directory != INVALID_HANDLE_VALUE);
137 #elif defined(VITA) || defined(PSP)
138    return (sceIoDread(rdir->directory, &rdir->entry) > 0);
139 #elif defined(__CELLOS_LV2__)
140    uint64_t nread;
141    rdir->error = cellFsReaddir(rdir->directory, &rdir->entry, &nread);
142    return (nread != 0);
143 #else
144    return ((rdir->entry = readdir(rdir->directory)) != NULL);
145 #endif
146 }
147 
retro_dirent_get_name(struct RDIR * rdir)148 const char *retro_dirent_get_name(struct RDIR *rdir)
149 {
150 #if defined(_WIN32)
151    return rdir->entry.cFileName;
152 #elif defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__)
153    return rdir->entry.d_name;
154 #else
155    return rdir->entry->d_name;
156 #endif
157 }
158 
path_is_directory_internal(const char * path)159 static bool path_is_directory_internal(const char *path)
160 {
161 #if defined(VITA) || defined(PSP)
162    SceIoStat buf;
163    char *tmp  = strdup(path);
164    size_t len = strlen(tmp);
165    if (tmp[len-1] == '/')
166       tmp[len-1]='\0';
167 
168    if (sceIoGetstat(tmp, &buf) < 0)
169    {
170       free(tmp);
171       return false;
172    }
173    free(tmp);
174 
175    return FIO_S_ISDIR(buf.st_mode);
176 #elif defined(__CELLOS_LV2__)
177    CellFsStat buf;
178    if (cellFsStat(path, &buf) < 0)
179       return false;
180    return ((buf.st_mode & S_IFMT) == S_IFDIR);
181 #elif defined(_WIN32)
182    struct _stat buf;
183    DWORD file_info = GetFileAttributes(path);
184 
185    _stat(path, &buf);
186 
187    if (file_info == INVALID_FILE_ATTRIBUTES)
188       return false;
189    return (file_info & FILE_ATTRIBUTE_DIRECTORY);
190 #else
191    struct stat buf;
192    if (stat(path, &buf) < 0)
193       return false;
194    return S_ISDIR(buf.st_mode);
195 #endif
196 }
197 
198 /**
199  *
200  * retro_dirent_is_dir:
201  * @rdir         : pointer to the directory entry.
202  * @path         : path to the directory entry.
203  *
204  * Is the directory listing entry a directory?
205  *
206  * Returns: true if directory listing entry is
207  * a directory, false if not.
208  */
retro_dirent_is_dir(struct RDIR * rdir,const char * path)209 bool retro_dirent_is_dir(struct RDIR *rdir, const char *path)
210 {
211 #if defined(_WIN32)
212    const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry;
213    return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
214 #elif defined(PSP) || defined(VITA)
215    const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry;
216 #if defined(PSP)
217    return (entry->d_stat.st_attr & FIO_SO_IFDIR) == FIO_SO_IFDIR;
218 #elif defined(VITA)
219    return SCE_S_ISDIR(entry->d_stat.st_mode);
220 #endif
221 #elif defined(__CELLOS_LV2__)
222    CellFsDirent *entry = (CellFsDirent*)&rdir->entry;
223    return (entry->d_type == CELL_FS_TYPE_DIRECTORY);
224 #elif defined(DT_DIR)
225    const struct dirent *entry = (const struct dirent*)rdir->entry;
226    if (entry->d_type == DT_DIR)
227       return true;
228    /* This can happen on certain file systems. */
229    if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK)
230       return path_is_directory_internal(path);
231    return false;
232 #else
233    /* dirent struct doesn't have d_type, do it the slow way ... */
234    return path_is_directory_internal(path);
235 #endif
236 }
237 
retro_dirent_include_hidden(struct RDIR * rdir,bool include_hidden)238 void retro_dirent_include_hidden(struct RDIR *rdir, bool include_hidden)
239 {
240 #ifdef _WIN32
241    if (include_hidden)
242       rdir->entry.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
243    else
244       rdir->entry.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
245 #endif
246 }
247 
retro_closedir(struct RDIR * rdir)248 void retro_closedir(struct RDIR *rdir)
249 {
250    if (!rdir)
251       return;
252 
253 #if defined(_WIN32)
254    if (rdir->directory != INVALID_HANDLE_VALUE)
255       FindClose(rdir->directory);
256 #elif defined(VITA) || defined(PSP)
257    sceIoDclose(rdir->directory);
258 #elif defined(__CELLOS_LV2__)
259    rdir->error = cellFsClosedir(rdir->directory);
260 #else
261    if (rdir->directory)
262       closedir(rdir->directory);
263 #endif
264 
265    free(rdir);
266 }
267