Atlas - SDL_sysfilesystem.c

Home / ext / SDL / src / filesystem / unix Lines: 2 | Size: 16300 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#include "SDL_internal.h" 22 23#ifdef SDL_FILESYSTEM_UNIX 24 25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 26// System dependent filesystem routines 27 28#include "../SDL_sysfilesystem.h" 29 30#include <stdio.h> 31#include <sys/stat.h> 32#include <sys/types.h> 33#include <dirent.h> 34#include <errno.h> 35#include <fcntl.h> 36#include <limits.h> 37#include <stdlib.h> 38#include <string.h> 39#include <unistd.h> 40 41#if defined(SDL_PLATFORM_FREEBSD) || defined(SDL_PLATFORM_OPENBSD) 42#include <sys/sysctl.h> 43#endif 44 45static char *readSymLink(const char *path) 46{ 47 char *result = NULL; 48 ssize_t len = 64; 49 ssize_t rc = -1; 50 51 while (1) { 52 char *ptr = (char *)SDL_realloc(result, (size_t)len); 53 if (!ptr) { 54 break; 55 } 56 57 result = ptr; 58 59 rc = readlink(path, result, len); 60 if (rc == -1) { 61 break; // not a symlink, i/o error, etc. 62 } else if (rc < len) { 63 result[rc] = '\0'; // readlink doesn't null-terminate. 64 return result; // we're good to go. 65 } 66 67 len *= 2; // grow buffer, try again. 68 } 69 70 SDL_free(result); 71 return NULL; 72} 73 74#ifdef SDL_PLATFORM_OPENBSD 75static char *search_path_for_binary(const char *bin) 76{ 77 const char *envr_real = SDL_getenv("PATH"); 78 char *envr; 79 size_t alloc_size; 80 char *exe = NULL; 81 char *start; 82 char *ptr; 83 84 if (!envr_real) { 85 SDL_SetError("No $PATH set"); 86 return NULL; 87 } 88 89 start = envr = SDL_strdup(envr_real); 90 if (!envr) { 91 return NULL; 92 } 93 94 SDL_assert(bin != NULL); 95 96 alloc_size = SDL_strlen(bin) + SDL_strlen(envr) + 2; 97 exe = (char *)SDL_malloc(alloc_size); 98 99 do { 100 ptr = SDL_strchr(start, ':'); // find next $PATH separator. 101 if (ptr != start) { 102 if (ptr) { 103 *ptr = '\0'; 104 } 105 106 // build full binary path... 107 SDL_snprintf(exe, alloc_size, "%s%s%s", start, (ptr && (ptr[-1] == '/')) ? "" : "/", bin); 108 109 if (access(exe, X_OK) == 0) { // Exists as executable? We're done. 110 SDL_free(envr); 111 return exe; 112 } 113 } 114 start = ptr + 1; // start points to beginning of next element. 115 } while (ptr); 116 117 SDL_free(envr); 118 SDL_free(exe); 119 120 SDL_SetError("Process not found in $PATH"); 121 return NULL; // doesn't exist in path. 122} 123#endif 124 125char *SDL_SYS_GetBasePath(void) 126{ 127 char *result = NULL; 128 129#ifdef SDL_PLATFORM_FREEBSD 130 char fullpath[PATH_MAX]; 131 size_t buflen = sizeof(fullpath); 132 const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; 133 if (sysctl(mib, SDL_arraysize(mib), fullpath, &buflen, NULL, 0) != -1) { 134 result = SDL_strdup(fullpath); 135 if (!result) { 136 return NULL; 137 } 138 } 139#endif 140#ifdef SDL_PLATFORM_OPENBSD 141 // Please note that this will fail if the process was launched with a relative path and $PWD + the cwd have changed, or argv is altered. So don't do that. Or add a new sysctl to OpenBSD. 142 char **cmdline; 143 size_t len; 144 const int mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV }; 145 if (sysctl(mib, 4, NULL, &len, NULL, 0) != -1) { 146 char *exe, *pwddst; 147 char *realpathbuf = (char *)SDL_malloc(PATH_MAX + 1); 148 if (!realpathbuf) { 149 return NULL; 150 } 151 152 cmdline = SDL_malloc(len); 153 if (!cmdline) { 154 SDL_free(realpathbuf); 155 return NULL; 156 } 157 158 sysctl(mib, 4, cmdline, &len, NULL, 0); 159 160 exe = cmdline[0]; 161 pwddst = NULL; 162 if (SDL_strchr(exe, '/') == NULL) { // not a relative or absolute path, check $PATH for it 163 exe = search_path_for_binary(cmdline[0]); 164 } else { 165 if (exe && *exe == '.') { 166 const char *pwd = SDL_getenv("PWD"); 167 if (pwd && *pwd) { 168 SDL_asprintf(&pwddst, "%s/%s", pwd, exe); 169 } 170 } 171 } 172 173 if (exe) { 174 if (!pwddst) { 175 if (realpath(exe, realpathbuf) != NULL) { 176 result = realpathbuf; 177 } 178 } else { 179 if (realpath(pwddst, realpathbuf) != NULL) { 180 result = realpathbuf; 181 } 182 SDL_free(pwddst); 183 } 184 185 if (exe != cmdline[0]) { 186 SDL_free(exe); 187 } 188 } 189 190 if (!result) { 191 SDL_free(realpathbuf); 192 } 193 194 SDL_free(cmdline); 195 } 196#endif 197 198 // is a Linux-style /proc filesystem available? 199 if (!result && (access("/proc", F_OK) == 0)) { 200 /* !!! FIXME: after 2.0.6 ships, let's delete this code and just 201 use the /proc/%llu version. There's no reason to have 202 two copies of this plus all the #ifdefs. --ryan. */ 203#ifdef SDL_PLATFORM_FREEBSD 204 result = readSymLink("/proc/curproc/file"); 205#elif defined(SDL_PLATFORM_NETBSD) 206 result = readSymLink("/proc/curproc/exe"); 207#elif defined(SDL_PLATFORM_SOLARIS) 208 result = readSymLink("/proc/self/path/a.out"); 209#else 210 result = readSymLink("/proc/self/exe"); // linux. 211 if (!result) { 212 // older kernels don't have /proc/self ... try PID version... 213 char path[64]; 214 const int rc = SDL_snprintf(path, sizeof(path), 215 "/proc/%llu/exe", 216 (unsigned long long)getpid()); 217 if ((rc > 0) && (rc < sizeof(path))) { 218 result = readSymLink(path); 219 } 220 } 221#endif 222 } 223 224#ifdef SDL_PLATFORM_SOLARIS // try this as a fallback if /proc didn't pan out 225 if (!result) { 226 const char *path = getexecname(); 227 if ((path) && (path[0] == '/')) { // must be absolute path... 228 result = SDL_strdup(path); 229 if (!result) { 230 return NULL; 231 } 232 } 233 } 234#endif 235 /* If we had access to argv[0] here, we could check it for a path, 236 or troll through $PATH looking for it, too. */ 237 238 if (result) { // chop off filename. 239 char *ptr = SDL_strrchr(result, '/'); 240 if (ptr) { 241 *(ptr + 1) = '\0'; 242 } else { // shouldn't happen, but just in case... 243 SDL_free(result); 244 result = NULL; 245 } 246 } 247 248 if (result) { 249 // try to shrink buffer... 250 char *ptr = (char *)SDL_realloc(result, SDL_strlen(result) + 1); 251 if (ptr) { 252 result = ptr; // oh well if it failed. 253 } 254 } 255 256 return result; 257} 258 259char *SDL_SYS_GetPrefPath(const char *org, const char *app) 260{ 261 /* 262 * We use XDG's base directory spec, even if you're not on Linux. 263 * This isn't strictly correct, but the results are relatively sane 264 * in any case. 265 * 266 * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html 267 */ 268 const char *envr = SDL_getenv("XDG_DATA_HOME"); 269 const char *append; 270 char *result = NULL; 271 char *ptr = NULL; 272 273 if (!envr) { 274 // You end up with "$HOME/.local/share/Game Name 2" 275 envr = SDL_getenv("HOME"); 276 if (!envr) { 277 // we could take heroic measures with /etc/passwd, but oh well. 278 SDL_SetError("neither XDG_DATA_HOME nor HOME environment is set"); 279 return NULL; 280 } 281 append = "/.local/share/"; 282 } else { 283 append = "/"; 284 } 285 286 size_t len = SDL_strlen(envr); 287 if (envr[len - 1] == '/') { 288 append += 1; 289 } 290 291 len += SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3; 292 result = (char *)SDL_malloc(len); 293 if (!result) { 294 return NULL; 295 } 296 297 if (*org) { 298 (void)SDL_snprintf(result, len, "%s%s%s/%s/", envr, append, org, app); 299 } else { 300 (void)SDL_snprintf(result, len, "%s%s%s/", envr, append, app); 301 } 302 303 for (ptr = result + 1; *ptr; ptr++) { 304 if (*ptr == '/') { 305 *ptr = '\0'; 306 if (mkdir(result, 0700) != 0 && errno != EEXIST) { 307 goto error; 308 } 309 *ptr = '/'; 310 } 311 } 312 if (mkdir(result, 0700) != 0 && errno != EEXIST) { 313 error: 314 SDL_SetError("Couldn't create directory '%s': '%s'", result, strerror(errno)); 315 SDL_free(result); 316 return NULL; 317 } 318 319 return result; 320} 321 322/* 323 The two functions below (prefixed with `xdg_`) have been copied from: 324 https://gitlab.freedesktop.org/xdg/xdg-user-dirs/-/blob/master/xdg-user-dir-lookup.c 325 and have been adapted to work with SDL. They are licensed under the following 326 terms: 327 328 Copyright (c) 2007 Red Hat, Inc. 329 330 Permission is hereby granted, free of charge, to any person 331 obtaining a copy of this software and associated documentation files 332 (the "Software"), to deal in the Software without restriction, 333 including without limitation the rights to use, copy, modify, merge, 334 publish, distribute, sublicense, and/or sell copies of the Software, 335 and to permit persons to whom the Software is furnished to do so, 336 subject to the following conditions: 337 338 The above copyright notice and this permission notice shall be 339 included in all copies or substantial portions of the Software. 340 341 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 342 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 343 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 344 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 345 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 346 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 347 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 348 SOFTWARE. 349*/ 350static char *xdg_user_dir_lookup_with_fallback (const char *type, const char *fallback) 351{ 352 FILE *file; 353 const char *home_dir, *config_home; 354 char *config_file; 355 char buffer[512]; 356 char *user_dir; 357 char *p, *d; 358 int len; 359 int relative; 360 size_t l; 361 362 home_dir = SDL_getenv("HOME"); 363 364 if (!home_dir) 365 goto error; 366 367 config_home = SDL_getenv("XDG_CONFIG_HOME"); 368 if (!config_home || config_home[0] == 0) 369 { 370 l = SDL_strlen (home_dir) + SDL_strlen ("/.config/user-dirs.dirs") + 1; 371 config_file = (char *)SDL_malloc (l); 372 if (!config_file) 373 goto error; 374 375 SDL_strlcpy (config_file, home_dir, l); 376 SDL_strlcat (config_file, "/.config/user-dirs.dirs", l); 377 } 378 else 379 { 380 l = SDL_strlen (config_home) + SDL_strlen ("/user-dirs.dirs") + 1; 381 config_file = (char *)SDL_malloc (l); 382 if (!config_file) 383 goto error; 384 385 SDL_strlcpy (config_file, config_home, l); 386 SDL_strlcat (config_file, "/user-dirs.dirs", l); 387 } 388 389 file = fopen (config_file, "r"); 390 SDL_free (config_file); 391 if (!file) 392 goto error; 393 394 user_dir = NULL; 395 while (fgets (buffer, sizeof (buffer), file)) 396 { 397 // Remove newline at end 398 len = SDL_strlen (buffer); 399 if (len > 0 && buffer[len-1] == '\n') 400 buffer[len-1] = 0; 401 402 p = buffer; 403 while (*p == ' ' || *p == '\t') 404 p++; 405 406 if (SDL_strncmp (p, "XDG_", 4) != 0) 407 continue; 408 p += 4; 409 if (SDL_strncmp (p, type, SDL_strlen (type)) != 0) 410 continue; 411 p += SDL_strlen (type); 412 if (SDL_strncmp (p, "_DIR", 4) != 0) 413 continue; 414 p += 4; 415 416 while (*p == ' ' || *p == '\t') 417 p++; 418 419 if (*p != '=') 420 continue; 421 p++; 422 423 while (*p == ' ' || *p == '\t') 424 p++; 425 426 if (*p != '"') 427 continue; 428 p++; 429 430 relative = 0; 431 if (SDL_strncmp (p, "$HOME/", 6) == 0) 432 { 433 p += 6; 434 relative = 1; 435 } 436 else if (*p != '/') 437 continue; 438 439 SDL_free (user_dir); 440 if (relative) 441 { 442 l = SDL_strlen (home_dir) + 1 + SDL_strlen (p) + 1; 443 user_dir = (char *)SDL_malloc (l); 444 if (!user_dir) 445 goto error2; 446 447 SDL_strlcpy (user_dir, home_dir, l); 448 SDL_strlcat (user_dir, "/", l); 449 } 450 else 451 { 452 user_dir = (char *)SDL_malloc (SDL_strlen (p) + 1); 453 if (!user_dir) 454 goto error2; 455 456 *user_dir = 0; 457 } 458 459 d = user_dir + SDL_strlen (user_dir); 460 while (*p && *p != '"') 461 { 462 if ((*p == '\\') && (*(p+1) != 0)) 463 p++; 464 *d++ = *p++; 465 } 466 *d = 0; 467 } 468error2: 469 fclose (file); 470 471 if (user_dir) 472 return user_dir; 473 474 error: 475 if (fallback) 476 return SDL_strdup (fallback); 477 return NULL; 478} 479 480static char *xdg_user_dir_lookup (const char *type) 481{ 482 const char *home_dir; 483 char *dir, *user_dir; 484 485 dir = xdg_user_dir_lookup_with_fallback(type, NULL); 486 if (dir) 487 return dir; 488 489 home_dir = SDL_getenv("HOME"); 490 491 if (!home_dir) 492 return NULL; 493 494 // Special case desktop for historical compatibility 495 if (SDL_strcmp(type, "DESKTOP") == 0) { 496 size_t length = SDL_strlen(home_dir) + SDL_strlen("/Desktop") + 1; 497 user_dir = (char *)SDL_malloc(length); 498 if (!user_dir) 499 return NULL; 500 501 SDL_strlcpy(user_dir, home_dir, length); 502 SDL_strlcat(user_dir, "/Desktop", length); 503 return user_dir; 504 } 505 506 return NULL; 507} 508 509char *SDL_SYS_GetUserFolder(SDL_Folder folder) 510{ 511 const char *param = NULL; 512 char *result; 513 char *newresult; 514 515 /* According to `man xdg-user-dir`, the possible values are: 516 DESKTOP 517 DOWNLOAD 518 TEMPLATES 519 PUBLICSHARE 520 DOCUMENTS 521 MUSIC 522 PICTURES 523 VIDEOS 524 */ 525 switch(folder) { 526 case SDL_FOLDER_HOME: 527 param = SDL_getenv("HOME"); 528 529 if (!param) { 530 SDL_SetError("No $HOME environment variable available"); 531 return NULL; 532 } 533 534 result = SDL_strdup(param); 535 goto append_slash; 536 537 case SDL_FOLDER_DESKTOP: 538 param = "DESKTOP"; 539 break; 540 541 case SDL_FOLDER_DOCUMENTS: 542 param = "DOCUMENTS"; 543 break; 544 545 case SDL_FOLDER_DOWNLOADS: 546 param = "DOWNLOAD"; 547 break; 548 549 case SDL_FOLDER_MUSIC: 550 param = "MUSIC"; 551 break; 552 553 case SDL_FOLDER_PICTURES: 554 param = "PICTURES"; 555 break; 556 557 case SDL_FOLDER_PUBLICSHARE: 558 param = "PUBLICSHARE"; 559 break; 560 561 case SDL_FOLDER_SAVEDGAMES: 562 SDL_SetError("Saved Games folder unavailable on XDG"); 563 return NULL; 564 565 case SDL_FOLDER_SCREENSHOTS: 566 SDL_SetError("Screenshots folder unavailable on XDG"); 567 return NULL; 568 569 case SDL_FOLDER_TEMPLATES: 570 param = "TEMPLATES"; 571 break; 572 573 case SDL_FOLDER_VIDEOS: 574 param = "VIDEOS"; 575 break; 576 577 default: 578 SDL_SetError("Invalid SDL_Folder: %d", (int) folder); 579 return NULL; 580 } 581 582 /* param *should* to be set to something at this point, but just in case */ 583 if (!param) { 584 SDL_SetError("No corresponding XDG user directory"); 585 return NULL; 586 } 587 588 result = xdg_user_dir_lookup(param); 589 590 if (!result) { 591 SDL_SetError("XDG directory not available"); 592 return NULL; 593 } 594 595append_slash: 596 newresult = (char *) SDL_realloc(result, SDL_strlen(result) + 2); 597 598 if (!newresult) { 599 SDL_free(result); 600 return NULL; 601 } 602 603 result = newresult; 604 SDL_strlcat(result, "/", SDL_strlen(result) + 2); 605 606 return result; 607} 608 609#endif // SDL_FILESYSTEM_UNIX 610
[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.