Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * Unix support routines for PhysicsFS.
3
 *
4
 * Please see the file LICENSE.txt in the source's root directory.
5
 *
6
 *  This file written by Ryan C. Gordon.
7
 */
8
 
9
#define __PHYSICSFS_INTERNAL__
10
#include "physfs_platforms.h"
11
 
12
#ifdef PHYSFS_PLATFORM_UNIX
13
 
14
#include <ctype.h>
15
#include <unistd.h>
16
#include <stdlib.h>
17
#include <sys/types.h>
18
#include <pwd.h>
19
#include <sys/stat.h>
20
#include <sys/param.h>
21
#include <dirent.h>
22
#include <time.h>
23
#include <errno.h>
24
#include <limits.h>
25
 
26
#if PHYSFS_NO_CDROM_SUPPORT
27
#elif PHYSFS_PLATFORM_LINUX
28
#  define PHYSFS_HAVE_MNTENT_H 1
29
#elif defined __CYGWIN__
30
#  define PHYSFS_HAVE_MNTENT_H 1
31
#elif PHYSFS_PLATFORM_SOLARIS
32
#  define PHYSFS_HAVE_SYS_MNTTAB_H 1
33
#elif PHYSFS_PLATFORM_BSD
34
#  define PHYSFS_HAVE_SYS_UCRED_H 1
35
#else
36
#  warning No CD-ROM support included. Either define your platform here,
37
#  warning  or define PHYSFS_NO_CDROM_SUPPORT=1 to confirm this is intentional.
38
#endif
39
 
40
#ifdef PHYSFS_HAVE_SYS_UCRED_H
41
#  ifdef PHYSFS_HAVE_MNTENT_H
42
#    undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
43
#  endif
44
#  include <sys/mount.h>
45
#  include <sys/ucred.h>
46
#endif
47
 
48
#ifdef PHYSFS_HAVE_MNTENT_H
49
#include <mntent.h>
50
#endif
51
 
52
#ifdef PHYSFS_HAVE_SYS_MNTTAB_H
53
#include <sys/mnttab.h>
54
#endif
55
 
56
#ifdef PHYSFS_PLATFORM_FREEBSD
57
#include <sys/sysctl.h>
58
#endif
59
 
60
 
61
#include "physfs_internal.h"
62
 
63
int __PHYSFS_platformInit(void)
64
{
65
    return 1;  /* always succeed. */
66
} /* __PHYSFS_platformInit */
67
 
68
 
69
void __PHYSFS_platformDeinit(void)
70
{
71
    /* no-op */
72
} /* __PHYSFS_platformDeinit */
73
 
74
 
75
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
76
{
77
#if (defined PHYSFS_NO_CDROM_SUPPORT)
78
    /* no-op. */
79
 
80
#elif (defined PHYSFS_HAVE_SYS_UCRED_H)
81
    int i;
82
    struct statfs *mntbufp = NULL;
83
    int mounts = getmntinfo(&mntbufp, MNT_NOWAIT);
84
 
85
    for (i = 0; i < mounts; i++)
86
    {
87
        int add_it = 0;
88
 
89
        if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
90
            add_it = 1;
91
        else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
92
            add_it = 1;
93
 
94
        /* add other mount types here */
95
 
96
        if (add_it)
97
            cb(data, mntbufp[i].f_mntonname);
98
    } /* for */
99
 
100
#elif (defined PHYSFS_HAVE_MNTENT_H)
101
    FILE *mounts = NULL;
102
    struct mntent *ent = NULL;
103
 
104
    mounts = setmntent("/etc/mtab", "r");
105
    BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
106
 
107
    while ( (ent = getmntent(mounts)) != NULL )
108
    {
109
        int add_it = 0;
110
        if (strcmp(ent->mnt_type, "iso9660") == 0)
111
            add_it = 1;
112
        else if (strcmp(ent->mnt_type, "udf") == 0)
113
            add_it = 1;
114
 
115
        /* !!! FIXME: these might pick up floppy drives, right? */
116
        else if (strcmp(ent->mnt_type, "auto") == 0)
117
            add_it = 1;
118
        else if (strcmp(ent->mnt_type, "supermount") == 0)
119
            add_it = 1;
120
 
121
        /* add other mount types here */
122
 
123
        if (add_it)
124
            cb(data, ent->mnt_dir);
125
    } /* while */
126
 
127
    endmntent(mounts);
128
 
129
#elif (defined PHYSFS_HAVE_SYS_MNTTAB_H)
130
    FILE *mounts = fopen(MNTTAB, "r");
131
    struct mnttab ent;
132
 
133
    BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
134
    while (getmntent(mounts, &ent) == 0)
135
    {
136
        int add_it = 0;
137
        if (strcmp(ent.mnt_fstype, "hsfs") == 0)
138
            add_it = 1;
139
 
140
        /* add other mount types here */
141
 
142
        if (add_it)
143
            cb(data, ent.mnt_mountp);
144
    } /* while */
145
 
146
    fclose(mounts);
147
#endif
148
} /* __PHYSFS_platformDetectAvailableCDs */
149
 
150
 
151
/*
152
 * See where program (bin) resides in the $PATH specified by (envr).
153
 *  returns a copy of the first element in envr that contains it, or NULL
154
 *  if it doesn't exist or there were other problems. PHYSFS_SetError() is
155
 *  called if we have a problem.
156
 *
157
 * (envr) will be scribbled over, and you are expected to allocator.Free() the
158
 *  return value when you're done with it.
159
 */
160
static char *findBinaryInPath(const char *bin, char *envr)
161
{
162
    size_t alloc_size = 0;
163
    char *exe = NULL;
164
    char *start = envr;
165
    char *ptr;
166
 
167
    assert(bin != NULL);
168
    assert(envr != NULL);
169
 
170
    do
171
    {
172
        size_t size;
173
        size_t binlen;
174
 
175
        ptr = strchr(start, ':');  /* find next $PATH separator. */
176
        if (ptr)
177
            *ptr = '\0';
178
 
179
        binlen = strlen(bin);
180
        size = strlen(start) + binlen + 2;
181
        if (size >= alloc_size)
182
        {
183
            char *x = (char *) allocator.Realloc(exe, size);
184
            if (!x)
185
            {
186
                if (exe != NULL)
187
                    allocator.Free(exe);
188
                BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
189
            } /* if */
190
 
191
            alloc_size = size;
192
            exe = x;
193
        } /* if */
194
 
195
        /* build full binary path... */
196
        strcpy(exe, start);
197
        if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
198
            strcat(exe, "/");
199
        strcat(exe, bin);
200
 
201
        if (access(exe, X_OK) == 0)  /* Exists as executable? We're done. */
202
        {
203
            exe[(size - binlen) - 1] = '\0'; /* chop off filename, leave '/' */
204
            return exe;
205
        } /* if */
206
 
207
        start = ptr + 1;  /* start points to beginning of next element. */
208
    } while (ptr != NULL);
209
 
210
    if (exe != NULL)
211
        allocator.Free(exe);
212
 
213
    return NULL;  /* doesn't exist in path. */
214
} /* findBinaryInPath */
215
 
216
 
217
static char *readSymLink(const char *path)
218
{
219
    ssize_t len = 64;
220
    ssize_t rc = -1;
221
    char *retval = NULL;
222
 
223
    while (1)
224
    {
225
         char *ptr = (char *) allocator.Realloc(retval, (size_t) len);
226
         if (ptr == NULL)
227
             break;   /* out of memory. */
228
         retval = ptr;
229
 
230
         rc = readlink(path, retval, len);
231
         if (rc == -1)
232
             break;  /* not a symlink, i/o error, etc. */
233
 
234
         else if (rc < len)
235
         {
236
             retval[rc] = '\0';  /* readlink doesn't null-terminate. */
237
             return retval;  /* we're good to go. */
238
         } /* else if */
239
 
240
         len *= 2;  /* grow buffer, try again. */
241
    } /* while */
242
 
243
    if (retval != NULL)
244
        allocator.Free(retval);
245
    return NULL;
246
} /* readSymLink */
247
 
248
 
249
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
250
{
251
    char *retval = NULL;
252
    const char *envr = NULL;
253
 
254
    /* Try to avoid using argv0 unless forced to. Try system-specific stuff. */
255
 
256
    #if defined(PHYSFS_PLATFORM_FREEBSD)
257
    {
258
        char fullpath[PATH_MAX];
259
        size_t buflen = sizeof (fullpath);
260
        int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
261
        if (sysctl(mib, 4, fullpath, &buflen, NULL, 0) != -1)
262
            retval = __PHYSFS_strdup(fullpath);
263
    }
264
    #elif defined(PHYSFS_PLATFORM_SOLARIS)
265
    {
266
        const char *path = getexecname();
267
        if ((path != NULL) && (path[0] == '/'))  /* must be absolute path... */
268
            retval = __PHYSFS_strdup(path);
269
    }
270
    #endif
271
 
272
    /* If there's a Linux-like /proc filesystem, you can get the full path to
273
     *  the current process from a symlink in there.
274
     */
275
 
276
    if (!retval && (access("/proc", F_OK) == 0))
277
    {
278
        retval = readSymLink("/proc/self/exe");
279
        if (!retval) retval = readSymLink("/proc/curproc/file");
280
        if (!retval) retval = readSymLink("/proc/curproc/exe");
281
        if (retval == NULL)
282
        {
283
            /* older kernels don't have /proc/self ... try PID version... */
284
            const unsigned long long pid = (unsigned long long) getpid();
285
            char path[64];
286
            const int rc = (int) snprintf(path,sizeof(path),"/proc/%llu/exe",pid);
287
            if ( (rc > 0) && (rc < sizeof(path)) )
288
                retval = readSymLink(path);
289
        } /* if */
290
    } /* if */
291
 
292
    if (retval != NULL)  /* chop off filename. */
293
    {
294
        char *ptr = strrchr(retval, '/');
295
        if (ptr != NULL)
296
            *(ptr+1) = '\0';
297
        else  /* shouldn't happen, but just in case... */
298
        {
299
            allocator.Free(retval);
300
            retval = NULL;
301
        } /* else */
302
    } /* if */
303
 
304
    /* No /proc/self/exe, etc, but we have an argv[0] we can parse? */
305
    if ((retval == NULL) && (argv0 != NULL))
306
    {
307
        /* fast path: default behaviour can handle this. */
308
        if (strchr(argv0, '/') != NULL)
309
            return NULL;  /* higher level parses out real path from argv0. */
310
 
311
        /* If there's no dirsep on argv0, then look through $PATH for it. */
312
        envr = getenv("PATH");
313
        if (envr != NULL)
314
        {
315
            char *path = (char *) __PHYSFS_smallAlloc(strlen(envr) + 1);
316
            BAIL_IF(!path, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
317
            strcpy(path, envr);
318
            retval = findBinaryInPath(argv0, path);
319
            __PHYSFS_smallFree(path);
320
        } /* if */
321
    } /* if */
322
 
323
    if (retval != NULL)
324
    {
325
        /* try to shrink buffer... */
326
        char *ptr = (char *) allocator.Realloc(retval, strlen(retval) + 1);
327
        if (ptr != NULL)
328
            retval = ptr;  /* oh well if it failed. */
329
    } /* if */
330
 
331
    return retval;
332
} /* __PHYSFS_platformCalcBaseDir */
333
 
334
 
335
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
336
{
337
    /*
338
     * We use XDG's base directory spec, even if you're not on Linux.
339
     *  This isn't strictly correct, but the results are relatively sane
340
     *  in any case.
341
     *
342
     * https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
343
     */
344
    const char *envr = getenv("XDG_DATA_HOME");
345
    const char *append = "/";
346
    char *retval = NULL;
347
    size_t len = 0;
348
 
349
    if (!envr)
350
    {
351
        /* You end up with "$HOME/.local/share/Game Name 2" */
352
        envr = __PHYSFS_getUserDir();
353
        BAIL_IF_ERRPASS(!envr, NULL);  /* oh well. */
354
        append = ".local/share/";
355
    } /* if */
356
 
357
    len = strlen(envr) + strlen(append) + strlen(app) + 2;
358
    retval = (char *) allocator.Malloc(len);
359
    BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
360
    snprintf(retval, len, "%s%s%s/", envr, append, app);
361
    return retval;
362
} /* __PHYSFS_platformCalcPrefDir */
363
 
364
#endif /* PHYSFS_PLATFORM_UNIX */
365
 
366
/* end of physfs_platform_unix.c ... */
367