Atlas - SDL_sysfsops.c
Home / ext / SDL / src / filesystem / posix Lines: 1 | Size: 12384 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22#include "SDL_internal.h" 23 24#if defined(SDL_FSOPS_POSIX) 25 26/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 27// System dependent filesystem routines 28 29#include "../SDL_sysfilesystem.h" 30 31#include <stdio.h> 32#include <string.h> 33#include <errno.h> 34#include <dirent.h> 35#include <sys/stat.h> 36#include <unistd.h> 37 38#ifdef SDL_PLATFORM_ANDROID 39#include "../../core/android/SDL_android.h" 40#endif 41 42bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata) 43{ 44#ifdef SDL_PLATFORM_ANDROID 45 if (*path != '/') { 46 char *apath = NULL; 47 SDL_asprintf(&apath, "%s/%s", SDL_GetAndroidInternalStoragePath(), path); 48 if (!apath) { 49 return false; 50 } 51 const bool retval = SDL_SYS_EnumerateDirectory(apath, cb, userdata); 52 SDL_free(apath); 53 if (retval) { 54 return true; 55 } 56 } 57#endif 58 59#ifdef SDL_PLATFORM_IOS 60 if (*path != '/') { 61 char *base = SDL_GetPrefPath("", ""); 62 if (!base) { 63 return false; 64 } 65 66 char *apath = NULL; 67 SDL_asprintf(&apath, "%s%s", base, path); 68 SDL_free(base); 69 if (!apath) { 70 return false; 71 } 72 const bool retval = SDL_SYS_EnumerateDirectory(apath, cb, userdata); 73 SDL_free(apath); 74 if (retval) { 75 return true; 76 } 77 } 78#endif 79 80 char *pathwithsep = NULL; 81 int pathwithseplen = SDL_asprintf(&pathwithsep, "%s/", path); 82 if ((pathwithseplen == -1) || (!pathwithsep)) { 83 return false; 84 } 85 86 // trim down to a single path separator at the end, in case the caller added one or more. 87 pathwithseplen--; 88 while ((pathwithseplen > 0) && (pathwithsep[pathwithseplen] == '/')) { 89 pathwithsep[pathwithseplen--] = '\0'; 90 } 91 92 DIR *dir = opendir(pathwithsep); 93 if (!dir) { 94 #ifdef SDL_PLATFORM_ANDROID // Maybe it's an asset...? 95 const bool retval = Android_JNI_EnumerateAssetDirectory(pathwithsep, cb, userdata); 96 SDL_free(pathwithsep); 97 return retval; 98 #else 99 SDL_free(pathwithsep); 100 return SDL_SetError("Can't open directory: %s", strerror(errno)); 101 #endif 102 } 103 104 // make sure there's a path separator at the end now for the actual callback. 105 pathwithsep[++pathwithseplen] = '/'; 106 pathwithsep[++pathwithseplen] = '\0'; 107 108 SDL_EnumerationResult result = SDL_ENUM_CONTINUE; 109 struct dirent *ent; 110 while ((result == SDL_ENUM_CONTINUE) && ((ent = readdir(dir)) != NULL)) { 111 const char *name = ent->d_name; 112 if ((SDL_strcmp(name, ".") == 0) || (SDL_strcmp(name, "..") == 0)) { 113 continue; 114 } 115 result = cb(userdata, pathwithsep, name); 116 } 117 118 closedir(dir); 119 120 SDL_free(pathwithsep); 121 122 return (result != SDL_ENUM_FAILURE); 123} 124 125bool SDL_SYS_RemovePath(const char *path) 126{ 127 int rc; 128 129#ifdef SDL_PLATFORM_ANDROID 130 if (*path == '/') { 131 rc = remove(path); 132 } else { 133 char *apath = NULL; 134 SDL_asprintf(&apath, "%s/%s", SDL_GetAndroidInternalStoragePath(), path); 135 if (!apath) { 136 return false; 137 } 138 rc = remove(apath); 139 SDL_free(apath); 140 } 141#elif defined(SDL_PLATFORM_IOS) 142 if (*path == '/') { 143 rc = remove(path); 144 } else { 145 char *base = SDL_GetPrefPath("", ""); 146 if (!base) { 147 return false; 148 } 149 150 char *apath = NULL; 151 SDL_asprintf(&apath, "%s%s", base, path); 152 SDL_free(base); 153 if (!apath) { 154 return false; 155 } 156 rc = remove(apath); 157 SDL_free(apath); 158 } 159#else 160 rc = remove(path); 161#endif 162 if (rc < 0) { 163 if (errno == ENOENT) { 164 // It's already gone, this is a success 165 return true; 166 } 167 return SDL_SetError("Can't remove path: %s", strerror(errno)); 168 } 169 return true; 170} 171 172bool SDL_SYS_RenamePath(const char *oldpath, const char *newpath) 173{ 174 int rc; 175 176#ifdef SDL_PLATFORM_ANDROID 177 char *aoldpath = NULL; 178 char *anewpath = NULL; 179 if (*oldpath != '/') { 180 SDL_asprintf(&aoldpath, "%s/%s", SDL_GetAndroidInternalStoragePath(), oldpath); 181 if (!aoldpath) { 182 return false; 183 } 184 oldpath = aoldpath; 185 } 186 if (*newpath != '/') { 187 SDL_asprintf(&anewpath, "%s/%s", SDL_GetAndroidInternalStoragePath(), newpath); 188 if (!anewpath) { 189 SDL_free(aoldpath); 190 return false; 191 } 192 newpath = anewpath; 193 } 194 rc = rename(oldpath, newpath); 195 SDL_free(aoldpath); 196 SDL_free(anewpath); 197#elif defined(SDL_PLATFORM_IOS) 198 char *base = NULL; 199 if (*oldpath != '/' || *newpath != '/') { 200 base = SDL_GetPrefPath("", ""); 201 if (!base) { 202 return false; 203 } 204 } 205 206 char *aoldpath = NULL; 207 char *anewpath = NULL; 208 if (*oldpath != '/') { 209 SDL_asprintf(&aoldpath, "%s%s", base, oldpath); 210 if (!aoldpath) { 211 SDL_free(base); 212 return false; 213 } 214 oldpath = aoldpath; 215 } 216 if (*newpath != '/') { 217 SDL_asprintf(&anewpath, "%s%s", base, newpath); 218 if (!anewpath) { 219 SDL_free(base); 220 SDL_free(aoldpath); 221 return false; 222 } 223 newpath = anewpath; 224 } 225 rc = rename(oldpath, newpath); 226 SDL_free(base); 227 SDL_free(aoldpath); 228 SDL_free(anewpath); 229#else 230 rc = rename(oldpath, newpath); 231#endif 232 if (rc < 0) { 233 return SDL_SetError("Can't rename path: %s", strerror(errno)); 234 } 235 return true; 236} 237 238bool SDL_SYS_CopyFile(const char *oldpath, const char *newpath) 239{ 240 char *buffer = NULL; 241 SDL_IOStream *input = NULL; 242 SDL_IOStream *output = NULL; 243 const size_t maxlen = 4096; 244 size_t len; 245 bool result = false; 246 247 input = SDL_IOFromFile(oldpath, "rb"); 248 if (!input) { 249 goto done; 250 } 251 252 output = SDL_IOFromFile(newpath, "wb"); 253 if (!output) { 254 goto done; 255 } 256 257 buffer = (char *)SDL_malloc(maxlen); 258 if (!buffer) { 259 goto done; 260 } 261 262 while ((len = SDL_ReadIO(input, buffer, maxlen)) > 0) { 263 if (SDL_WriteIO(output, buffer, len) < len) { 264 goto done; 265 } 266 } 267 if (SDL_GetIOStatus(input) != SDL_IO_STATUS_EOF) { 268 goto done; 269 } 270 271 SDL_CloseIO(input); 272 input = NULL; 273 274 if (!SDL_FlushIO(output)) { 275 goto done; 276 } 277 278 result = SDL_CloseIO(output); 279 output = NULL; // it's gone, even if it failed. 280 281done: 282 if (output) { 283 SDL_CloseIO(output); 284 } 285 if (input) { 286 SDL_CloseIO(input); 287 } 288 SDL_free(buffer); 289 290 return result; 291} 292 293bool SDL_SYS_CreateDirectory(const char *path) 294{ 295 int rc; 296 297#ifdef SDL_PLATFORM_ANDROID 298 if (*path == '/') { 299 rc = mkdir(path, 0770); 300 } else { 301 char *apath = NULL; 302 SDL_asprintf(&apath, "%s/%s", SDL_GetAndroidInternalStoragePath(), path); 303 if (!apath) { 304 return false; 305 } 306 rc = mkdir(apath, 0770); 307 SDL_free(apath); 308 } 309#elif defined(SDL_PLATFORM_IOS) 310 if (*path == '/') { 311 rc = mkdir(path, 0770); 312 } else { 313 char *base = SDL_GetPrefPath("", ""); 314 if (!base) { 315 return false; 316 } 317 318 char *apath = NULL; 319 SDL_asprintf(&apath, "%s%s", base, path); 320 SDL_free(base); 321 if (!apath) { 322 return false; 323 } 324 rc = mkdir(apath, 0770); 325 SDL_free(apath); 326 } 327#else 328 rc = mkdir(path, 0770); 329#endif 330 if (rc < 0) { 331 const int origerrno = errno; 332 if (origerrno == EEXIST) { 333 struct stat statbuf; 334 if ((stat(path, &statbuf) == 0) && (S_ISDIR(statbuf.st_mode))) { 335 return true; // it already exists and it's a directory, consider it success. 336 } 337 } 338 return SDL_SetError("Can't create directory: %s", strerror(origerrno)); 339 } 340 return true; 341} 342 343bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info) 344{ 345 struct stat statbuf; 346 int rc; 347 348#ifdef SDL_PLATFORM_ANDROID 349 if (*path == '/') { 350 rc = stat(path, &statbuf); 351 } else { 352 char *apath = NULL; 353 SDL_asprintf(&apath, "%s/%s", SDL_GetAndroidInternalStoragePath(), path); 354 if (!apath) { 355 return false; 356 } 357 rc = stat(apath, &statbuf); 358 SDL_free(apath); 359 } 360 if (rc < 0) { 361 return Android_JNI_GetAssetPathInfo(path, info); 362 } 363#elif defined(SDL_PLATFORM_IOS) 364 if (*path == '/') { 365 rc = stat(path, &statbuf); 366 } else { 367 char *base = SDL_GetPrefPath("", ""); 368 if (!base) { 369 return false; 370 } 371 372 char *apath = NULL; 373 SDL_asprintf(&apath, "%s%s", base, path); 374 SDL_free(base); 375 if (!apath) { 376 return false; 377 } 378 rc = stat(apath, &statbuf); 379 SDL_free(apath); 380 } 381#else 382 rc = stat(path, &statbuf); 383#endif 384 if (rc < 0) { 385 return SDL_SetError("Can't stat: %s", strerror(errno)); 386 } else if (S_ISREG(statbuf.st_mode)) { 387 info->type = SDL_PATHTYPE_FILE; 388 info->size = (Uint64) statbuf.st_size; 389 } else if (S_ISDIR(statbuf.st_mode)) { 390 info->type = SDL_PATHTYPE_DIRECTORY; 391 info->size = 0; 392 } else { 393 info->type = SDL_PATHTYPE_OTHER; 394 info->size = (Uint64) statbuf.st_size; 395 } 396 397#if defined(HAVE_ST_MTIM) 398 // POSIX.1-2008 standard 399 info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctim.tv_sec) + statbuf.st_ctim.tv_nsec; 400 info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtim.tv_sec) + statbuf.st_mtim.tv_nsec; 401 info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atim.tv_sec) + statbuf.st_atim.tv_nsec; 402#elif defined(SDL_PLATFORM_APPLE) 403 /* Apple platform stat structs use 'st_*timespec' naming. */ 404 info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctimespec.tv_sec) + statbuf.st_ctimespec.tv_nsec; 405 info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtimespec.tv_sec) + statbuf.st_mtimespec.tv_nsec; 406 info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atimespec.tv_sec) + statbuf.st_atimespec.tv_nsec; 407#else 408 info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctime); 409 info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtime); 410 info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atime); 411#endif 412 return true; 413} 414 415// Note that this is actually part of filesystem, not fsops, but everything that uses posix fsops uses this implementation, even with separate filesystem code. 416char *SDL_SYS_GetCurrentDirectory(void) 417{ 418 size_t buflen = 64; 419 char *buf = NULL; 420 421 while (true) { 422 void *ptr = SDL_realloc(buf, buflen); 423 if (!ptr) { 424 SDL_free(buf); 425 return NULL; 426 } 427 buf = (char *) ptr; 428 429 if (getcwd(buf, buflen-1) != NULL) { 430 break; // we got it! 431 } 432 433 if (errno == ERANGE) { 434 buflen *= 2; // try again with a bigger buffer. 435 continue; 436 } 437 438 SDL_free(buf); 439 SDL_SetError("getcwd failed: %s", strerror(errno)); 440 return NULL; 441 } 442 443 // make sure there's a path separator at the end. 444 SDL_assert(SDL_strlen(buf) < (buflen + 2)); 445 buflen = SDL_strlen(buf); 446 if ((buflen == 0) || (buf[buflen-1] != '/')) { 447 buf[buflen] = '/'; 448 buf[buflen + 1] = '\0'; 449 } 450 451 return buf; 452} 453 454#endif // SDL_FSOPS_POSIX 455 456[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.