SDL

Форк
0
/
SDL_sysfilesystem.c 
618 строк · 16.1 Кб
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
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

45
static 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
75
static 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 = envr;
82
    char *ptr;
83

84
    if (!envr_real) {
85
        SDL_SetError("No $PATH set");
86
        return NULL;
87
    }
88

89
    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

125
char *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

259
char *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
    size_t len = 0;
273

274
    if (!app) {
275
        SDL_InvalidParamError("app");
276
        return NULL;
277
    }
278
    if (!org) {
279
        org = "";
280
    }
281

282
    if (!envr) {
283
        // You end up with "$HOME/.local/share/Game Name 2"
284
        envr = SDL_getenv("HOME");
285
        if (!envr) {
286
            // we could take heroic measures with /etc/passwd, but oh well.
287
            SDL_SetError("neither XDG_DATA_HOME nor HOME environment is set");
288
            return NULL;
289
        }
290
        append = "/.local/share/";
291
    } else {
292
        append = "/";
293
    }
294

295
    len = SDL_strlen(envr);
296
    if (envr[len - 1] == '/') {
297
        append += 1;
298
    }
299

300
    len += SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3;
301
    result = (char *)SDL_malloc(len);
302
    if (!result) {
303
        return NULL;
304
    }
305

306
    if (*org) {
307
        (void)SDL_snprintf(result, len, "%s%s%s/%s/", envr, append, org, app);
308
    } else {
309
        (void)SDL_snprintf(result, len, "%s%s%s/", envr, append, app);
310
    }
311

312
    for (ptr = result + 1; *ptr; ptr++) {
313
        if (*ptr == '/') {
314
            *ptr = '\0';
315
            if (mkdir(result, 0700) != 0 && errno != EEXIST) {
316
                goto error;
317
            }
318
            *ptr = '/';
319
        }
320
    }
321
    if (mkdir(result, 0700) != 0 && errno != EEXIST) {
322
    error:
323
        SDL_SetError("Couldn't create directory '%s': '%s'", result, strerror(errno));
324
        SDL_free(result);
325
        return NULL;
326
    }
327

328
    return result;
329
}
330

331
/*
332
  The two functions below (prefixed with `xdg_`) have been copied from:
333
  https://gitlab.freedesktop.org/xdg/xdg-user-dirs/-/blob/master/xdg-user-dir-lookup.c
334
  and have been adapted to work with SDL. They are licensed under the following
335
  terms:
336

337
  Copyright (c) 2007 Red Hat, Inc.
338

339
  Permission is hereby granted, free of charge, to any person
340
  obtaining a copy of this software and associated documentation files
341
  (the "Software"), to deal in the Software without restriction,
342
  including without limitation the rights to use, copy, modify, merge,
343
  publish, distribute, sublicense, and/or sell copies of the Software,
344
  and to permit persons to whom the Software is furnished to do so,
345
  subject to the following conditions:
346

347
  The above copyright notice and this permission notice shall be
348
  included in all copies or substantial portions of the Software.
349

350
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
351
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
352
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
353
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
354
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
355
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
356
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
357
  SOFTWARE.
358
*/
359
static char *xdg_user_dir_lookup_with_fallback (const char *type, const char *fallback)
360
{
361
  FILE *file;
362
  const char *home_dir, *config_home;
363
  char *config_file;
364
  char buffer[512];
365
  char *user_dir;
366
  char *p, *d;
367
  int len;
368
  int relative;
369
  size_t l;
370

371
  home_dir = SDL_getenv ("HOME");
372

373
  if (!home_dir)
374
    goto error;
375

376
  config_home = SDL_getenv ("XDG_CONFIG_HOME");
377
  if (!config_home || config_home[0] == 0)
378
    {
379
      l = SDL_strlen (home_dir) + SDL_strlen ("/.config/user-dirs.dirs") + 1;
380
      config_file = (char*) SDL_malloc (l);
381
      if (!config_file)
382
        goto error;
383

384
      SDL_strlcpy (config_file, home_dir, l);
385
      SDL_strlcat (config_file, "/.config/user-dirs.dirs", l);
386
    }
387
  else
388
    {
389
      l = SDL_strlen (config_home) + SDL_strlen ("/user-dirs.dirs") + 1;
390
      config_file = (char*) SDL_malloc (l);
391
      if (!config_file)
392
        goto error;
393

394
      SDL_strlcpy (config_file, config_home, l);
395
      SDL_strlcat (config_file, "/user-dirs.dirs", l);
396
    }
397

398
  file = fopen (config_file, "r");
399
  SDL_free (config_file);
400
  if (!file)
401
    goto error;
402

403
  user_dir = NULL;
404
  while (fgets (buffer, sizeof (buffer), file))
405
    {
406
      // Remove newline at end
407
      len = SDL_strlen (buffer);
408
      if (len > 0 && buffer[len-1] == '\n')
409
        buffer[len-1] = 0;
410

411
      p = buffer;
412
      while (*p == ' ' || *p == '\t')
413
        p++;
414

415
      if (SDL_strncmp (p, "XDG_", 4) != 0)
416
        continue;
417
      p += 4;
418
      if (SDL_strncmp (p, type, SDL_strlen (type)) != 0)
419
        continue;
420
      p += SDL_strlen (type);
421
      if (SDL_strncmp (p, "_DIR", 4) != 0)
422
        continue;
423
      p += 4;
424

425
      while (*p == ' ' || *p == '\t')
426
        p++;
427

428
      if (*p != '=')
429
        continue;
430
      p++;
431

432
      while (*p == ' ' || *p == '\t')
433
        p++;
434

435
      if (*p != '"')
436
        continue;
437
      p++;
438

439
      relative = 0;
440
      if (SDL_strncmp (p, "$HOME/", 6) == 0)
441
        {
442
          p += 6;
443
          relative = 1;
444
        }
445
      else if (*p != '/')
446
        continue;
447

448
      SDL_free (user_dir);
449
      if (relative)
450
        {
451
          l = SDL_strlen (home_dir) + 1 + SDL_strlen (p) + 1;
452
          user_dir = (char*) SDL_malloc (l);
453
          if (!user_dir)
454
            goto error2;
455

456
          SDL_strlcpy (user_dir, home_dir, l);
457
          SDL_strlcat (user_dir, "/", l);
458
        }
459
      else
460
        {
461
          user_dir = (char*) SDL_malloc (SDL_strlen (p) + 1);
462
          if (!user_dir)
463
            goto error2;
464

465
          *user_dir = 0;
466
        }
467

468
      d = user_dir + SDL_strlen (user_dir);
469
      while (*p && *p != '"')
470
        {
471
          if ((*p == '\\') && (*(p+1) != 0))
472
            p++;
473
          *d++ = *p++;
474
        }
475
      *d = 0;
476
    }
477
error2:
478
  fclose (file);
479

480
  if (user_dir)
481
    return user_dir;
482

483
 error:
484
  if (fallback)
485
    return SDL_strdup (fallback);
486
  return NULL;
487
}
488

489
static char *xdg_user_dir_lookup (const char *type)
490
{
491
    const char *home_dir;
492
    char *dir, *user_dir;
493

494
    dir = xdg_user_dir_lookup_with_fallback(type, NULL);
495
    if (dir)
496
        return dir;
497

498
    home_dir = SDL_getenv("HOME");
499

500
    if (!home_dir)
501
        return NULL;
502

503
    // Special case desktop for historical compatibility
504
    if (SDL_strcmp(type, "DESKTOP") == 0) {
505
        size_t length = SDL_strlen(home_dir) + SDL_strlen("/Desktop") + 1;
506
        user_dir = (char*) SDL_malloc(length);
507
        if (!user_dir)
508
            return NULL;
509

510
        SDL_strlcpy(user_dir, home_dir, length);
511
        SDL_strlcat(user_dir, "/Desktop", length);
512
        return user_dir;
513
    }
514

515
    return NULL;
516
}
517

518
char *SDL_SYS_GetUserFolder(SDL_Folder folder)
519
{
520
    const char *param = NULL;
521
    char *result;
522
    char *newresult;
523

524
    /* According to `man xdg-user-dir`, the possible values are:
525
        DESKTOP
526
        DOWNLOAD
527
        TEMPLATES
528
        PUBLICSHARE
529
        DOCUMENTS
530
        MUSIC
531
        PICTURES
532
        VIDEOS
533
    */
534
    switch(folder) {
535
    case SDL_FOLDER_HOME:
536
        param = SDL_getenv("HOME");
537

538
        if (!param) {
539
            SDL_SetError("No $HOME environment variable available");
540
            return NULL;
541
        }
542

543
        result = SDL_strdup(param);
544
        goto append_slash;
545

546
    case SDL_FOLDER_DESKTOP:
547
        param = "DESKTOP";
548
        break;
549

550
    case SDL_FOLDER_DOCUMENTS:
551
        param = "DOCUMENTS";
552
        break;
553

554
    case SDL_FOLDER_DOWNLOADS:
555
        param = "DOWNLOAD";
556
        break;
557

558
    case SDL_FOLDER_MUSIC:
559
        param = "MUSIC";
560
        break;
561

562
    case SDL_FOLDER_PICTURES:
563
        param = "PICTURES";
564
        break;
565

566
    case SDL_FOLDER_PUBLICSHARE:
567
        param = "PUBLICSHARE";
568
        break;
569

570
    case SDL_FOLDER_SAVEDGAMES:
571
        SDL_SetError("Saved Games folder unavailable on XDG");
572
        return NULL;
573

574
    case SDL_FOLDER_SCREENSHOTS:
575
        SDL_SetError("Screenshots folder unavailable on XDG");
576
        return NULL;
577

578
    case SDL_FOLDER_TEMPLATES:
579
        param = "TEMPLATES";
580
        break;
581

582
    case SDL_FOLDER_VIDEOS:
583
        param = "VIDEOS";
584
        break;
585

586
    default:
587
        SDL_SetError("Invalid SDL_Folder: %d", (int) folder);
588
        return NULL;
589
    }
590

591
    /* param *should* to be set to something at this point, but just in case */
592
    if (!param) {
593
        SDL_SetError("No corresponding XDG user directory");
594
        return NULL;
595
    }
596

597
    result = xdg_user_dir_lookup(param);
598

599
    if (!result) {
600
        SDL_SetError("XDG directory not available");
601
        return NULL;
602
    }
603

604
append_slash:
605
    newresult = (char *) SDL_realloc(result, SDL_strlen(result) + 2);
606

607
    if (!newresult) {
608
        SDL_free(result);
609
        return NULL;
610
    }
611

612
    result = newresult;
613
    SDL_strlcat(result, "/", SDL_strlen(result) + 2);
614

615
    return result;
616
}
617

618
#endif // SDL_FILESYSTEM_UNIX
619

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.