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