Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/**
2
 * PhysicsFS; a portable, flexible file i/o abstraction.
3
 *
4
 * Documentation is in physfs.h. It's verbose, honest.  :)
5
 *
6
 * Please see the file LICENSE.txt in the source's root directory.
7
 *
8
 *  This file written by Ryan C. Gordon.
9
 */
10
 
11
#define __PHYSICSFS_INTERNAL__
12
#include "physfs_internal.h"
13
 
14
#if defined(_MSC_VER)
15
#include <stdarg.h>
16
 
17
/* this code came from https://stackoverflow.com/a/8712996 */
18
int __PHYSFS_msvc_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
19
{
20
    int count = -1;
21
 
22
    if (size != 0)
23
        count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
24
    if (count == -1)
25
        count = _vscprintf(format, ap);
26
 
27
    return count;
28
}
29
 
30
int __PHYSFS_msvc_snprintf(char *outBuf, size_t size, const char *format, ...)
31
{
32
    int count;
33
    va_list ap;
34
 
35
    va_start(ap, format);
36
    count = __PHYSFS_msvc_vsnprintf(outBuf, size, format, ap);
37
    va_end(ap);
38
 
39
    return count;
40
}
41
#endif
42
 
43
 
44
typedef struct __PHYSFS_DIRHANDLE__
45
{
46
    void *opaque;  /* Instance data unique to the archiver. */
47
    char *dirName;  /* Path to archive in platform-dependent notation. */
48
    char *mountPoint; /* Mountpoint in virtual file tree. */
49
    const PHYSFS_Archiver *funcs;  /* Ptr to archiver info for this handle. */
50
    struct __PHYSFS_DIRHANDLE__ *next;  /* linked list stuff. */
51
} DirHandle;
52
 
53
 
54
typedef struct __PHYSFS_FILEHANDLE__
55
{
56
    PHYSFS_Io *io;  /* Instance data unique to the archiver for this file. */
57
    PHYSFS_uint8 forReading; /* Non-zero if reading, zero if write/append */
58
    const DirHandle *dirHandle;  /* Archiver instance that created this */
59
    PHYSFS_uint8 *buffer;  /* Buffer, if set (NULL otherwise). Don't touch! */
60
    size_t bufsize;  /* Bufsize, if set (0 otherwise). Don't touch! */
61
    size_t buffill;  /* Buffer fill size. Don't touch! */
62
    size_t bufpos;  /* Buffer position. Don't touch! */
63
    struct __PHYSFS_FILEHANDLE__ *next;  /* linked list stuff. */
64
} FileHandle;
65
 
66
 
67
typedef struct __PHYSFS_ERRSTATETYPE__
68
{
69
    void *tid;
70
    PHYSFS_ErrorCode code;
71
    struct __PHYSFS_ERRSTATETYPE__ *next;
72
} ErrState;
73
 
74
 
75
/* General PhysicsFS state ... */
76
static int initialized = 0;
77
static ErrState *errorStates = NULL;
78
static DirHandle *searchPath = NULL;
79
static DirHandle *writeDir = NULL;
80
static FileHandle *openWriteList = NULL;
81
static FileHandle *openReadList = NULL;
82
static char *baseDir = NULL;
83
static char *userDir = NULL;
84
static char *prefDir = NULL;
85
static int allowSymLinks = 0;
86
static PHYSFS_Archiver **archivers = NULL;
87
static PHYSFS_ArchiveInfo **archiveInfo = NULL;
88
static volatile size_t numArchivers = 0;
89
 
90
/* mutexes ... */
91
static void *errorLock = NULL;     /* protects error message list.        */
92
static void *stateLock = NULL;     /* protects other PhysFS static state. */
93
 
94
/* allocator ... */
95
static int externalAllocator = 0;
96
PHYSFS_Allocator allocator;
97
 
98
 
99
#ifdef PHYSFS_NEED_ATOMIC_OP_FALLBACK
100
static inline int __PHYSFS_atomicAdd(int *ptrval, const int val)
101
{
102
    int retval;
103
    __PHYSFS_platformGrabMutex(stateLock);
104
    retval = *ptrval;
105
    *ptrval = retval + val;
106
    __PHYSFS_platformReleaseMutex(stateLock);
107
    return retval;
108
} /* __PHYSFS_atomicAdd */
109
 
110
int __PHYSFS_ATOMIC_INCR(int *ptrval)
111
{
112
    return __PHYSFS_atomicAdd(ptrval, 1);
113
} /* __PHYSFS_ATOMIC_INCR */
114
 
115
int __PHYSFS_ATOMIC_DECR(int *ptrval)
116
{
117
    return __PHYSFS_atomicAdd(ptrval, -1);
118
} /* __PHYSFS_ATOMIC_DECR */
119
#endif
120
 
121
 
122
 
123
/* PHYSFS_Io implementation for i/o to physical filesystem... */
124
 
125
/* !!! FIXME: maybe refcount the paths in a string pool? */
126
typedef struct __PHYSFS_NativeIoInfo
127
{
128
    void *handle;
129
    const char *path;
130
    int mode;   /* 'r', 'w', or 'a' */
131
} NativeIoInfo;
132
 
133
static PHYSFS_sint64 nativeIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
134
{
135
    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
136
    return __PHYSFS_platformRead(info->handle, buf, len);
137
} /* nativeIo_read */
138
 
139
static PHYSFS_sint64 nativeIo_write(PHYSFS_Io *io, const void *buffer,
140
                                    PHYSFS_uint64 len)
141
{
142
    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
143
    return __PHYSFS_platformWrite(info->handle, buffer, len);
144
} /* nativeIo_write */
145
 
146
static int nativeIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
147
{
148
    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
149
    return __PHYSFS_platformSeek(info->handle, offset);
150
} /* nativeIo_seek */
151
 
152
static PHYSFS_sint64 nativeIo_tell(PHYSFS_Io *io)
153
{
154
    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
155
    return __PHYSFS_platformTell(info->handle);
156
} /* nativeIo_tell */
157
 
158
static PHYSFS_sint64 nativeIo_length(PHYSFS_Io *io)
159
{
160
    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
161
    return __PHYSFS_platformFileLength(info->handle);
162
} /* nativeIo_length */
163
 
164
static PHYSFS_Io *nativeIo_duplicate(PHYSFS_Io *io)
165
{
166
    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
167
    return __PHYSFS_createNativeIo(info->path, info->mode);
168
} /* nativeIo_duplicate */
169
 
170
static int nativeIo_flush(PHYSFS_Io *io)
171
{
172
    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
173
    return __PHYSFS_platformFlush(info->handle);
174
} /* nativeIo_flush */
175
 
176
static void nativeIo_destroy(PHYSFS_Io *io)
177
{
178
    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
179
    __PHYSFS_platformClose(info->handle);
180
    allocator.Free((void *) info->path);
181
    allocator.Free(info);
182
    allocator.Free(io);
183
} /* nativeIo_destroy */
184
 
185
static const PHYSFS_Io __PHYSFS_nativeIoInterface =
186
{
187
    CURRENT_PHYSFS_IO_API_VERSION, NULL,
188
    nativeIo_read,
189
    nativeIo_write,
190
    nativeIo_seek,
191
    nativeIo_tell,
192
    nativeIo_length,
193
    nativeIo_duplicate,
194
    nativeIo_flush,
195
    nativeIo_destroy
196
};
197
 
198
PHYSFS_Io *__PHYSFS_createNativeIo(const char *path, const int mode)
199
{
200
    PHYSFS_Io *io = NULL;
201
    NativeIoInfo *info = NULL;
202
    void *handle = NULL;
203
    char *pathdup = NULL;
204
 
205
    assert((mode == 'r') || (mode == 'w') || (mode == 'a'));
206
 
207
    io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
208
    GOTO_IF(!io, PHYSFS_ERR_OUT_OF_MEMORY, createNativeIo_failed);
209
    info = (NativeIoInfo *) allocator.Malloc(sizeof (NativeIoInfo));
210
    GOTO_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, createNativeIo_failed);
211
    pathdup = (char *) allocator.Malloc(strlen(path) + 1);
212
    GOTO_IF(!pathdup, PHYSFS_ERR_OUT_OF_MEMORY, createNativeIo_failed);
213
 
214
    if (mode == 'r')
215
        handle = __PHYSFS_platformOpenRead(path);
216
    else if (mode == 'w')
217
        handle = __PHYSFS_platformOpenWrite(path);
218
    else if (mode == 'a')
219
        handle = __PHYSFS_platformOpenAppend(path);
220
 
221
    GOTO_IF_ERRPASS(!handle, createNativeIo_failed);
222
 
223
    strcpy(pathdup, path);
224
    info->handle = handle;
225
    info->path = pathdup;
226
    info->mode = mode;
227
    memcpy(io, &__PHYSFS_nativeIoInterface, sizeof (*io));
228
    io->opaque = info;
229
    return io;
230
 
231
createNativeIo_failed:
232
    if (handle != NULL) __PHYSFS_platformClose(handle);
233
    if (pathdup != NULL) allocator.Free(pathdup);
234
    if (info != NULL) allocator.Free(info);
235
    if (io != NULL) allocator.Free(io);
236
    return NULL;
237
} /* __PHYSFS_createNativeIo */
238
 
239
 
240
/* PHYSFS_Io implementation for i/o to a memory buffer... */
241
 
242
typedef struct __PHYSFS_MemoryIoInfo
243
{
244
    const PHYSFS_uint8 *buf;
245
    PHYSFS_uint64 len;
246
    PHYSFS_uint64 pos;
247
    PHYSFS_Io *parent;
248
    int refcount;
249
    void (*destruct)(void *);
250
} MemoryIoInfo;
251
 
252
static PHYSFS_sint64 memoryIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
253
{
254
    MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
255
    const PHYSFS_uint64 avail = info->len - info->pos;
256
    assert(avail <= info->len);
257
 
258
    if (avail == 0)
259
        return 0;  /* we're at EOF; nothing to do. */
260
 
261
    if (len > avail)
262
        len = avail;
263
 
264
    memcpy(buf, info->buf + info->pos, (size_t) len);
265
    info->pos += len;
266
    return len;
267
} /* memoryIo_read */
268
 
269
static PHYSFS_sint64 memoryIo_write(PHYSFS_Io *io, const void *buffer,
270
                                    PHYSFS_uint64 len)
271
{
272
    BAIL(PHYSFS_ERR_OPEN_FOR_READING, -1);
273
} /* memoryIo_write */
274
 
275
static int memoryIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
276
{
277
    MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
278
    BAIL_IF(offset > info->len, PHYSFS_ERR_PAST_EOF, 0);
279
    info->pos = offset;
280
    return 1;
281
} /* memoryIo_seek */
282
 
283
static PHYSFS_sint64 memoryIo_tell(PHYSFS_Io *io)
284
{
285
    const MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
286
    return (PHYSFS_sint64) info->pos;
287
} /* memoryIo_tell */
288
 
289
static PHYSFS_sint64 memoryIo_length(PHYSFS_Io *io)
290
{
291
    const MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
292
    return (PHYSFS_sint64) info->len;
293
} /* memoryIo_length */
294
 
295
static PHYSFS_Io *memoryIo_duplicate(PHYSFS_Io *io)
296
{
297
    MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
298
    MemoryIoInfo *newinfo = NULL;
299
    PHYSFS_Io *parent = info->parent;
300
    PHYSFS_Io *retval = NULL;
301
 
302
    /* avoid deep copies. */
303
    assert((!parent) || (!((MemoryIoInfo *) parent->opaque)->parent) );
304
 
305
    /* share the buffer between duplicates. */
306
    if (parent != NULL)  /* dup the parent, increment its refcount. */
307
        return parent->duplicate(parent);
308
 
309
    /* we're the parent. */
310
 
311
    retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
312
    BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
313
    newinfo = (MemoryIoInfo *) allocator.Malloc(sizeof (MemoryIoInfo));
314
    if (!newinfo)
315
    {
316
        allocator.Free(retval);
317
        BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
318
    } /* if */
319
 
320
    __PHYSFS_ATOMIC_INCR(&info->refcount);
321
 
322
    memset(newinfo, '\0', sizeof (*info));
323
    newinfo->buf = info->buf;
324
    newinfo->len = info->len;
325
    newinfo->pos = 0;
326
    newinfo->parent = io;
327
    newinfo->refcount = 0;
328
    newinfo->destruct = NULL;
329
 
330
    memcpy(retval, io, sizeof (*retval));
331
    retval->opaque = newinfo;
332
    return retval;
333
} /* memoryIo_duplicate */
334
 
335
static int memoryIo_flush(PHYSFS_Io *io) { return 1;  /* it's read-only. */ }
336
 
337
static void memoryIo_destroy(PHYSFS_Io *io)
338
{
339
    MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
340
    PHYSFS_Io *parent = info->parent;
341
 
342
    if (parent != NULL)
343
    {
344
        assert(info->buf == ((MemoryIoInfo *) info->parent->opaque)->buf);
345
        assert(info->len == ((MemoryIoInfo *) info->parent->opaque)->len);
346
        assert(info->refcount == 0);
347
        assert(info->destruct == NULL);
348
        allocator.Free(info);
349
        allocator.Free(io);
350
        parent->destroy(parent);  /* decrements refcount. */
351
        return;
352
    } /* if */
353
 
354
    /* we _are_ the parent. */
355
    assert(info->refcount > 0);  /* even in a race, we hold a reference. */
356
 
357
    if (__PHYSFS_ATOMIC_DECR(&info->refcount) == 0)
358
    {
359
        void (*destruct)(void *) = info->destruct;
360
        void *buf = (void *) info->buf;
361
        io->opaque = NULL;  /* kill this here in case of race. */
362
        allocator.Free(info);
363
        allocator.Free(io);
364
        if (destruct != NULL)
365
            destruct(buf);
366
    } /* if */
367
} /* memoryIo_destroy */
368
 
369
 
370
static const PHYSFS_Io __PHYSFS_memoryIoInterface =
371
{
372
    CURRENT_PHYSFS_IO_API_VERSION, NULL,
373
    memoryIo_read,
374
    memoryIo_write,
375
    memoryIo_seek,
376
    memoryIo_tell,
377
    memoryIo_length,
378
    memoryIo_duplicate,
379
    memoryIo_flush,
380
    memoryIo_destroy
381
};
382
 
383
PHYSFS_Io *__PHYSFS_createMemoryIo(const void *buf, PHYSFS_uint64 len,
384
                                   void (*destruct)(void *))
385
{
386
    PHYSFS_Io *io = NULL;
387
    MemoryIoInfo *info = NULL;
388
 
389
    io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
390
    GOTO_IF(!io, PHYSFS_ERR_OUT_OF_MEMORY, createMemoryIo_failed);
391
    info = (MemoryIoInfo *) allocator.Malloc(sizeof (MemoryIoInfo));
392
    GOTO_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, createMemoryIo_failed);
393
 
394
    memset(info, '\0', sizeof (*info));
395
    info->buf = (const PHYSFS_uint8 *) buf;
396
    info->len = len;
397
    info->pos = 0;
398
    info->parent = NULL;
399
    info->refcount = 1;
400
    info->destruct = destruct;
401
 
402
    memcpy(io, &__PHYSFS_memoryIoInterface, sizeof (*io));
403
    io->opaque = info;
404
    return io;
405
 
406
createMemoryIo_failed:
407
    if (info != NULL) allocator.Free(info);
408
    if (io != NULL) allocator.Free(io);
409
    return NULL;
410
} /* __PHYSFS_createMemoryIo */
411
 
412
 
413
/* PHYSFS_Io implementation for i/o to a PHYSFS_File... */
414
 
415
static PHYSFS_sint64 handleIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
416
{
417
    return PHYSFS_readBytes((PHYSFS_File *) io->opaque, buf, len);
418
} /* handleIo_read */
419
 
420
static PHYSFS_sint64 handleIo_write(PHYSFS_Io *io, const void *buffer,
421
                                    PHYSFS_uint64 len)
422
{
423
    return PHYSFS_writeBytes((PHYSFS_File *) io->opaque, buffer, len);
424
} /* handleIo_write */
425
 
426
static int handleIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
427
{
428
    return PHYSFS_seek((PHYSFS_File *) io->opaque, offset);
429
} /* handleIo_seek */
430
 
431
static PHYSFS_sint64 handleIo_tell(PHYSFS_Io *io)
432
{
433
    return PHYSFS_tell((PHYSFS_File *) io->opaque);
434
} /* handleIo_tell */
435
 
436
static PHYSFS_sint64 handleIo_length(PHYSFS_Io *io)
437
{
438
    return PHYSFS_fileLength((PHYSFS_File *) io->opaque);
439
} /* handleIo_length */
440
 
441
static PHYSFS_Io *handleIo_duplicate(PHYSFS_Io *io)
442
{
443
    /*
444
     * There's no duplicate at the PHYSFS_File level, so we break the
445
     *  abstraction. We're allowed to: we're physfs.c!
446
     */
447
    FileHandle *origfh = (FileHandle *) io->opaque;
448
    FileHandle *newfh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
449
    PHYSFS_Io *retval = NULL;
450
 
451
    GOTO_IF(!newfh, PHYSFS_ERR_OUT_OF_MEMORY, handleIo_dupe_failed);
452
    memset(newfh, '\0', sizeof (*newfh));
453
 
454
    retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
455
    GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, handleIo_dupe_failed);
456
 
457
#if 0  /* we don't buffer the duplicate, at least not at the moment. */
458
    if (origfh->buffer != NULL)
459
    {
460
        newfh->buffer = (PHYSFS_uint8 *) allocator.Malloc(origfh->bufsize);
461
        if (!newfh->buffer)
462
            GOTO(PHYSFS_ERR_OUT_OF_MEMORY, handleIo_dupe_failed);
463
        newfh->bufsize = origfh->bufsize;
464
    } /* if */
465
#endif
466
 
467
    newfh->io = origfh->io->duplicate(origfh->io);
468
    GOTO_IF_ERRPASS(!newfh->io, handleIo_dupe_failed);
469
 
470
    newfh->forReading = origfh->forReading;
471
    newfh->dirHandle = origfh->dirHandle;
472
 
473
    __PHYSFS_platformGrabMutex(stateLock);
474
    if (newfh->forReading)
475
    {
476
        newfh->next = openReadList;
477
        openReadList = newfh;
478
    } /* if */
479
    else
480
    {
481
        newfh->next = openWriteList;
482
        openWriteList = newfh;
483
    } /* else */
484
    __PHYSFS_platformReleaseMutex(stateLock);
485
 
486
    memcpy(retval, io, sizeof (PHYSFS_Io));
487
    retval->opaque = newfh;
488
    return retval;
489
 
490
handleIo_dupe_failed:
491
    if (newfh)
492
    {
493
        if (newfh->io != NULL) newfh->io->destroy(newfh->io);
494
        if (newfh->buffer != NULL) allocator.Free(newfh->buffer);
495
        allocator.Free(newfh);
496
    } /* if */
497
 
498
    return NULL;
499
} /* handleIo_duplicate */
500
 
501
static int handleIo_flush(PHYSFS_Io *io)
502
{
503
    return PHYSFS_flush((PHYSFS_File *) io->opaque);
504
} /* handleIo_flush */
505
 
506
static void handleIo_destroy(PHYSFS_Io *io)
507
{
508
    if (io->opaque != NULL)
509
        PHYSFS_close((PHYSFS_File *) io->opaque);
510
    allocator.Free(io);
511
} /* handleIo_destroy */
512
 
513
static const PHYSFS_Io __PHYSFS_handleIoInterface =
514
{
515
    CURRENT_PHYSFS_IO_API_VERSION, NULL,
516
    handleIo_read,
517
    handleIo_write,
518
    handleIo_seek,
519
    handleIo_tell,
520
    handleIo_length,
521
    handleIo_duplicate,
522
    handleIo_flush,
523
    handleIo_destroy
524
};
525
 
526
static PHYSFS_Io *__PHYSFS_createHandleIo(PHYSFS_File *f)
527
{
528
    PHYSFS_Io *io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
529
    BAIL_IF(!io, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
530
    memcpy(io, &__PHYSFS_handleIoInterface, sizeof (*io));
531
    io->opaque = f;
532
    return io;
533
} /* __PHYSFS_createHandleIo */
534
 
535
 
536
/* functions ... */
537
 
538
typedef struct
539
{
540
    char **list;
541
    PHYSFS_uint32 size;
542
    PHYSFS_ErrorCode errcode;
543
} EnumStringListCallbackData;
544
 
545
static void enumStringListCallback(void *data, const char *str)
546
{
547
    void *ptr;
548
    char *newstr;
549
    EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data;
550
 
551
    if (pecd->errcode)
552
        return;
553
 
554
    ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *));
555
    newstr = (char *) allocator.Malloc(strlen(str) + 1);
556
    if (ptr != NULL)
557
        pecd->list = (char **) ptr;
558
 
559
    if ((ptr == NULL) || (newstr == NULL))
560
    {
561
        pecd->errcode = PHYSFS_ERR_OUT_OF_MEMORY;
562
        pecd->list[pecd->size] = NULL;
563
        PHYSFS_freeList(pecd->list);
564
        return;
565
    } /* if */
566
 
567
    strcpy(newstr, str);
568
    pecd->list[pecd->size] = newstr;
569
    pecd->size++;
570
} /* enumStringListCallback */
571
 
572
 
573
static char **doEnumStringList(void (*func)(PHYSFS_StringCallback, void *))
574
{
575
    EnumStringListCallbackData ecd;
576
    memset(&ecd, '\0', sizeof (ecd));
577
    ecd.list = (char **) allocator.Malloc(sizeof (char *));
578
    BAIL_IF(!ecd.list, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
579
    func(enumStringListCallback, &ecd);
580
 
581
    if (ecd.errcode)
582
    {
583
        PHYSFS_setErrorCode(ecd.errcode);
584
        return NULL;
585
    } /* if */
586
 
587
    ecd.list[ecd.size] = NULL;
588
    return ecd.list;
589
} /* doEnumStringList */
590
 
591
 
592
static void __PHYSFS_bubble_sort(void *a, size_t lo, size_t hi,
593
                                 int (*cmpfn)(void *, size_t, size_t),
594
                                 void (*swapfn)(void *, size_t, size_t))
595
{
596
    size_t i;
597
    int sorted;
598
 
599
    do
600
    {
601
        sorted = 1;
602
        for (i = lo; i < hi; i++)
603
        {
604
            if (cmpfn(a, i, i + 1) > 0)
605
            {
606
                swapfn(a, i, i + 1);
607
                sorted = 0;
608
            } /* if */
609
        } /* for */
610
    } while (!sorted);
611
} /* __PHYSFS_bubble_sort */
612
 
613
 
614
static void __PHYSFS_quick_sort(void *a, size_t lo, size_t hi,
615
                         int (*cmpfn)(void *, size_t, size_t),
616
                         void (*swapfn)(void *, size_t, size_t))
617
{
618
    size_t i;
619
    size_t j;
620
    size_t v;
621
 
622
    if ((hi - lo) <= PHYSFS_QUICKSORT_THRESHOLD)
623
        __PHYSFS_bubble_sort(a, lo, hi, cmpfn, swapfn);
624
    else
625
    {
626
        i = (hi + lo) / 2;
627
 
628
        if (cmpfn(a, lo, i) > 0) swapfn(a, lo, i);
629
        if (cmpfn(a, lo, hi) > 0) swapfn(a, lo, hi);
630
        if (cmpfn(a, i, hi) > 0) swapfn(a, i, hi);
631
 
632
        j = hi - 1;
633
        swapfn(a, i, j);
634
        i = lo;
635
        v = j;
636
        while (1)
637
        {
638
            while(cmpfn(a, ++i, v) < 0) { /* do nothing */ }
639
            while(cmpfn(a, --j, v) > 0) { /* do nothing */ }
640
            if (j < i)
641
                break;
642
            swapfn(a, i, j);
643
        } /* while */
644
        if (i != (hi-1))
645
            swapfn(a, i, hi-1);
646
        __PHYSFS_quick_sort(a, lo, j, cmpfn, swapfn);
647
        __PHYSFS_quick_sort(a, i+1, hi, cmpfn, swapfn);
648
    } /* else */
649
} /* __PHYSFS_quick_sort */
650
 
651
 
652
void __PHYSFS_sort(void *entries, size_t max,
653
                   int (*cmpfn)(void *, size_t, size_t),
654
                   void (*swapfn)(void *, size_t, size_t))
655
{
656
    /*
657
     * Quicksort w/ Bubblesort fallback algorithm inspired by code from here:
658
     *   https://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html
659
     */
660
    if (max > 0)
661
        __PHYSFS_quick_sort(entries, 0, max - 1, cmpfn, swapfn);
662
} /* __PHYSFS_sort */
663
 
664
 
665
static ErrState *findErrorForCurrentThread(void)
666
{
667
    ErrState *i;
668
    void *tid;
669
 
670
    if (errorLock != NULL)
671
        __PHYSFS_platformGrabMutex(errorLock);
672
 
673
    if (errorStates != NULL)
674
    {
675
        tid = __PHYSFS_platformGetThreadID();
676
 
677
        for (i = errorStates; i != NULL; i = i->next)
678
        {
679
            if (i->tid == tid)
680
            {
681
                if (errorLock != NULL)
682
                    __PHYSFS_platformReleaseMutex(errorLock);
683
                return i;
684
            } /* if */
685
        } /* for */
686
    } /* if */
687
 
688
    if (errorLock != NULL)
689
        __PHYSFS_platformReleaseMutex(errorLock);
690
 
691
    return NULL;   /* no error available. */
692
} /* findErrorForCurrentThread */
693
 
694
 
695
/* this doesn't reset the error state. */
696
static inline PHYSFS_ErrorCode currentErrorCode(void)
697
{
698
    const ErrState *err = findErrorForCurrentThread();
699
    return err ? err->code : PHYSFS_ERR_OK;
700
} /* currentErrorCode */
701
 
702
 
703
PHYSFS_ErrorCode PHYSFS_getLastErrorCode(void)
704
{
705
    ErrState *err = findErrorForCurrentThread();
706
    const PHYSFS_ErrorCode retval = (err) ? err->code : PHYSFS_ERR_OK;
707
    if (err)
708
        err->code = PHYSFS_ERR_OK;
709
    return retval;
710
} /* PHYSFS_getLastErrorCode */
711
 
712
 
713
PHYSFS_DECL const char *PHYSFS_getErrorByCode(PHYSFS_ErrorCode code)
714
{
715
    switch (code)
716
    {
717
        case PHYSFS_ERR_OK: return "no error";
718
        case PHYSFS_ERR_OTHER_ERROR: return "unknown error";
719
        case PHYSFS_ERR_OUT_OF_MEMORY: return "out of memory";
720
        case PHYSFS_ERR_NOT_INITIALIZED: return "not initialized";
721
        case PHYSFS_ERR_IS_INITIALIZED: return "already initialized";
722
        case PHYSFS_ERR_ARGV0_IS_NULL: return "argv[0] is NULL";
723
        case PHYSFS_ERR_UNSUPPORTED: return "unsupported";
724
        case PHYSFS_ERR_PAST_EOF: return "past end of file";
725
        case PHYSFS_ERR_FILES_STILL_OPEN: return "files still open";
726
        case PHYSFS_ERR_INVALID_ARGUMENT: return "invalid argument";
727
        case PHYSFS_ERR_NOT_MOUNTED: return "not mounted";
728
        case PHYSFS_ERR_NOT_FOUND: return "not found";
729
        case PHYSFS_ERR_SYMLINK_FORBIDDEN: return "symlinks are forbidden";
730
        case PHYSFS_ERR_NO_WRITE_DIR: return "write directory is not set";
731
        case PHYSFS_ERR_OPEN_FOR_READING: return "file open for reading";
732
        case PHYSFS_ERR_OPEN_FOR_WRITING: return "file open for writing";
733
        case PHYSFS_ERR_NOT_A_FILE: return "not a file";
734
        case PHYSFS_ERR_READ_ONLY: return "read-only filesystem";
735
        case PHYSFS_ERR_CORRUPT: return "corrupted";
736
        case PHYSFS_ERR_SYMLINK_LOOP: return "infinite symbolic link loop";
737
        case PHYSFS_ERR_IO: return "i/o error";
738
        case PHYSFS_ERR_PERMISSION: return "permission denied";
739
        case PHYSFS_ERR_NO_SPACE: return "no space available for writing";
740
        case PHYSFS_ERR_BAD_FILENAME: return "filename is illegal or insecure";
741
        case PHYSFS_ERR_BUSY: return "tried to modify a file the OS needs";
742
        case PHYSFS_ERR_DIR_NOT_EMPTY: return "directory isn't empty";
743
        case PHYSFS_ERR_OS_ERROR: return "OS reported an error";
744
        case PHYSFS_ERR_DUPLICATE: return "duplicate resource";
745
        case PHYSFS_ERR_BAD_PASSWORD: return "bad password";
746
        case PHYSFS_ERR_APP_CALLBACK: return "app callback reported error";
747
    } /* switch */
748
 
749
    return NULL;  /* don't know this error code. */
750
} /* PHYSFS_getErrorByCode */
751
 
752
 
753
void PHYSFS_setErrorCode(PHYSFS_ErrorCode errcode)
754
{
755
    ErrState *err;
756
 
757
    if (!errcode)
758
        return;
759
 
760
    err = findErrorForCurrentThread();
761
    if (err == NULL)
762
    {
763
        err = (ErrState *) allocator.Malloc(sizeof (ErrState));
764
        if (err == NULL)
765
            return;   /* uhh...? */
766
 
767
        memset(err, '\0', sizeof (ErrState));
768
        err->tid = __PHYSFS_platformGetThreadID();
769
 
770
        if (errorLock != NULL)
771
            __PHYSFS_platformGrabMutex(errorLock);
772
 
773
        err->next = errorStates;
774
        errorStates = err;
775
 
776
        if (errorLock != NULL)
777
            __PHYSFS_platformReleaseMutex(errorLock);
778
    } /* if */
779
 
780
    err->code = errcode;
781
} /* PHYSFS_setErrorCode */
782
 
783
 
784
const char *PHYSFS_getLastError(void)
785
{
786
    const PHYSFS_ErrorCode err = PHYSFS_getLastErrorCode();
787
    return (err) ? PHYSFS_getErrorByCode(err) : NULL;
788
} /* PHYSFS_getLastError */
789
 
790
 
791
/* MAKE SURE that errorLock is held before calling this! */
792
static void freeErrorStates(void)
793
{
794
    ErrState *i;
795
    ErrState *next;
796
 
797
    for (i = errorStates; i != NULL; i = next)
798
    {
799
        next = i->next;
800
        allocator.Free(i);
801
    } /* for */
802
 
803
    errorStates = NULL;
804
} /* freeErrorStates */
805
 
806
 
807
void PHYSFS_getLinkedVersion(PHYSFS_Version *ver)
808
{
809
    if (ver != NULL)
810
    {
811
        ver->major = PHYSFS_VER_MAJOR;
812
        ver->minor = PHYSFS_VER_MINOR;
813
        ver->patch = PHYSFS_VER_PATCH;
814
    } /* if */
815
} /* PHYSFS_getLinkedVersion */
816
 
817
 
818
static const char *find_filename_extension(const char *fname)
819
{
820
    const char *retval = NULL;
821
    if (fname != NULL)
822
    {
823
        const char *p = strchr(fname, '.');
824
        retval = p;
825
 
826
        while (p != NULL)
827
        {
828
            p = strchr(p + 1, '.');
829
            if (p != NULL)
830
                retval = p;
831
        } /* while */
832
 
833
        if (retval != NULL)
834
            retval++;  /* skip '.' */
835
    } /* if */
836
 
837
    return retval;
838
} /* find_filename_extension */
839
 
840
 
841
static DirHandle *tryOpenDir(PHYSFS_Io *io, const PHYSFS_Archiver *funcs,
842
                             const char *d, int forWriting, int *_claimed)
843
{
844
    DirHandle *retval = NULL;
845
    void *opaque = NULL;
846
 
847
    if (io != NULL)
848
        BAIL_IF_ERRPASS(!io->seek(io, 0), NULL);
849
 
850
    opaque = funcs->openArchive(io, d, forWriting, _claimed);
851
    if (opaque != NULL)
852
    {
853
        retval = (DirHandle *) allocator.Malloc(sizeof (DirHandle));
854
        if (retval == NULL)
855
            funcs->closeArchive(opaque);
856
        else
857
        {
858
            memset(retval, '\0', sizeof (DirHandle));
859
            retval->mountPoint = NULL;
860
            retval->funcs = funcs;
861
            retval->opaque = opaque;
862
        } /* else */
863
    } /* if */
864
 
865
    return retval;
866
} /* tryOpenDir */
867
 
868
 
869
static DirHandle *openDirectory(PHYSFS_Io *io, const char *d, int forWriting)
870
{
871
    DirHandle *retval = NULL;
872
    PHYSFS_Archiver **i;
873
    const char *ext;
874
    int created_io = 0;
875
    int claimed = 0;
876
    PHYSFS_ErrorCode errcode;
877
 
878
    assert((io != NULL) || (d != NULL));
879
 
880
    if (io == NULL)
881
    {
882
        /* file doesn't exist, etc? Just fail out. */
883
        PHYSFS_Stat statbuf;
884
        BAIL_IF_ERRPASS(!__PHYSFS_platformStat(d, &statbuf, 1), NULL);
885
 
886
        /* DIR gets first shot (unlike the rest, it doesn't deal with files). */
887
        if (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY)
888
        {
889
            retval = tryOpenDir(io, &__PHYSFS_Archiver_DIR, d, forWriting, &claimed);
890
            if (retval || claimed)
891
                return retval;
892
        } /* if */
893
 
894
        io = __PHYSFS_createNativeIo(d, forWriting ? 'w' : 'r');
895
        BAIL_IF_ERRPASS(!io, NULL);
896
        created_io = 1;
897
    } /* if */
898
 
899
    ext = find_filename_extension(d);
900
    if (ext != NULL)
901
    {
902
        /* Look for archivers with matching file extensions first... */
903
        for (i = archivers; (*i != NULL) && (retval == NULL) && !claimed; i++)
904
        {
905
            if (PHYSFS_utf8stricmp(ext, (*i)->info.extension) == 0)
906
                retval = tryOpenDir(io, *i, d, forWriting, &claimed);
907
        } /* for */
908
 
909
        /* failing an exact file extension match, try all the others... */
910
        for (i = archivers; (*i != NULL) && (retval == NULL) && !claimed; i++)
911
        {
912
            if (PHYSFS_utf8stricmp(ext, (*i)->info.extension) != 0)
913
                retval = tryOpenDir(io, *i, d, forWriting, &claimed);
914
        } /* for */
915
    } /* if */
916
 
917
    else  /* no extension? Try them all. */
918
    {
919
        for (i = archivers; (*i != NULL) && (retval == NULL) && !claimed; i++)
920
            retval = tryOpenDir(io, *i, d, forWriting, &claimed);
921
    } /* else */
922
 
923
    errcode = currentErrorCode();
924
 
925
    if ((!retval) && (created_io))
926
        io->destroy(io);
927
 
928
    BAIL_IF(!retval, claimed ? errcode : PHYSFS_ERR_UNSUPPORTED, NULL);
929
    return retval;
930
} /* openDirectory */
931
 
932
 
933
/*
934
 * Make a platform-independent path string sane. Doesn't actually check the
935
 *  file hierarchy, it just cleans up the string.
936
 *  (dst) must be a buffer at least as big as (src), as this is where the
937
 *  cleaned up string is deposited.
938
 * If there are illegal bits in the path (".." entries, etc) then we
939
 *  return zero and (dst) is undefined. Non-zero if the path was sanitized.
940
 */
941
static int sanitizePlatformIndependentPath(const char *src, char *dst)
942
{
943
    char *prev;
944
    char ch;
945
 
946
    while (*src == '/')  /* skip initial '/' chars... */
947
        src++;
948
 
949
    /* Make sure the entire string isn't "." or ".." */
950
    if ((strcmp(src, ".") == 0) || (strcmp(src, "..") == 0))
951
        BAIL(PHYSFS_ERR_BAD_FILENAME, 0);
952
 
953
    prev = dst;
954
    do
955
    {
956
        ch = *(src++);
957
 
958
        if ((ch == ':') || (ch == '\\'))  /* illegal chars in a physfs path. */
959
            BAIL(PHYSFS_ERR_BAD_FILENAME, 0);
960
 
961
        if (ch == '/')   /* path separator. */
962
        {
963
            *dst = '\0';  /* "." and ".." are illegal pathnames. */
964
            if ((strcmp(prev, ".") == 0) || (strcmp(prev, "..") == 0))
965
                BAIL(PHYSFS_ERR_BAD_FILENAME, 0);
966
 
967
            while (*src == '/')   /* chop out doubles... */
968
                src++;
969
 
970
            if (*src == '\0') /* ends with a pathsep? */
971
                break;  /* we're done, don't add final pathsep to dst. */
972
 
973
            prev = dst + 1;
974
        } /* if */
975
 
976
        *(dst++) = ch;
977
    } while (ch != '\0');
978
 
979
    return 1;
980
} /* sanitizePlatformIndependentPath */
981
 
982
 
983
/*
984
 * Figure out if (fname) is part of (h)'s mountpoint. (fname) must be an
985
 *  output from sanitizePlatformIndependentPath(), so that it is in a known
986
 *  state.
987
 *
988
 * This only finds legitimate segments of a mountpoint. If the mountpoint is
989
 *  "/a/b/c" and (fname) is "/a/b/c", "/", or "/a/b/c/d", then the results are
990
 *  all zero. "/a/b" will succeed, though.
991
 */
992
static int partOfMountPoint(DirHandle *h, char *fname)
993
{
994
    int rc;
995
    size_t len, mntpntlen;
996
 
997
    if (h->mountPoint == NULL)
998
        return 0;
999
    else if (*fname == '\0')
1000
        return 1;
1001
 
1002
    len = strlen(fname);
1003
    mntpntlen = strlen(h->mountPoint);
1004
    if (len > mntpntlen)  /* can't be a subset of mountpoint. */
1005
        return 0;
1006
 
1007
    /* if true, must be not a match or a complete match, but not a subset. */
1008
    if ((len + 1) == mntpntlen)
1009
        return 0;
1010
 
1011
    rc = strncmp(fname, h->mountPoint, len); /* !!! FIXME: case insensitive? */
1012
    if (rc != 0)
1013
        return 0;  /* not a match. */
1014
 
1015
    /* make sure /a/b matches /a/b/ and not /a/bc ... */
1016
    return h->mountPoint[len] == '/';
1017
} /* partOfMountPoint */
1018
 
1019
 
1020
static DirHandle *createDirHandle(PHYSFS_Io *io, const char *newDir,
1021
                                  const char *mountPoint, int forWriting)
1022
{
1023
    DirHandle *dirHandle = NULL;
1024
    char *tmpmntpnt = NULL;
1025
 
1026
    assert(newDir != NULL);  /* should have caught this higher up. */
1027
 
1028
    if (mountPoint != NULL)
1029
    {
1030
        const size_t len = strlen(mountPoint) + 1;
1031
        tmpmntpnt = (char *) __PHYSFS_smallAlloc(len);
1032
        GOTO_IF(!tmpmntpnt, PHYSFS_ERR_OUT_OF_MEMORY, badDirHandle);
1033
        if (!sanitizePlatformIndependentPath(mountPoint, tmpmntpnt))
1034
            goto badDirHandle;
1035
        mountPoint = tmpmntpnt;  /* sanitized version. */
1036
    } /* if */
1037
 
1038
    dirHandle = openDirectory(io, newDir, forWriting);
1039
    GOTO_IF_ERRPASS(!dirHandle, badDirHandle);
1040
 
1041
    dirHandle->dirName = (char *) allocator.Malloc(strlen(newDir) + 1);
1042
    GOTO_IF(!dirHandle->dirName, PHYSFS_ERR_OUT_OF_MEMORY, badDirHandle);
1043
    strcpy(dirHandle->dirName, newDir);
1044
 
1045
    if ((mountPoint != NULL) && (*mountPoint != '\0'))
1046
    {
1047
        dirHandle->mountPoint = (char *)allocator.Malloc(strlen(mountPoint)+2);
1048
        if (!dirHandle->mountPoint)
1049
            GOTO(PHYSFS_ERR_OUT_OF_MEMORY, badDirHandle);
1050
        strcpy(dirHandle->mountPoint, mountPoint);
1051
        strcat(dirHandle->mountPoint, "/");
1052
    } /* if */
1053
 
1054
    __PHYSFS_smallFree(tmpmntpnt);
1055
    return dirHandle;
1056
 
1057
badDirHandle:
1058
    if (dirHandle != NULL)
1059
    {
1060
        dirHandle->funcs->closeArchive(dirHandle->opaque);
1061
        allocator.Free(dirHandle->dirName);
1062
        allocator.Free(dirHandle->mountPoint);
1063
        allocator.Free(dirHandle);
1064
    } /* if */
1065
 
1066
    __PHYSFS_smallFree(tmpmntpnt);
1067
    return NULL;
1068
} /* createDirHandle */
1069
 
1070
 
1071
/* MAKE SURE you've got the stateLock held before calling this! */
1072
static int freeDirHandle(DirHandle *dh, FileHandle *openList)
1073
{
1074
    FileHandle *i;
1075
 
1076
    if (dh == NULL)
1077
        return 1;
1078
 
1079
    for (i = openList; i != NULL; i = i->next)
1080
        BAIL_IF(i->dirHandle == dh, PHYSFS_ERR_FILES_STILL_OPEN, 0);
1081
 
1082
    dh->funcs->closeArchive(dh->opaque);
1083
    allocator.Free(dh->dirName);
1084
    allocator.Free(dh->mountPoint);
1085
    allocator.Free(dh);
1086
    return 1;
1087
} /* freeDirHandle */
1088
 
1089
 
1090
static char *calculateBaseDir(const char *argv0)
1091
{
1092
    const char dirsep = __PHYSFS_platformDirSeparator;
1093
    char *retval = NULL;
1094
    char *ptr = NULL;
1095
 
1096
    /* Give the platform layer first shot at this. */
1097
    retval = __PHYSFS_platformCalcBaseDir(argv0);
1098
    if (retval != NULL)
1099
        return retval;
1100
 
1101
    /* We need argv0 to go on. */
1102
    BAIL_IF(argv0 == NULL, PHYSFS_ERR_ARGV0_IS_NULL, NULL);
1103
 
1104
    ptr = strrchr(argv0, dirsep);
1105
    if (ptr != NULL)
1106
    {
1107
        const size_t size = ((size_t) (ptr - argv0)) + 1;
1108
        retval = (char *) allocator.Malloc(size + 1);
1109
        BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
1110
        memcpy(retval, argv0, size);
1111
        retval[size] = '\0';
1112
        return retval;
1113
    } /* if */
1114
 
1115
    /* argv0 wasn't helpful. */
1116
    BAIL(PHYSFS_ERR_INVALID_ARGUMENT, NULL);
1117
} /* calculateBaseDir */
1118
 
1119
 
1120
static int initializeMutexes(void)
1121
{
1122
    errorLock = __PHYSFS_platformCreateMutex();
1123
    if (errorLock == NULL)
1124
        goto initializeMutexes_failed;
1125
 
1126
    stateLock = __PHYSFS_platformCreateMutex();
1127
    if (stateLock == NULL)
1128
        goto initializeMutexes_failed;
1129
 
1130
    return 1;  /* success. */
1131
 
1132
initializeMutexes_failed:
1133
    if (errorLock != NULL)
1134
        __PHYSFS_platformDestroyMutex(errorLock);
1135
 
1136
    if (stateLock != NULL)
1137
        __PHYSFS_platformDestroyMutex(stateLock);
1138
 
1139
    errorLock = stateLock = NULL;
1140
    return 0;  /* failed. */
1141
} /* initializeMutexes */
1142
 
1143
 
1144
static int doRegisterArchiver(const PHYSFS_Archiver *_archiver);
1145
 
1146
static int initStaticArchivers(void)
1147
{
1148
    #define REGISTER_STATIC_ARCHIVER(arc) { \
1149
        if (!doRegisterArchiver(&__PHYSFS_Archiver_##arc)) { \
1150
            return 0; \
1151
        } \
1152
    }
1153
 
1154
    #if PHYSFS_SUPPORTS_ZIP
1155
        REGISTER_STATIC_ARCHIVER(ZIP);
1156
    #endif
1157
    #if PHYSFS_SUPPORTS_7Z
1158
        SZIP_global_init();
1159
        REGISTER_STATIC_ARCHIVER(7Z);
1160
    #endif
1161
    #if PHYSFS_SUPPORTS_GRP
1162
        REGISTER_STATIC_ARCHIVER(GRP);
1163
    #endif
1164
    #if PHYSFS_SUPPORTS_QPAK
1165
        REGISTER_STATIC_ARCHIVER(QPAK);
1166
    #endif
1167
    #if PHYSFS_SUPPORTS_HOG
1168
        REGISTER_STATIC_ARCHIVER(HOG);
1169
    #endif
1170
    #if PHYSFS_SUPPORTS_MVL
1171
        REGISTER_STATIC_ARCHIVER(MVL);
1172
    #endif
1173
    #if PHYSFS_SUPPORTS_WAD
1174
        REGISTER_STATIC_ARCHIVER(WAD);
1175
    #endif
1176
    #if PHYSFS_SUPPORTS_SLB
1177
        REGISTER_STATIC_ARCHIVER(SLB);
1178
    #endif
1179
    #if PHYSFS_SUPPORTS_ISO9660
1180
        REGISTER_STATIC_ARCHIVER(ISO9660);
1181
    #endif
1182
    #if PHYSFS_SUPPORTS_VDF
1183
        REGISTER_STATIC_ARCHIVER(VDF)
1184
    #endif
1185
 
1186
    #undef REGISTER_STATIC_ARCHIVER
1187
 
1188
    return 1;
1189
} /* initStaticArchivers */
1190
 
1191
 
1192
static void setDefaultAllocator(void);
1193
static int doDeinit(void);
1194
 
1195
int PHYSFS_init(const char *argv0)
1196
{
1197
    BAIL_IF(initialized, PHYSFS_ERR_IS_INITIALIZED, 0);
1198
 
1199
    if (!externalAllocator)
1200
        setDefaultAllocator();
1201
 
1202
    if ((allocator.Init != NULL) && (!allocator.Init())) return 0;
1203
 
1204
    if (!__PHYSFS_platformInit())
1205
    {
1206
        if (allocator.Deinit != NULL) allocator.Deinit();
1207
        return 0;
1208
    } /* if */
1209
 
1210
    /* everything below here can be cleaned up safely by doDeinit(). */
1211
 
1212
    if (!initializeMutexes()) goto initFailed;
1213
 
1214
    baseDir = calculateBaseDir(argv0);
1215
    if (!baseDir) goto initFailed;
1216
 
1217
    userDir = __PHYSFS_platformCalcUserDir();
1218
    if (!userDir) goto initFailed;
1219
 
1220
    /* Platform layer is required to append a dirsep. */
1221
    assert(baseDir[strlen(baseDir) - 1] == __PHYSFS_platformDirSeparator);
1222
    assert(userDir[strlen(userDir) - 1] == __PHYSFS_platformDirSeparator);
1223
 
1224
    if (!initStaticArchivers()) goto initFailed;
1225
 
1226
    initialized = 1;
1227
 
1228
    /* This makes sure that the error subsystem is initialized. */
1229
    PHYSFS_setErrorCode(PHYSFS_getLastErrorCode());
1230
 
1231
    return 1;
1232
 
1233
initFailed:
1234
    doDeinit();
1235
    return 0;
1236
} /* PHYSFS_init */
1237
 
1238
 
1239
/* MAKE SURE you hold stateLock before calling this! */
1240
static int closeFileHandleList(FileHandle **list)
1241
{
1242
    FileHandle *i;
1243
    FileHandle *next = NULL;
1244
 
1245
    for (i = *list; i != NULL; i = next)
1246
    {
1247
        PHYSFS_Io *io = i->io;
1248
        next = i->next;
1249
 
1250
        if (io->flush && !io->flush(io))
1251
        {
1252
            *list = i;
1253
            return 0;
1254
        } /* if */
1255
 
1256
        io->destroy(io);
1257
        allocator.Free(i);
1258
    } /* for */
1259
 
1260
    *list = NULL;
1261
    return 1;
1262
} /* closeFileHandleList */
1263
 
1264
 
1265
/* MAKE SURE you hold the stateLock before calling this! */
1266
static void freeSearchPath(void)
1267
{
1268
    DirHandle *i;
1269
    DirHandle *next = NULL;
1270
 
1271
    closeFileHandleList(&openReadList);
1272
 
1273
    if (searchPath != NULL)
1274
    {
1275
        for (i = searchPath; i != NULL; i = next)
1276
        {
1277
            next = i->next;
1278
            freeDirHandle(i, openReadList);
1279
        } /* for */
1280
        searchPath = NULL;
1281
    } /* if */
1282
} /* freeSearchPath */
1283
 
1284
 
1285
/* MAKE SURE you hold stateLock before calling this! */
1286
static int archiverInUse(const PHYSFS_Archiver *arc, const DirHandle *list)
1287
{
1288
    const DirHandle *i;
1289
    for (i = list; i != NULL; i = i->next)
1290
    {
1291
        if (i->funcs == arc)
1292
            return 1;
1293
    } /* for */
1294
 
1295
    return 0;  /* not in use */
1296
} /* archiverInUse */
1297
 
1298
 
1299
/* MAKE SURE you hold stateLock before calling this! */
1300
static int doDeregisterArchiver(const size_t idx)
1301
{
1302
    const size_t len = (numArchivers - idx) * sizeof (void *);
1303
    PHYSFS_ArchiveInfo *info = archiveInfo[idx];
1304
    PHYSFS_Archiver *arc = archivers[idx];
1305
 
1306
    /* make sure nothing is still using this archiver */
1307
    if (archiverInUse(arc, searchPath) || archiverInUse(arc, writeDir))
1308
        BAIL(PHYSFS_ERR_FILES_STILL_OPEN, 0);
1309
 
1310
    allocator.Free((void *) info->extension);
1311
    allocator.Free((void *) info->description);
1312
    allocator.Free((void *) info->author);
1313
    allocator.Free((void *) info->url);
1314
    allocator.Free((void *) arc);
1315
 
1316
    memmove(&archiveInfo[idx], &archiveInfo[idx+1], len);
1317
    memmove(&archivers[idx], &archivers[idx+1], len);
1318
 
1319
    assert(numArchivers > 0);
1320
    numArchivers--;
1321
 
1322
    return 1;
1323
} /* doDeregisterArchiver */
1324
 
1325
 
1326
/* Does NOT hold the state lock; we're shutting down. */
1327
static void freeArchivers(void)
1328
{
1329
    while (numArchivers > 0)
1330
    {
1331
        if (!doDeregisterArchiver(numArchivers - 1))
1332
            assert(!"nothing should be mounted during shutdown.");
1333
    } /* while */
1334
 
1335
    allocator.Free(archivers);
1336
    allocator.Free(archiveInfo);
1337
    archivers = NULL;
1338
    archiveInfo = NULL;
1339
} /* freeArchivers */
1340
 
1341
 
1342
static int doDeinit(void)
1343
{
1344
    closeFileHandleList(&openWriteList);
1345
    BAIL_IF(!PHYSFS_setWriteDir(NULL), PHYSFS_ERR_FILES_STILL_OPEN, 0);
1346
 
1347
    freeSearchPath();
1348
    freeArchivers();
1349
    freeErrorStates();
1350
 
1351
    if (baseDir != NULL)
1352
    {
1353
        allocator.Free(baseDir);
1354
        baseDir = NULL;
1355
    } /* if */
1356
 
1357
    if (userDir != NULL)
1358
    {
1359
        allocator.Free(userDir);
1360
        userDir = NULL;
1361
    } /* if */
1362
 
1363
    if (prefDir != NULL)
1364
    {
1365
        allocator.Free(prefDir);
1366
        prefDir = NULL;
1367
    } /* if */
1368
 
1369
    if (archiveInfo != NULL)
1370
    {
1371
        allocator.Free(archiveInfo);
1372
        archiveInfo = NULL;
1373
    } /* if */
1374
 
1375
    if (archivers != NULL)
1376
    {
1377
        allocator.Free(archivers);
1378
        archivers = NULL;
1379
    } /* if */
1380
 
1381
    allowSymLinks = 0;
1382
    initialized = 0;
1383
 
1384
    if (errorLock) __PHYSFS_platformDestroyMutex(errorLock);
1385
    if (stateLock) __PHYSFS_platformDestroyMutex(stateLock);
1386
 
1387
    if (allocator.Deinit != NULL)
1388
        allocator.Deinit();
1389
 
1390
    errorLock = stateLock = NULL;
1391
 
1392
    __PHYSFS_platformDeinit();
1393
 
1394
    return 1;
1395
} /* doDeinit */
1396
 
1397
 
1398
int PHYSFS_deinit(void)
1399
{
1400
    BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
1401
    return doDeinit();
1402
} /* PHYSFS_deinit */
1403
 
1404
 
1405
int PHYSFS_isInit(void)
1406
{
1407
    return initialized;
1408
} /* PHYSFS_isInit */
1409
 
1410
 
1411
char *__PHYSFS_strdup(const char *str)
1412
{
1413
    char *retval = (char *) allocator.Malloc(strlen(str) + 1);
1414
    if (retval)
1415
        strcpy(retval, str);
1416
    return retval;
1417
} /* __PHYSFS_strdup */
1418
 
1419
 
1420
PHYSFS_uint32 __PHYSFS_hashString(const char *str, size_t len)
1421
{
1422
    PHYSFS_uint32 hash = 5381;
1423
    while (len--)
1424
        hash = ((hash << 5) + hash) ^ *(str++);
1425
    return hash;
1426
} /* __PHYSFS_hashString */
1427
 
1428
 
1429
/* MAKE SURE you hold stateLock before calling this! */
1430
static int doRegisterArchiver(const PHYSFS_Archiver *_archiver)
1431
{
1432
    const PHYSFS_uint32 maxver = CURRENT_PHYSFS_ARCHIVER_API_VERSION;
1433
    const size_t len = (numArchivers + 2) * sizeof (void *);
1434
    PHYSFS_Archiver *archiver = NULL;
1435
    PHYSFS_ArchiveInfo *info = NULL;
1436
    const char *ext = NULL;
1437
    void *ptr = NULL;
1438
    size_t i;
1439
 
1440
    BAIL_IF(!_archiver, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1441
    BAIL_IF(_archiver->version > maxver, PHYSFS_ERR_UNSUPPORTED, 0);
1442
    BAIL_IF(!_archiver->info.extension, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1443
    BAIL_IF(!_archiver->info.description, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1444
    BAIL_IF(!_archiver->info.author, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1445
    BAIL_IF(!_archiver->info.url, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1446
    BAIL_IF(!_archiver->openArchive, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1447
    BAIL_IF(!_archiver->enumerate, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1448
    BAIL_IF(!_archiver->openRead, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1449
    BAIL_IF(!_archiver->openWrite, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1450
    BAIL_IF(!_archiver->openAppend, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1451
    BAIL_IF(!_archiver->remove, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1452
    BAIL_IF(!_archiver->mkdir, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1453
    BAIL_IF(!_archiver->closeArchive, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1454
    BAIL_IF(!_archiver->stat, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1455
 
1456
    ext = _archiver->info.extension;
1457
    for (i = 0; i < numArchivers; i++)
1458
    {
1459
        if (PHYSFS_utf8stricmp(archiveInfo[i]->extension, ext) == 0)
1460
            BAIL(PHYSFS_ERR_DUPLICATE, 0);
1461
    } /* for */
1462
 
1463
    /* make a copy of the data. */
1464
    archiver = (PHYSFS_Archiver *) allocator.Malloc(sizeof (*archiver));
1465
    GOTO_IF(!archiver, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
1466
 
1467
    /* Must copy sizeof (OLD_VERSION_OF_STRUCT) when version changes! */
1468
    memcpy(archiver, _archiver, sizeof (*archiver));
1469
 
1470
    info = (PHYSFS_ArchiveInfo *) &archiver->info;
1471
    memset(info, '\0', sizeof (*info));  /* NULL in case an alloc fails. */
1472
    #define CPYSTR(item) \
1473
        info->item = __PHYSFS_strdup(_archiver->info.item); \
1474
        GOTO_IF(!info->item, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
1475
    CPYSTR(extension);
1476
    CPYSTR(description);
1477
    CPYSTR(author);
1478
    CPYSTR(url);
1479
    info->supportsSymlinks = _archiver->info.supportsSymlinks;
1480
    #undef CPYSTR
1481
 
1482
    ptr = allocator.Realloc(archiveInfo, len);
1483
    GOTO_IF(!ptr, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
1484
    archiveInfo = (PHYSFS_ArchiveInfo **) ptr;
1485
 
1486
    ptr = allocator.Realloc(archivers, len);
1487
    GOTO_IF(!ptr, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
1488
    archivers = (PHYSFS_Archiver **) ptr;
1489
 
1490
    archiveInfo[numArchivers] = info;
1491
    archiveInfo[numArchivers + 1] = NULL;
1492
 
1493
    archivers[numArchivers] = archiver;
1494
    archivers[numArchivers + 1] = NULL;
1495
 
1496
    numArchivers++;
1497
 
1498
    return 1;
1499
 
1500
regfailed:
1501
    if (info != NULL)
1502
    {
1503
        allocator.Free((void *) info->extension);
1504
        allocator.Free((void *) info->description);
1505
        allocator.Free((void *) info->author);
1506
        allocator.Free((void *) info->url);
1507
    } /* if */
1508
    allocator.Free(archiver);
1509
 
1510
    return 0;
1511
} /* doRegisterArchiver */
1512
 
1513
 
1514
int PHYSFS_registerArchiver(const PHYSFS_Archiver *archiver)
1515
{
1516
    int retval;
1517
    BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
1518
    __PHYSFS_platformGrabMutex(stateLock);
1519
    retval = doRegisterArchiver(archiver);
1520
    __PHYSFS_platformReleaseMutex(stateLock);
1521
    return retval;
1522
} /* PHYSFS_registerArchiver */
1523
 
1524
 
1525
int PHYSFS_deregisterArchiver(const char *ext)
1526
{
1527
    size_t i;
1528
 
1529
    BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
1530
    BAIL_IF(!ext, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1531
 
1532
    __PHYSFS_platformGrabMutex(stateLock);
1533
    for (i = 0; i < numArchivers; i++)
1534
    {
1535
        if (PHYSFS_utf8stricmp(archiveInfo[i]->extension, ext) == 0)
1536
        {
1537
            const int retval = doDeregisterArchiver(i);
1538
            __PHYSFS_platformReleaseMutex(stateLock);
1539
            return retval;
1540
        } /* if */
1541
    } /* for */
1542
    __PHYSFS_platformReleaseMutex(stateLock);
1543
 
1544
    BAIL(PHYSFS_ERR_NOT_FOUND, 0);
1545
} /* PHYSFS_deregisterArchiver */
1546
 
1547
 
1548
const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void)
1549
{
1550
    BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, NULL);
1551
    return (const PHYSFS_ArchiveInfo **) archiveInfo;
1552
} /* PHYSFS_supportedArchiveTypes */
1553
 
1554
 
1555
void PHYSFS_freeList(void *list)
1556
{
1557
    void **i;
1558
    if (list != NULL)
1559
    {
1560
        for (i = (void **) list; *i != NULL; i++)
1561
            allocator.Free(*i);
1562
 
1563
        allocator.Free(list);
1564
    } /* if */
1565
} /* PHYSFS_freeList */
1566
 
1567
 
1568
const char *PHYSFS_getDirSeparator(void)
1569
{
1570
    static char retval[2] = { __PHYSFS_platformDirSeparator, '\0' };
1571
    return retval;
1572
} /* PHYSFS_getDirSeparator */
1573
 
1574
 
1575
char **PHYSFS_getCdRomDirs(void)
1576
{
1577
    return doEnumStringList(__PHYSFS_platformDetectAvailableCDs);
1578
} /* PHYSFS_getCdRomDirs */
1579
 
1580
 
1581
void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback callback, void *data)
1582
{
1583
    __PHYSFS_platformDetectAvailableCDs(callback, data);
1584
} /* PHYSFS_getCdRomDirsCallback */
1585
 
1586
 
1587
const char *PHYSFS_getPrefDir(const char *org, const char *app)
1588
{
1589
    const char dirsep = __PHYSFS_platformDirSeparator;
1590
    PHYSFS_Stat statbuf;
1591
    char *ptr = NULL;
1592
    char *endstr = NULL;
1593
 
1594
    BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
1595
    BAIL_IF(!org, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
1596
    BAIL_IF(*org == '\0', PHYSFS_ERR_INVALID_ARGUMENT, NULL);
1597
    BAIL_IF(!app, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
1598
    BAIL_IF(*app == '\0', PHYSFS_ERR_INVALID_ARGUMENT, NULL);
1599
 
1600
    allocator.Free(prefDir);
1601
    prefDir = __PHYSFS_platformCalcPrefDir(org, app);
1602
    BAIL_IF_ERRPASS(!prefDir, NULL);
1603
 
1604
    assert(strlen(prefDir) > 0);
1605
    endstr = prefDir + (strlen(prefDir) - 1);
1606
    assert(*endstr == dirsep);
1607
    *endstr = '\0';  /* mask out the final dirsep for now. */
1608
 
1609
    if (!__PHYSFS_platformStat(prefDir, &statbuf, 1))
1610
    {
1611
        for (ptr = strchr(prefDir, dirsep); ptr; ptr = strchr(ptr+1, dirsep))
1612
        {
1613
            *ptr = '\0';
1614
            __PHYSFS_platformMkDir(prefDir);
1615
            *ptr = dirsep;
1616
        } /* for */
1617
 
1618
        if (!__PHYSFS_platformMkDir(prefDir))
1619
        {
1620
            allocator.Free(prefDir);
1621
            prefDir = NULL;
1622
        } /* if */
1623
    } /* if */
1624
 
1625
    *endstr = dirsep;  /* readd the final dirsep. */
1626
 
1627
    return prefDir;
1628
} /* PHYSFS_getPrefDir */
1629
 
1630
 
1631
const char *PHYSFS_getBaseDir(void)
1632
{
1633
    return baseDir;   /* this is calculated in PHYSFS_init()... */
1634
} /* PHYSFS_getBaseDir */
1635
 
1636
 
1637
const char *__PHYSFS_getUserDir(void)  /* not deprecated internal version. */
1638
{
1639
    return userDir;   /* this is calculated in PHYSFS_init()... */
1640
} /* __PHYSFS_getUserDir */
1641
 
1642
 
1643
const char *PHYSFS_getUserDir(void)
1644
{
1645
    return __PHYSFS_getUserDir();
1646
} /* PHYSFS_getUserDir */
1647
 
1648
 
1649
const char *PHYSFS_getWriteDir(void)
1650
{
1651
    const char *retval = NULL;
1652
 
1653
    __PHYSFS_platformGrabMutex(stateLock);
1654
    if (writeDir != NULL)
1655
        retval = writeDir->dirName;
1656
    __PHYSFS_platformReleaseMutex(stateLock);
1657
 
1658
    return retval;
1659
} /* PHYSFS_getWriteDir */
1660
 
1661
 
1662
int PHYSFS_setWriteDir(const char *newDir)
1663
{
1664
    int retval = 1;
1665
 
1666
    __PHYSFS_platformGrabMutex(stateLock);
1667
 
1668
    if (writeDir != NULL)
1669
    {
1670
        BAIL_IF_MUTEX_ERRPASS(!freeDirHandle(writeDir, openWriteList),
1671
                            stateLock, 0);
1672
        writeDir = NULL;
1673
    } /* if */
1674
 
1675
    if (newDir != NULL)
1676
    {
1677
        writeDir = createDirHandle(NULL, newDir, NULL, 1);
1678
        retval = (writeDir != NULL);
1679
    } /* if */
1680
 
1681
    __PHYSFS_platformReleaseMutex(stateLock);
1682
 
1683
    return retval;
1684
} /* PHYSFS_setWriteDir */
1685
 
1686
 
1687
static int doMount(PHYSFS_Io *io, const char *fname,
1688
                   const char *mountPoint, int appendToPath)
1689
{
1690
    DirHandle *dh;
1691
    DirHandle *prev = NULL;
1692
    DirHandle *i;
1693
 
1694
    BAIL_IF(!fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1695
 
1696
    if (mountPoint == NULL)
1697
        mountPoint = "/";
1698
 
1699
    __PHYSFS_platformGrabMutex(stateLock);
1700
 
1701
    for (i = searchPath; i != NULL; i = i->next)
1702
    {
1703
        /* already in search path? */
1704
        if ((i->dirName != NULL) && (strcmp(fname, i->dirName) == 0))
1705
            BAIL_MUTEX_ERRPASS(stateLock, 1);
1706
        prev = i;
1707
    } /* for */
1708
 
1709
    dh = createDirHandle(io, fname, mountPoint, 0);
1710
    BAIL_IF_MUTEX_ERRPASS(!dh, stateLock, 0);
1711
 
1712
    if (appendToPath)
1713
    {
1714
        if (prev == NULL)
1715
            searchPath = dh;
1716
        else
1717
            prev->next = dh;
1718
    } /* if */
1719
    else
1720
    {
1721
        dh->next = searchPath;
1722
        searchPath = dh;
1723
    } /* else */
1724
 
1725
    __PHYSFS_platformReleaseMutex(stateLock);
1726
    return 1;
1727
} /* doMount */
1728
 
1729
 
1730
int PHYSFS_mountIo(PHYSFS_Io *io, const char *fname,
1731
                   const char *mountPoint, int appendToPath)
1732
{
1733
    BAIL_IF(!io, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1734
    BAIL_IF(!fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1735
    BAIL_IF(io->version != 0, PHYSFS_ERR_UNSUPPORTED, 0);
1736
    return doMount(io, fname, mountPoint, appendToPath);
1737
} /* PHYSFS_mountIo */
1738
 
1739
 
1740
int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len, void (*del)(void *),
1741
                       const char *fname, const char *mountPoint,
1742
                       int appendToPath)
1743
{
1744
    int retval = 0;
1745
    PHYSFS_Io *io = NULL;
1746
 
1747
    BAIL_IF(!buf, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1748
    BAIL_IF(!fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1749
 
1750
    io = __PHYSFS_createMemoryIo(buf, len, del);
1751
    BAIL_IF_ERRPASS(!io, 0);
1752
    retval = doMount(io, fname, mountPoint, appendToPath);
1753
    if (!retval)
1754
    {
1755
        /* docs say not to call (del) in case of failure, so cheat. */
1756
        MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
1757
        info->destruct = NULL;
1758
        io->destroy(io);
1759
    } /* if */
1760
 
1761
    return retval;
1762
} /* PHYSFS_mountMemory */
1763
 
1764
 
1765
int PHYSFS_mountHandle(PHYSFS_File *file, const char *fname,
1766
                       const char *mountPoint, int appendToPath)
1767
{
1768
    int retval = 0;
1769
    PHYSFS_Io *io = NULL;
1770
 
1771
    BAIL_IF(!file, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1772
    BAIL_IF(!fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1773
 
1774
    io = __PHYSFS_createHandleIo(file);
1775
    BAIL_IF_ERRPASS(!io, 0);
1776
    retval = doMount(io, fname, mountPoint, appendToPath);
1777
    if (!retval)
1778
    {
1779
        /* docs say not to destruct in case of failure, so cheat. */
1780
        io->opaque = NULL;
1781
        io->destroy(io);
1782
    } /* if */
1783
 
1784
    return retval;
1785
} /* PHYSFS_mountHandle */
1786
 
1787
 
1788
int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath)
1789
{
1790
    BAIL_IF(!newDir, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1791
    return doMount(NULL, newDir, mountPoint, appendToPath);
1792
} /* PHYSFS_mount */
1793
 
1794
 
1795
int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
1796
{
1797
    return PHYSFS_mount(newDir, NULL, appendToPath);
1798
} /* PHYSFS_addToSearchPath */
1799
 
1800
 
1801
int PHYSFS_removeFromSearchPath(const char *oldDir)
1802
{
1803
    return PHYSFS_unmount(oldDir);
1804
} /* PHYSFS_removeFromSearchPath */
1805
 
1806
 
1807
int PHYSFS_unmount(const char *oldDir)
1808
{
1809
    DirHandle *i;
1810
    DirHandle *prev = NULL;
1811
    DirHandle *next = NULL;
1812
 
1813
    BAIL_IF(oldDir == NULL, PHYSFS_ERR_INVALID_ARGUMENT, 0);
1814
 
1815
    __PHYSFS_platformGrabMutex(stateLock);
1816
    for (i = searchPath; i != NULL; i = i->next)
1817
    {
1818
        if (strcmp(i->dirName, oldDir) == 0)
1819
        {
1820
            next = i->next;
1821
            BAIL_IF_MUTEX_ERRPASS(!freeDirHandle(i, openReadList),
1822
                                stateLock, 0);
1823
 
1824
            if (prev == NULL)
1825
                searchPath = next;
1826
            else
1827
                prev->next = next;
1828
 
1829
            BAIL_MUTEX_ERRPASS(stateLock, 1);
1830
        } /* if */
1831
        prev = i;
1832
    } /* for */
1833
 
1834
    BAIL_MUTEX(PHYSFS_ERR_NOT_MOUNTED, stateLock, 0);
1835
} /* PHYSFS_unmount */
1836
 
1837
 
1838
char **PHYSFS_getSearchPath(void)
1839
{
1840
    return doEnumStringList(PHYSFS_getSearchPathCallback);
1841
} /* PHYSFS_getSearchPath */
1842
 
1843
 
1844
const char *PHYSFS_getMountPoint(const char *dir)
1845
{
1846
    DirHandle *i;
1847
    __PHYSFS_platformGrabMutex(stateLock);
1848
    for (i = searchPath; i != NULL; i = i->next)
1849
    {
1850
        if (strcmp(i->dirName, dir) == 0)
1851
        {
1852
            const char *retval = ((i->mountPoint) ? i->mountPoint : "/");
1853
            __PHYSFS_platformReleaseMutex(stateLock);
1854
            return retval;
1855
        } /* if */
1856
    } /* for */
1857
    __PHYSFS_platformReleaseMutex(stateLock);
1858
 
1859
    BAIL(PHYSFS_ERR_NOT_MOUNTED, NULL);
1860
} /* PHYSFS_getMountPoint */
1861
 
1862
 
1863
void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback callback, void *data)
1864
{
1865
    DirHandle *i;
1866
 
1867
    __PHYSFS_platformGrabMutex(stateLock);
1868
 
1869
    for (i = searchPath; i != NULL; i = i->next)
1870
        callback(data, i->dirName);
1871
 
1872
    __PHYSFS_platformReleaseMutex(stateLock);
1873
} /* PHYSFS_getSearchPathCallback */
1874
 
1875
 
1876
typedef struct setSaneCfgEnumData
1877
{
1878
    const char *archiveExt;
1879
    size_t archiveExtLen;
1880
    int archivesFirst;
1881
    PHYSFS_ErrorCode errcode;
1882
} setSaneCfgEnumData;
1883
 
1884
static PHYSFS_EnumerateCallbackResult setSaneCfgEnumCallback(void *_data,
1885
                                                const char *dir, const char *f)
1886
{
1887
    setSaneCfgEnumData *data = (setSaneCfgEnumData *) _data;
1888
    const size_t extlen = data->archiveExtLen;
1889
    const size_t l = strlen(f);
1890
    const char *ext;
1891
 
1892
    if ((l > extlen) && (f[l - extlen - 1] == '.'))
1893
    {
1894
        ext = f + (l - extlen);
1895
        if (PHYSFS_utf8stricmp(ext, data->archiveExt) == 0)
1896
        {
1897
            const char dirsep = __PHYSFS_platformDirSeparator;
1898
            const char *d = PHYSFS_getRealDir(f);
1899
            const size_t allocsize = strlen(d) + l + 2;
1900
            char *str = (char *) __PHYSFS_smallAlloc(allocsize);
1901
            if (str == NULL)
1902
                data->errcode = PHYSFS_ERR_OUT_OF_MEMORY;
1903
            else
1904
            {
1905
                snprintf(str, allocsize, "%s%c%s", d, dirsep, f);
1906
                if (!PHYSFS_mount(str, NULL, data->archivesFirst == 0))
1907
                    data->errcode = currentErrorCode();
1908
                __PHYSFS_smallFree(str);
1909
            } /* else */
1910
        } /* if */
1911
    } /* if */
1912
 
1913
    /* !!! FIXME: if we want to abort on errors... */
1914
    /*return (data->errcode != PHYSFS_ERR_OK) ? PHYSFS_ENUM_ERROR : PHYSFS_ENUM_OK;*/
1915
 
1916
    return PHYSFS_ENUM_OK;  /* keep going */
1917
} /* setSaneCfgEnumCallback */
1918
 
1919
 
1920
int PHYSFS_setSaneConfig(const char *organization, const char *appName,
1921
                         const char *archiveExt, int includeCdRoms,
1922
                         int archivesFirst)
1923
{
1924
    const char *basedir;
1925
    const char *prefdir;
1926
 
1927
    BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
1928
 
1929
    prefdir = PHYSFS_getPrefDir(organization, appName);
1930
    BAIL_IF_ERRPASS(!prefdir, 0);
1931
 
1932
    basedir = PHYSFS_getBaseDir();
1933
    BAIL_IF_ERRPASS(!basedir, 0);
1934
 
1935
    BAIL_IF(!PHYSFS_setWriteDir(prefdir), PHYSFS_ERR_NO_WRITE_DIR, 0);
1936
 
1937
    /* !!! FIXME: these can fail and we should report that... */
1938
 
1939
    /* Put write dir first in search path... */
1940
    PHYSFS_mount(prefdir, NULL, 0);
1941
 
1942
    /* Put base path on search path... */
1943
    PHYSFS_mount(basedir, NULL, 1);
1944
 
1945
    /* handle CD-ROMs... */
1946
    if (includeCdRoms)
1947
    {
1948
        char **cds = PHYSFS_getCdRomDirs();
1949
        char **i;
1950
        for (i = cds; *i != NULL; i++)
1951
            PHYSFS_mount(*i, NULL, 1);
1952
        PHYSFS_freeList(cds);
1953
    } /* if */
1954
 
1955
    /* Root out archives, and add them to search path... */
1956
    if (archiveExt != NULL)
1957
    {
1958
        setSaneCfgEnumData data;
1959
        memset(&data, '\0', sizeof (data));
1960
        data.archiveExt = archiveExt;
1961
        data.archiveExtLen = strlen(archiveExt);
1962
        data.archivesFirst = archivesFirst;
1963
        data.errcode = PHYSFS_ERR_OK;
1964
        if (!PHYSFS_enumerate("/", setSaneCfgEnumCallback, &data))
1965
        {
1966
            /* !!! FIXME: use this if we're reporting errors.
1967
            PHYSFS_ErrorCode errcode = currentErrorCode();
1968
            if (errcode == PHYSFS_ERR_APP_CALLBACK)
1969
                errcode = data->errcode; */
1970
        } /* if */
1971
    } /* if */
1972
 
1973
    return 1;
1974
} /* PHYSFS_setSaneConfig */
1975
 
1976
 
1977
void PHYSFS_permitSymbolicLinks(int allow)
1978
{
1979
    allowSymLinks = allow;
1980
} /* PHYSFS_permitSymbolicLinks */
1981
 
1982
 
1983
int PHYSFS_symbolicLinksPermitted(void)
1984
{
1985
    return allowSymLinks;
1986
} /* PHYSFS_symbolicLinksPermitted */
1987
 
1988
 
1989
/*
1990
 * Verify that (fname) (in platform-independent notation), in relation
1991
 *  to (h) is secure. That means that each element of fname is checked
1992
 *  for symlinks (if they aren't permitted). This also allows for quick
1993
 *  rejection of files that exist outside an archive's mountpoint.
1994
 *
1995
 * With some exceptions (like PHYSFS_mkdir(), which builds multiple subdirs
1996
 *  at a time), you should always pass zero for "allowMissing" for efficiency.
1997
 *
1998
 * (fname) must point to an output from sanitizePlatformIndependentPath(),
1999
 *  since it will make sure that path names are in the right format for
2000
 *  passing certain checks. It will also do checks for "insecure" pathnames
2001
 *  like ".." which should be done once instead of once per archive. This also
2002
 *  gives us license to treat (fname) as scratch space in this function.
2003
 *
2004
 * Returns non-zero if string is safe, zero if there's a security issue.
2005
 *  PHYSFS_getLastError() will specify what was wrong. (*fname) will be
2006
 *  updated to point past any mount point elements so it is prepared to
2007
 *  be used with the archiver directly.
2008
 */
2009
static int verifyPath(DirHandle *h, char **_fname, int allowMissing)
2010
{
2011
    char *fname = *_fname;
2012
    int retval = 1;
2013
    char *start;
2014
    char *end;
2015
 
2016
    if (*fname == '\0')  /* quick rejection. */
2017
        return 1;
2018
 
2019
    /* !!! FIXME: This codeblock sucks. */
2020
    if (h->mountPoint != NULL)  /* NULL mountpoint means "/". */
2021
    {
2022
        size_t mntpntlen = strlen(h->mountPoint);
2023
        size_t len = strlen(fname);
2024
        assert(mntpntlen > 1); /* root mount points should be NULL. */
2025
        /* not under the mountpoint, so skip this archive. */
2026
        BAIL_IF(len < mntpntlen-1, PHYSFS_ERR_NOT_FOUND, 0);
2027
        /* !!! FIXME: Case insensitive? */
2028
        retval = strncmp(h->mountPoint, fname, mntpntlen-1);
2029
        BAIL_IF(retval != 0, PHYSFS_ERR_NOT_FOUND, 0);
2030
        if (len > mntpntlen-1)  /* corner case... */
2031
            BAIL_IF(fname[mntpntlen-1]!='/', PHYSFS_ERR_NOT_FOUND, 0);
2032
        fname += mntpntlen-1;  /* move to start of actual archive path. */
2033
        if (*fname == '/')
2034
            fname++;
2035
        *_fname = fname;  /* skip mountpoint for later use. */
2036
        retval = 1;  /* may be reset, below. */
2037
    } /* if */
2038
 
2039
    start = fname;
2040
    if (!allowSymLinks)
2041
    {
2042
        while (1)
2043
        {
2044
            PHYSFS_Stat statbuf;
2045
            int rc = 0;
2046
            end = strchr(start, '/');
2047
 
2048
            if (end != NULL) *end = '\0';
2049
            rc = h->funcs->stat(h->opaque, fname, &statbuf);
2050
            if (rc)
2051
                rc = (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK);
2052
            else if (currentErrorCode() == PHYSFS_ERR_NOT_FOUND)
2053
                retval = 0;
2054
 
2055
            if (end != NULL) *end = '/';
2056
 
2057
            /* insecure path (has a disallowed symlink in it)? */
2058
            BAIL_IF(rc, PHYSFS_ERR_SYMLINK_FORBIDDEN, 0);
2059
 
2060
            /* break out early if path element is missing. */
2061
            if (!retval)
2062
            {
2063
                /*
2064
                 * We need to clear it if it's the last element of the path,
2065
                 *  since this might be a non-existant file we're opening
2066
                 *  for writing...
2067
                 */
2068
                if ((end == NULL) || (allowMissing))
2069
                    retval = 1;
2070
                break;
2071
            } /* if */
2072
 
2073
            if (end == NULL)
2074
                break;
2075
 
2076
            start = end + 1;
2077
        } /* while */
2078
    } /* if */
2079
 
2080
    return retval;
2081
} /* verifyPath */
2082
 
2083
 
2084
static int doMkdir(const char *_dname, char *dname)
2085
{
2086
    DirHandle *h;
2087
    char *start;
2088
    char *end;
2089
    int retval = 0;
2090
    int exists = 1;  /* force existance check on first path element. */
2091
 
2092
    BAIL_IF_ERRPASS(!sanitizePlatformIndependentPath(_dname, dname), 0);
2093
 
2094
    __PHYSFS_platformGrabMutex(stateLock);
2095
    BAIL_IF_MUTEX(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, stateLock, 0);
2096
    h = writeDir;
2097
    BAIL_IF_MUTEX_ERRPASS(!verifyPath(h, &dname, 1), stateLock, 0);
2098
 
2099
    start = dname;
2100
    while (1)
2101
    {
2102
        end = strchr(start, '/');
2103
        if (end != NULL)
2104
            *end = '\0';
2105
 
2106
        /* only check for existance if all parent dirs existed, too... */
2107
        if (exists)
2108
        {
2109
            PHYSFS_Stat statbuf;
2110
            const int rc = h->funcs->stat(h->opaque, dname, &statbuf);
2111
            if ((!rc) && (currentErrorCode() == PHYSFS_ERR_NOT_FOUND))
2112
                exists = 0;
2113
            retval = ((rc) && (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY));
2114
        } /* if */
2115
 
2116
        if (!exists)
2117
            retval = h->funcs->mkdir(h->opaque, dname);
2118
 
2119
        if (!retval)
2120
            break;
2121
 
2122
        if (end == NULL)
2123
            break;
2124
 
2125
        *end = '/';
2126
        start = end + 1;
2127
    } /* while */
2128
 
2129
    __PHYSFS_platformReleaseMutex(stateLock);
2130
    return retval;
2131
} /* doMkdir */
2132
 
2133
 
2134
int PHYSFS_mkdir(const char *_dname)
2135
{
2136
    int retval = 0;
2137
    char *dname;
2138
    size_t len;
2139
 
2140
    BAIL_IF(!_dname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
2141
    len = strlen(_dname) + 1;
2142
    dname = (char *) __PHYSFS_smallAlloc(len);
2143
    BAIL_IF(!dname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
2144
    retval = doMkdir(_dname, dname);
2145
    __PHYSFS_smallFree(dname);
2146
    return retval;
2147
} /* PHYSFS_mkdir */
2148
 
2149
 
2150
static int doDelete(const char *_fname, char *fname)
2151
{
2152
    int retval;
2153
    DirHandle *h;
2154
    BAIL_IF_ERRPASS(!sanitizePlatformIndependentPath(_fname, fname), 0);
2155
 
2156
    __PHYSFS_platformGrabMutex(stateLock);
2157
 
2158
    BAIL_IF_MUTEX(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, stateLock, 0);
2159
    h = writeDir;
2160
    BAIL_IF_MUTEX_ERRPASS(!verifyPath(h, &fname, 0), stateLock, 0);
2161
    retval = h->funcs->remove(h->opaque, fname);
2162
 
2163
    __PHYSFS_platformReleaseMutex(stateLock);
2164
    return retval;
2165
} /* doDelete */
2166
 
2167
 
2168
int PHYSFS_delete(const char *_fname)
2169
{
2170
    int retval;
2171
    char *fname;
2172
    size_t len;
2173
 
2174
    BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
2175
    len = strlen(_fname) + 1;
2176
    fname = (char *) __PHYSFS_smallAlloc(len);
2177
    BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
2178
    retval = doDelete(_fname, fname);
2179
    __PHYSFS_smallFree(fname);
2180
    return retval;
2181
} /* PHYSFS_delete */
2182
 
2183
 
2184
static DirHandle *getRealDirHandle(const char *_fname)
2185
{
2186
    DirHandle *retval = NULL;
2187
    char *fname = NULL;
2188
    size_t len;
2189
 
2190
    BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
2191
    len = strlen(_fname) + 1;
2192
    fname = __PHYSFS_smallAlloc(len);
2193
    BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
2194
    if (sanitizePlatformIndependentPath(_fname, fname))
2195
    {
2196
        DirHandle *i;
2197
        __PHYSFS_platformGrabMutex(stateLock);
2198
        for (i = searchPath; i != NULL; i = i->next)
2199
        {
2200
            char *arcfname = fname;
2201
            if (partOfMountPoint(i, arcfname))
2202
            {
2203
                retval = i;
2204
                break;
2205
            } /* if */
2206
            else if (verifyPath(i, &arcfname, 0))
2207
            {
2208
                PHYSFS_Stat statbuf;
2209
                if (i->funcs->stat(i->opaque, arcfname, &statbuf))
2210
                {
2211
                    retval = i;
2212
                    break;
2213
                } /* if */
2214
            } /* if */
2215
        } /* for */
2216
        __PHYSFS_platformReleaseMutex(stateLock);
2217
    } /* if */
2218
 
2219
    __PHYSFS_smallFree(fname);
2220
    return retval;
2221
} /* getRealDirHandle */
2222
 
2223
const char *PHYSFS_getRealDir(const char *fname)
2224
{
2225
    DirHandle *dh = getRealDirHandle(fname);
2226
    return dh ? dh->dirName : NULL;
2227
} /* PHYSFS_getRealDir */
2228
 
2229
 
2230
static int locateInStringList(const char *str,
2231
                              char **list,
2232
                              PHYSFS_uint32 *pos)
2233
{
2234
    PHYSFS_uint32 len = *pos;
2235
    PHYSFS_uint32 half_len;
2236
    PHYSFS_uint32 lo = 0;
2237
    PHYSFS_uint32 middle;
2238
    int cmp;
2239
 
2240
    while (len > 0)
2241
    {
2242
        half_len = len >> 1;
2243
        middle = lo + half_len;
2244
        cmp = strcmp(list[middle], str);
2245
 
2246
        if (cmp == 0)  /* it's in the list already. */
2247
            return 1;
2248
        else if (cmp > 0)
2249
            len = half_len;
2250
        else
2251
        {
2252
            lo = middle + 1;
2253
            len -= half_len + 1;
2254
        } /* else */
2255
    } /* while */
2256
 
2257
    *pos = lo;
2258
    return 0;
2259
} /* locateInStringList */
2260
 
2261
 
2262
static PHYSFS_EnumerateCallbackResult enumFilesCallback(void *data,
2263
                                        const char *origdir, const char *str)
2264
{
2265
    PHYSFS_uint32 pos;
2266
    void *ptr;
2267
    char *newstr;
2268
    EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data;
2269
 
2270
    /*
2271
     * See if file is in the list already, and if not, insert it in there
2272
     *  alphabetically...
2273
     */
2274
    pos = pecd->size;
2275
    if (locateInStringList(str, pecd->list, &pos))
2276
        return PHYSFS_ENUM_OK;  /* already in the list, but keep going. */
2277
 
2278
    ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *));
2279
    newstr = (char *) allocator.Malloc(strlen(str) + 1);
2280
    if (ptr != NULL)
2281
        pecd->list = (char **) ptr;
2282
 
2283
    if ((ptr == NULL) || (newstr == NULL))
2284
    {
2285
        if (newstr)
2286
            allocator.Free(newstr);
2287
 
2288
        pecd->errcode = PHYSFS_ERR_OUT_OF_MEMORY;
2289
        return PHYSFS_ENUM_ERROR;  /* better luck next time. */
2290
    } /* if */
2291
 
2292
    strcpy(newstr, str);
2293
 
2294
    if (pos != pecd->size)
2295
    {
2296
        memmove(&pecd->list[pos+1], &pecd->list[pos],
2297
                 sizeof (char *) * ((pecd->size) - pos));
2298
    } /* if */
2299
 
2300
    pecd->list[pos] = newstr;
2301
    pecd->size++;
2302
 
2303
    return PHYSFS_ENUM_OK;
2304
} /* enumFilesCallback */
2305
 
2306
 
2307
char **PHYSFS_enumerateFiles(const char *path)
2308
{
2309
    EnumStringListCallbackData ecd;
2310
    memset(&ecd, '\0', sizeof (ecd));
2311
    ecd.list = (char **) allocator.Malloc(sizeof (char *));
2312
    BAIL_IF(!ecd.list, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
2313
    if (!PHYSFS_enumerate(path, enumFilesCallback, &ecd))
2314
    {
2315
        const PHYSFS_ErrorCode errcode = currentErrorCode();
2316
        PHYSFS_uint32 i;
2317
        for (i = 0; i < ecd.size; i++)
2318
            allocator.Free(ecd.list[i]);
2319
        allocator.Free(ecd.list);
2320
        BAIL_IF(errcode == PHYSFS_ERR_APP_CALLBACK, ecd.errcode, NULL);
2321
        return NULL;
2322
    } /* if */
2323
 
2324
    ecd.list[ecd.size] = NULL;
2325
    return ecd.list;
2326
} /* PHYSFS_enumerateFiles */
2327
 
2328
 
2329
/*
2330
 * Broke out to seperate function so we can use stack allocation gratuitously.
2331
 */
2332
static PHYSFS_EnumerateCallbackResult enumerateFromMountPoint(DirHandle *i,
2333
                                    const char *arcfname,
2334
                                    PHYSFS_EnumerateCallback callback,
2335
                                    const char *_fname, void *data)
2336
{
2337
    PHYSFS_EnumerateCallbackResult retval;
2338
    const size_t len = strlen(arcfname);
2339
    char *ptr = NULL;
2340
    char *end = NULL;
2341
    const size_t slen = strlen(i->mountPoint) + 1;
2342
    char *mountPoint = (char *) __PHYSFS_smallAlloc(slen);
2343
 
2344
    BAIL_IF(!mountPoint, PHYSFS_ERR_OUT_OF_MEMORY, PHYSFS_ENUM_ERROR);
2345
 
2346
    strcpy(mountPoint, i->mountPoint);
2347
    ptr = mountPoint + ((len) ? len + 1 : 0);
2348
    end = strchr(ptr, '/');
2349
    assert(end);  /* should always find a terminating '/'. */
2350
    *end = '\0';
2351
    retval = callback(data, _fname, ptr);
2352
    __PHYSFS_smallFree(mountPoint);
2353
 
2354
    BAIL_IF(retval == PHYSFS_ENUM_ERROR, PHYSFS_ERR_APP_CALLBACK, retval);
2355
    return retval;
2356
} /* enumerateFromMountPoint */
2357
 
2358
 
2359
typedef struct SymlinkFilterData
2360
{
2361
    PHYSFS_EnumerateCallback callback;
2362
    void *callbackData;
2363
    DirHandle *dirhandle;
2364
    const char *arcfname;
2365
    PHYSFS_ErrorCode errcode;
2366
} SymlinkFilterData;
2367
 
2368
static PHYSFS_EnumerateCallbackResult enumCallbackFilterSymLinks(void *_data,
2369
                                    const char *origdir, const char *fname)
2370
{
2371
    SymlinkFilterData *data = (SymlinkFilterData *) _data;
2372
    const DirHandle *dh = data->dirhandle;
2373
    const char *arcfname = data->arcfname;
2374
    PHYSFS_Stat statbuf;
2375
    const char *trimmedDir = (*arcfname == '/') ? (arcfname + 1) : arcfname;
2376
    const size_t slen = strlen(trimmedDir) + strlen(fname) + 2;
2377
    char *path = (char *) __PHYSFS_smallAlloc(slen);
2378
    PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
2379
 
2380
    if (path == NULL)
2381
    {
2382
        data->errcode = PHYSFS_ERR_OUT_OF_MEMORY;
2383
        return PHYSFS_ENUM_ERROR;
2384
    } /* if */
2385
 
2386
    snprintf(path, slen, "%s%s%s", trimmedDir, *trimmedDir ? "/" : "", fname);
2387
 
2388
    if (!dh->funcs->stat(dh->opaque, path, &statbuf))
2389
    {
2390
        data->errcode = currentErrorCode();
2391
        retval = PHYSFS_ENUM_ERROR;
2392
    } /* if */
2393
    else
2394
    {
2395
        /* Pass it on to the application if it's not a symlink. */
2396
        if (statbuf.filetype != PHYSFS_FILETYPE_SYMLINK)
2397
        {
2398
            retval = data->callback(data->callbackData, origdir, fname);
2399
            if (retval == PHYSFS_ENUM_ERROR)
2400
                data->errcode = PHYSFS_ERR_APP_CALLBACK;
2401
        } /* if */
2402
    } /* else */
2403
 
2404
    __PHYSFS_smallFree(path);
2405
 
2406
    return retval;
2407
} /* enumCallbackFilterSymLinks */
2408
 
2409
 
2410
int PHYSFS_enumerate(const char *_fn, PHYSFS_EnumerateCallback cb, void *data)
2411
{
2412
    PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
2413
    size_t len;
2414
    char *fname;
2415
 
2416
    BAIL_IF(!_fn, PHYSFS_ERR_INVALID_ARGUMENT, 0);
2417
    BAIL_IF(!cb, PHYSFS_ERR_INVALID_ARGUMENT, 0);
2418
 
2419
    len = strlen(_fn) + 1;
2420
    fname = (char *) __PHYSFS_smallAlloc(len);
2421
    BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
2422
 
2423
    if (!sanitizePlatformIndependentPath(_fn, fname))
2424
        retval = PHYSFS_ENUM_STOP;
2425
    else
2426
    {
2427
        DirHandle *i;
2428
        SymlinkFilterData filterdata;
2429
 
2430
        __PHYSFS_platformGrabMutex(stateLock);
2431
 
2432
        if (!allowSymLinks)
2433
        {
2434
            memset(&filterdata, '\0', sizeof (filterdata));
2435
            filterdata.callback = cb;
2436
            filterdata.callbackData = data;
2437
        } /* if */
2438
 
2439
        for (i = searchPath; (retval == PHYSFS_ENUM_OK) && i; i = i->next)
2440
        {
2441
            char *arcfname = fname;
2442
 
2443
            if (partOfMountPoint(i, arcfname))
2444
                retval = enumerateFromMountPoint(i, arcfname, cb, _fn, data);
2445
 
2446
            else if (verifyPath(i, &arcfname, 0))
2447
            {
2448
                PHYSFS_Stat statbuf;
2449
                if (!i->funcs->stat(i->opaque, arcfname, &statbuf))
2450
                {
2451
                    if (currentErrorCode() == PHYSFS_ERR_NOT_FOUND)
2452
                        continue;  /* no such dir in this archive, skip it. */
2453
                } /* if */
2454
 
2455
                if (statbuf.filetype != PHYSFS_FILETYPE_DIRECTORY)
2456
                    continue;  /* not a directory in this archive, skip it. */
2457
 
2458
                else if ((!allowSymLinks) && (i->funcs->info.supportsSymlinks))
2459
                {
2460
                    filterdata.dirhandle = i;
2461
                    filterdata.arcfname = arcfname;
2462
                    filterdata.errcode = PHYSFS_ERR_OK;
2463
                    retval = i->funcs->enumerate(i->opaque, arcfname,
2464
                                                 enumCallbackFilterSymLinks,
2465
                                                 _fn, &filterdata);
2466
                    if (retval == PHYSFS_ENUM_ERROR)
2467
                    {
2468
                        if (currentErrorCode() == PHYSFS_ERR_APP_CALLBACK)
2469
                            PHYSFS_setErrorCode(filterdata.errcode);
2470
                    } /* if */
2471
                } /* else if */
2472
                else
2473
                {
2474
                    retval = i->funcs->enumerate(i->opaque, arcfname,
2475
                                                 cb, _fn, data);
2476
                } /* else */
2477
            } /* else if */
2478
        } /* for */
2479
 
2480
        __PHYSFS_platformReleaseMutex(stateLock);
2481
    } /* if */
2482
 
2483
    __PHYSFS_smallFree(fname);
2484
 
2485
    return (retval == PHYSFS_ENUM_ERROR) ? 0 : 1;
2486
} /* PHYSFS_enumerate */
2487
 
2488
 
2489
typedef struct
2490
{
2491
    PHYSFS_EnumFilesCallback callback;
2492
    void *data;
2493
} LegacyEnumFilesCallbackData;
2494
 
2495
static PHYSFS_EnumerateCallbackResult enumFilesCallbackAlwaysSucceed(void *d,
2496
                                    const char *origdir, const char *fname)
2497
{
2498
    LegacyEnumFilesCallbackData *cbdata = (LegacyEnumFilesCallbackData *) d;
2499
    cbdata->callback(cbdata->data, origdir, fname);
2500
    return PHYSFS_ENUM_OK;
2501
} /* enumFilesCallbackAlwaysSucceed */
2502
 
2503
void PHYSFS_enumerateFilesCallback(const char *fname,
2504
                                   PHYSFS_EnumFilesCallback callback,
2505
                                   void *data)
2506
{
2507
    LegacyEnumFilesCallbackData cbdata;
2508
    cbdata.callback = callback;
2509
    cbdata.data = data;
2510
    (void) PHYSFS_enumerate(fname, enumFilesCallbackAlwaysSucceed, &cbdata);
2511
} /* PHYSFS_enumerateFilesCallback */
2512
 
2513
 
2514
int PHYSFS_exists(const char *fname)
2515
{
2516
    return (getRealDirHandle(fname) != NULL);
2517
} /* PHYSFS_exists */
2518
 
2519
 
2520
PHYSFS_sint64 PHYSFS_getLastModTime(const char *fname)
2521
{
2522
    PHYSFS_Stat statbuf;
2523
    BAIL_IF_ERRPASS(!PHYSFS_stat(fname, &statbuf), -1);
2524
    return statbuf.modtime;
2525
} /* PHYSFS_getLastModTime */
2526
 
2527
 
2528
int PHYSFS_isDirectory(const char *fname)
2529
{
2530
    PHYSFS_Stat statbuf;
2531
    BAIL_IF_ERRPASS(!PHYSFS_stat(fname, &statbuf), 0);
2532
    return (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY);
2533
} /* PHYSFS_isDirectory */
2534
 
2535
 
2536
int PHYSFS_isSymbolicLink(const char *fname)
2537
{
2538
    PHYSFS_Stat statbuf;
2539
    BAIL_IF_ERRPASS(!PHYSFS_stat(fname, &statbuf), 0);
2540
    return (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK);
2541
} /* PHYSFS_isSymbolicLink */
2542
 
2543
 
2544
static PHYSFS_File *doOpenWrite(const char *_fname, int appending)
2545
{
2546
    FileHandle *fh = NULL;
2547
    size_t len;
2548
    char *fname;
2549
 
2550
    BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
2551
    len = strlen(_fname) + 1;
2552
    fname = (char *) __PHYSFS_smallAlloc(len);
2553
    BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
2554
 
2555
    if (sanitizePlatformIndependentPath(_fname, fname))
2556
    {
2557
        PHYSFS_Io *io = NULL;
2558
        DirHandle *h = NULL;
2559
        const PHYSFS_Archiver *f;
2560
 
2561
        __PHYSFS_platformGrabMutex(stateLock);
2562
 
2563
        GOTO_IF(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, doOpenWriteEnd);
2564
 
2565
        h = writeDir;
2566
        GOTO_IF_ERRPASS(!verifyPath(h, &fname, 0), doOpenWriteEnd);
2567
 
2568
        f = h->funcs;
2569
        if (appending)
2570
            io = f->openAppend(h->opaque, fname);
2571
        else
2572
            io = f->openWrite(h->opaque, fname);
2573
 
2574
        GOTO_IF_ERRPASS(!io, doOpenWriteEnd);
2575
 
2576
        fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
2577
        if (fh == NULL)
2578
        {
2579
            io->destroy(io);
2580
            GOTO(PHYSFS_ERR_OUT_OF_MEMORY, doOpenWriteEnd);
2581
        } /* if */
2582
        else
2583
        {
2584
            memset(fh, '\0', sizeof (FileHandle));
2585
            fh->io = io;
2586
            fh->dirHandle = h;
2587
            fh->next = openWriteList;
2588
            openWriteList = fh;
2589
        } /* else */
2590
 
2591
        doOpenWriteEnd:
2592
        __PHYSFS_platformReleaseMutex(stateLock);
2593
    } /* if */
2594
 
2595
    __PHYSFS_smallFree(fname);
2596
    return ((PHYSFS_File *) fh);
2597
} /* doOpenWrite */
2598
 
2599
 
2600
PHYSFS_File *PHYSFS_openWrite(const char *filename)
2601
{
2602
    return doOpenWrite(filename, 0);
2603
} /* PHYSFS_openWrite */
2604
 
2605
 
2606
PHYSFS_File *PHYSFS_openAppend(const char *filename)
2607
{
2608
    return doOpenWrite(filename, 1);
2609
} /* PHYSFS_openAppend */
2610
 
2611
 
2612
PHYSFS_File *PHYSFS_openRead(const char *_fname)
2613
{
2614
    FileHandle *fh = NULL;
2615
    char *fname;
2616
    size_t len;
2617
 
2618
    BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
2619
    len = strlen(_fname) + 1;
2620
    fname = (char *) __PHYSFS_smallAlloc(len);
2621
    BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
2622
 
2623
    if (sanitizePlatformIndependentPath(_fname, fname))
2624
    {
2625
        DirHandle *i = NULL;
2626
        PHYSFS_Io *io = NULL;
2627
 
2628
        __PHYSFS_platformGrabMutex(stateLock);
2629
 
2630
        GOTO_IF(!searchPath, PHYSFS_ERR_NOT_FOUND, openReadEnd);
2631
 
2632
        for (i = searchPath; i != NULL; i = i->next)
2633
        {
2634
            char *arcfname = fname;
2635
            if (verifyPath(i, &arcfname, 0))
2636
            {
2637
                io = i->funcs->openRead(i->opaque, arcfname);
2638
                if (io)
2639
                    break;
2640
            } /* if */
2641
        } /* for */
2642
 
2643
        GOTO_IF_ERRPASS(!io, openReadEnd);
2644
 
2645
        fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
2646
        if (fh == NULL)
2647
        {
2648
            io->destroy(io);
2649
            GOTO(PHYSFS_ERR_OUT_OF_MEMORY, openReadEnd);
2650
        } /* if */
2651
 
2652
        memset(fh, '\0', sizeof (FileHandle));
2653
        fh->io = io;
2654
        fh->forReading = 1;
2655
        fh->dirHandle = i;
2656
        fh->next = openReadList;
2657
        openReadList = fh;
2658
 
2659
        openReadEnd:
2660
        __PHYSFS_platformReleaseMutex(stateLock);
2661
    } /* if */
2662
 
2663
    __PHYSFS_smallFree(fname);
2664
    return ((PHYSFS_File *) fh);
2665
} /* PHYSFS_openRead */
2666
 
2667
 
2668
static int closeHandleInOpenList(FileHandle **list, FileHandle *handle)
2669
{
2670
    FileHandle *prev = NULL;
2671
    FileHandle *i;
2672
 
2673
    for (i = *list; i != NULL; i = i->next)
2674
    {
2675
        if (i == handle)  /* handle is in this list? */
2676
        {
2677
            PHYSFS_Io *io = handle->io;
2678
            PHYSFS_uint8 *tmp = handle->buffer;
2679
 
2680
            /* send our buffer to io... */
2681
            if (!handle->forReading)
2682
            {
2683
                if (!PHYSFS_flush((PHYSFS_File *) handle))
2684
                    return -1;
2685
 
2686
                /* ...then have io send it to the disk... */
2687
                else if (io->flush && !io->flush(io))
2688
                    return -1;
2689
            } /* if */
2690
 
2691
            /* ...then close the underlying file. */
2692
            io->destroy(io);
2693
 
2694
            if (tmp != NULL)  /* free any associated buffer. */
2695
                allocator.Free(tmp);
2696
 
2697
            if (prev == NULL)
2698
                *list = handle->next;
2699
            else
2700
                prev->next = handle->next;
2701
 
2702
            allocator.Free(handle);
2703
            return 1;
2704
        } /* if */
2705
        prev = i;
2706
    } /* for */
2707
 
2708
    return 0;
2709
} /* closeHandleInOpenList */
2710
 
2711
 
2712
int PHYSFS_close(PHYSFS_File *_handle)
2713
{
2714
    FileHandle *handle = (FileHandle *) _handle;
2715
    int rc;
2716
 
2717
    __PHYSFS_platformGrabMutex(stateLock);
2718
 
2719
    /* -1 == close failure. 0 == not found. 1 == success. */
2720
    rc = closeHandleInOpenList(&openReadList, handle);
2721
    BAIL_IF_MUTEX_ERRPASS(rc == -1, stateLock, 0);
2722
    if (!rc)
2723
    {
2724
        rc = closeHandleInOpenList(&openWriteList, handle);
2725
        BAIL_IF_MUTEX_ERRPASS(rc == -1, stateLock, 0);
2726
    } /* if */
2727
 
2728
    __PHYSFS_platformReleaseMutex(stateLock);
2729
    BAIL_IF(!rc, PHYSFS_ERR_INVALID_ARGUMENT, 0);
2730
    return 1;
2731
} /* PHYSFS_close */
2732
 
2733
 
2734
static PHYSFS_sint64 doBufferedRead(FileHandle *fh, void *_buffer, size_t len)
2735
{
2736
    PHYSFS_uint8 *buffer = (PHYSFS_uint8 *) _buffer;
2737
    PHYSFS_sint64 retval = 0;
2738
 
2739
    while (len > 0)
2740
    {
2741
        const size_t avail = fh->buffill - fh->bufpos;
2742
        if (avail > 0)  /* data available in the buffer. */
2743
        {
2744
            const size_t cpy = (len < avail) ? len : avail;
2745
            memcpy(buffer, fh->buffer + fh->bufpos, cpy);
2746
            assert(len >= cpy);
2747
            buffer += cpy;
2748
            len -= cpy;
2749
            fh->bufpos += cpy;
2750
            retval += cpy;
2751
        } /* if */
2752
 
2753
        else   /* buffer is empty, refill it. */
2754
        {
2755
            PHYSFS_Io *io = fh->io;
2756
            const PHYSFS_sint64 rc = io->read(io, fh->buffer, fh->bufsize);
2757
            fh->bufpos = 0;
2758
            if (rc > 0)
2759
                fh->buffill = (size_t) rc;
2760
            else
2761
            {
2762
                fh->buffill = 0;
2763
                if (retval == 0)  /* report already-read data, or failure. */
2764
                    retval = rc;
2765
                break;
2766
            } /* else */
2767
        } /* else */
2768
    } /* while */
2769
 
2770
    return retval;
2771
} /* doBufferedRead */
2772
 
2773
 
2774
PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, void *buffer,
2775
                          PHYSFS_uint32 size, PHYSFS_uint32 count)
2776
{
2777
    const PHYSFS_uint64 len = ((PHYSFS_uint64) size) * ((PHYSFS_uint64) count);
2778
    const PHYSFS_sint64 retval = PHYSFS_readBytes(handle, buffer, len);
2779
    return ( (retval <= 0) ? retval : (retval / ((PHYSFS_sint64) size)) );
2780
} /* PHYSFS_read */
2781
 
2782
 
2783
PHYSFS_sint64 PHYSFS_readBytes(PHYSFS_File *handle, void *buffer,
2784
                               PHYSFS_uint64 _len)
2785
{
2786
    const size_t len = (size_t) _len;
2787
    FileHandle *fh = (FileHandle *) handle;
2788
 
2789
#ifdef PHYSFS_NO_64BIT_SUPPORT
2790
    const PHYSFS_uint64 maxlen = __PHYSFS_UI64(0x7FFFFFFF);
2791
#else
2792
    const PHYSFS_uint64 maxlen = __PHYSFS_UI64(0x7FFFFFFFFFFFFFFF);
2793
#endif
2794
 
2795
    if (!__PHYSFS_ui64FitsAddressSpace(_len))
2796
        BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
2797
 
2798
    BAIL_IF(_len > maxlen, PHYSFS_ERR_INVALID_ARGUMENT, -1);
2799
    BAIL_IF(!fh->forReading, PHYSFS_ERR_OPEN_FOR_WRITING, -1);
2800
    BAIL_IF_ERRPASS(len == 0, 0);
2801
    if (fh->buffer)
2802
        return doBufferedRead(fh, buffer, len);
2803
 
2804
    return fh->io->read(fh->io, buffer, len);
2805
} /* PHYSFS_readBytes */
2806
 
2807
 
2808
static PHYSFS_sint64 doBufferedWrite(PHYSFS_File *handle, const void *buffer,
2809
                                     const size_t len)
2810
{
2811
    FileHandle *fh = (FileHandle *) handle;
2812
 
2813
    /* whole thing fits in the buffer? */
2814
    if ((fh->buffill + len) < fh->bufsize)
2815
    {
2816
        memcpy(fh->buffer + fh->buffill, buffer, len);
2817
        fh->buffill += len;
2818
        return (PHYSFS_sint64) len;
2819
    } /* if */
2820
 
2821
    /* would overflow buffer. Flush and then write the new objects, too. */
2822
    BAIL_IF_ERRPASS(!PHYSFS_flush(handle), -1);
2823
    return fh->io->write(fh->io, buffer, len);
2824
} /* doBufferedWrite */
2825
 
2826
 
2827
PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, const void *buffer,
2828
                           PHYSFS_uint32 size, PHYSFS_uint32 count)
2829
{
2830
    const PHYSFS_uint64 len = ((PHYSFS_uint64) size) * ((PHYSFS_uint64) count);
2831
    const PHYSFS_sint64 retval = PHYSFS_writeBytes(handle, buffer, len);
2832
    return ( (retval <= 0) ? retval : (retval / ((PHYSFS_sint64) size)) );
2833
} /* PHYSFS_write */
2834
 
2835
 
2836
PHYSFS_sint64 PHYSFS_writeBytes(PHYSFS_File *handle, const void *buffer,
2837
                                PHYSFS_uint64 _len)
2838
{
2839
    const size_t len = (size_t) _len;
2840
    FileHandle *fh = (FileHandle *) handle;
2841
 
2842
#ifdef PHYSFS_NO_64BIT_SUPPORT
2843
    const PHYSFS_uint64 maxlen = __PHYSFS_UI64(0x7FFFFFFF);
2844
#else
2845
    const PHYSFS_uint64 maxlen = __PHYSFS_UI64(0x7FFFFFFFFFFFFFFF);
2846
#endif
2847
 
2848
    if (!__PHYSFS_ui64FitsAddressSpace(_len))
2849
        BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
2850
 
2851
    BAIL_IF(_len > maxlen, PHYSFS_ERR_INVALID_ARGUMENT, -1);
2852
    BAIL_IF(fh->forReading, PHYSFS_ERR_OPEN_FOR_READING, -1);
2853
    BAIL_IF_ERRPASS(len == 0, 0);
2854
    if (fh->buffer)
2855
        return doBufferedWrite(handle, buffer, len);
2856
 
2857
    return fh->io->write(fh->io, buffer, len);
2858
} /* PHYSFS_write */
2859
 
2860
 
2861
int PHYSFS_eof(PHYSFS_File *handle)
2862
{
2863
    FileHandle *fh = (FileHandle *) handle;
2864
 
2865
    if (!fh->forReading)  /* never EOF on files opened for write/append. */
2866
        return 0;
2867
 
2868
    /* can't be eof if buffer isn't empty */
2869
    if (fh->bufpos == fh->buffill)
2870
    {
2871
        /* check the Io. */
2872
        PHYSFS_Io *io = fh->io;
2873
        const PHYSFS_sint64 pos = io->tell(io);
2874
        const PHYSFS_sint64 len = io->length(io);
2875
        if ((pos < 0) || (len < 0))
2876
            return 0;  /* beats me. */
2877
        return (pos >= len);
2878
    } /* if */
2879
 
2880
    return 0;
2881
} /* PHYSFS_eof */
2882
 
2883
 
2884
PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle)
2885
{
2886
    FileHandle *fh = (FileHandle *) handle;
2887
    const PHYSFS_sint64 pos = fh->io->tell(fh->io);
2888
    const PHYSFS_sint64 retval = fh->forReading ?
2889
                                 (pos - fh->buffill) + fh->bufpos :
2890
                                 (pos + fh->buffill);
2891
    return retval;
2892
} /* PHYSFS_tell */
2893
 
2894
 
2895
int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos)
2896
{
2897
    FileHandle *fh = (FileHandle *) handle;
2898
    BAIL_IF_ERRPASS(!PHYSFS_flush(handle), 0);
2899
 
2900
    if (fh->buffer && fh->forReading)
2901
    {
2902
        /* avoid throwing away our precious buffer if seeking within it. */
2903
        PHYSFS_sint64 offset = pos - PHYSFS_tell(handle);
2904
        if ( /* seeking within the already-buffered range? */
2905
             /* forward? */
2906
            ((offset >= 0) && (((size_t)offset) <= fh->buffill-fh->bufpos)) ||
2907
            /* backward? */
2908
            ((offset < 0) && (((size_t) -offset) <= fh->bufpos)) )
2909
        {
2910
            fh->bufpos = (size_t) (((PHYSFS_sint64) fh->bufpos) + offset);
2911
            return 1; /* successful seek */
2912
        } /* if */
2913
    } /* if */
2914
 
2915
    /* we have to fall back to a 'raw' seek. */
2916
    fh->buffill = fh->bufpos = 0;
2917
    return fh->io->seek(fh->io, pos);
2918
} /* PHYSFS_seek */
2919
 
2920
 
2921
PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle)
2922
{
2923
    PHYSFS_Io *io = ((FileHandle *) handle)->io;
2924
    return io->length(io);
2925
} /* PHYSFS_filelength */
2926
 
2927
 
2928
int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 _bufsize)
2929
{
2930
    FileHandle *fh = (FileHandle *) handle;
2931
    const size_t bufsize = (size_t) _bufsize;
2932
 
2933
    if (!__PHYSFS_ui64FitsAddressSpace(_bufsize))
2934
        BAIL(PHYSFS_ERR_INVALID_ARGUMENT, 0);
2935
 
2936
    BAIL_IF_ERRPASS(!PHYSFS_flush(handle), 0);
2937
 
2938
    /*
2939
     * For reads, we need to move the file pointer to where it would be
2940
     *  if we weren't buffering, so that the next read will get the
2941
     *  right chunk of stuff from the file. PHYSFS_flush() handles writes.
2942
     */
2943
    if ((fh->forReading) && (fh->buffill != fh->bufpos))
2944
    {
2945
        PHYSFS_uint64 pos;
2946
        const PHYSFS_sint64 curpos = fh->io->tell(fh->io);
2947
        BAIL_IF_ERRPASS(curpos == -1, 0);
2948
        pos = ((curpos - fh->buffill) + fh->bufpos);
2949
        BAIL_IF_ERRPASS(!fh->io->seek(fh->io, pos), 0);
2950
    } /* if */
2951
 
2952
    if (bufsize == 0)  /* delete existing buffer. */
2953
    {
2954
        if (fh->buffer)
2955
        {
2956
            allocator.Free(fh->buffer);
2957
            fh->buffer = NULL;
2958
        } /* if */
2959
    } /* if */
2960
 
2961
    else
2962
    {
2963
        PHYSFS_uint8 *newbuf;
2964
        newbuf = (PHYSFS_uint8 *) allocator.Realloc(fh->buffer, bufsize);
2965
        BAIL_IF(!newbuf, PHYSFS_ERR_OUT_OF_MEMORY, 0);
2966
        fh->buffer = newbuf;
2967
    } /* else */
2968
 
2969
    fh->bufsize = bufsize;
2970
    fh->buffill = fh->bufpos = 0;
2971
    return 1;
2972
} /* PHYSFS_setBuffer */
2973
 
2974
 
2975
int PHYSFS_flush(PHYSFS_File *handle)
2976
{
2977
    FileHandle *fh = (FileHandle *) handle;
2978
    PHYSFS_Io *io;
2979
    PHYSFS_sint64 rc;
2980
 
2981
    if ((fh->forReading) || (fh->bufpos == fh->buffill))
2982
        return 1;  /* open for read or buffer empty are successful no-ops. */
2983
 
2984
    /* dump buffer to disk. */
2985
    io = fh->io;
2986
    rc = io->write(io, fh->buffer + fh->bufpos, fh->buffill - fh->bufpos);
2987
    BAIL_IF_ERRPASS(rc <= 0, 0);
2988
    fh->bufpos = fh->buffill = 0;
2989
    return 1;
2990
} /* PHYSFS_flush */
2991
 
2992
 
2993
int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat)
2994
{
2995
    int retval = 0;
2996
    char *fname;
2997
    size_t len;
2998
 
2999
    BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
3000
    BAIL_IF(!stat, PHYSFS_ERR_INVALID_ARGUMENT, 0);
3001
    len = strlen(_fname) + 1;
3002
    fname = (char *) __PHYSFS_smallAlloc(len);
3003
    BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
3004
 
3005
    /* set some sane defaults... */
3006
    stat->filesize = -1;
3007
    stat->modtime = -1;
3008
    stat->createtime = -1;
3009
    stat->accesstime = -1;
3010
    stat->filetype = PHYSFS_FILETYPE_OTHER;
3011
    stat->readonly = 1;
3012
 
3013
    if (sanitizePlatformIndependentPath(_fname, fname))
3014
    {
3015
        if (*fname == '\0')
3016
        {
3017
            stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
3018
            stat->readonly = !writeDir; /* Writeable if we have a writeDir */
3019
            retval = 1;
3020
        } /* if */
3021
        else
3022
        {
3023
            DirHandle *i;
3024
            int exists = 0;
3025
            __PHYSFS_platformGrabMutex(stateLock);
3026
            for (i = searchPath; ((i != NULL) && (!exists)); i = i->next)
3027
            {
3028
                char *arcfname = fname;
3029
                exists = partOfMountPoint(i, arcfname);
3030
                if (exists)
3031
                {
3032
                    stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
3033
                    stat->readonly = 1;
3034
                    retval = 1;
3035
                } /* if */
3036
                else if (verifyPath(i, &arcfname, 0))
3037
                {
3038
                    retval = i->funcs->stat(i->opaque, arcfname, stat);
3039
                    if ((retval) || (currentErrorCode() != PHYSFS_ERR_NOT_FOUND))
3040
                        exists = 1;
3041
                } /* else if */
3042
            } /* for */
3043
            __PHYSFS_platformReleaseMutex(stateLock);
3044
        } /* else */
3045
    } /* if */
3046
 
3047
    __PHYSFS_smallFree(fname);
3048
    return retval;
3049
} /* PHYSFS_stat */
3050
 
3051
 
3052
int __PHYSFS_readAll(PHYSFS_Io *io, void *buf, const size_t _len)
3053
{
3054
    const PHYSFS_uint64 len = (PHYSFS_uint64) _len;
3055
    return (io->read(io, buf, len) == len);
3056
} /* __PHYSFS_readAll */
3057
 
3058
 
3059
void *__PHYSFS_initSmallAlloc(void *ptr, const size_t len)
3060
{
3061
    void *useHeap = ((ptr == NULL) ? ((void *) 1) : ((void *) 0));
3062
    if (useHeap)  /* too large for stack allocation or alloca() failed. */
3063
        ptr = allocator.Malloc(len+sizeof (void *));
3064
 
3065
    if (ptr != NULL)
3066
    {
3067
        void **retval = (void **) ptr;
3068
        /*printf("%s alloc'd (%lld) bytes at (%p).\n",
3069
                useHeap ? "heap" : "stack", (long long) len, ptr);*/
3070
        *retval = useHeap;
3071
        return retval + 1;
3072
    } /* if */
3073
 
3074
    return NULL;  /* allocation failed. */
3075
} /* __PHYSFS_initSmallAlloc */
3076
 
3077
 
3078
void __PHYSFS_smallFree(void *ptr)
3079
{
3080
    if (ptr != NULL)
3081
    {
3082
        void **block = ((void **) ptr) - 1;
3083
        const int useHeap = (*block != NULL);
3084
        if (useHeap)
3085
            allocator.Free(block);
3086
        /*printf("%s free'd (%p).\n", useHeap ? "heap" : "stack", block);*/
3087
    } /* if */
3088
} /* __PHYSFS_smallFree */
3089
 
3090
 
3091
int PHYSFS_setAllocator(const PHYSFS_Allocator *a)
3092
{
3093
    BAIL_IF(initialized, PHYSFS_ERR_IS_INITIALIZED, 0);
3094
    externalAllocator = (a != NULL);
3095
    if (externalAllocator)
3096
        memcpy(&allocator, a, sizeof (PHYSFS_Allocator));
3097
 
3098
    return 1;
3099
} /* PHYSFS_setAllocator */
3100
 
3101
 
3102
const PHYSFS_Allocator *PHYSFS_getAllocator(void)
3103
{
3104
    BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, NULL);
3105
    return &allocator;
3106
} /* PHYSFS_getAllocator */
3107
 
3108
 
3109
static void *mallocAllocatorMalloc(PHYSFS_uint64 s)
3110
{
3111
    if (!__PHYSFS_ui64FitsAddressSpace(s))
3112
        BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
3113
    #undef malloc
3114
    return malloc((size_t) s);
3115
} /* mallocAllocatorMalloc */
3116
 
3117
 
3118
static void *mallocAllocatorRealloc(void *ptr, PHYSFS_uint64 s)
3119
{
3120
    if (!__PHYSFS_ui64FitsAddressSpace(s))
3121
        BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
3122
    #undef realloc
3123
    return realloc(ptr, (size_t) s);
3124
} /* mallocAllocatorRealloc */
3125
 
3126
 
3127
static void mallocAllocatorFree(void *ptr)
3128
{
3129
    #undef free
3130
    free(ptr);
3131
} /* mallocAllocatorFree */
3132
 
3133
 
3134
static void setDefaultAllocator(void)
3135
{
3136
    assert(!externalAllocator);
3137
    allocator.Init = NULL;
3138
    allocator.Deinit = NULL;
3139
    allocator.Malloc = mallocAllocatorMalloc;
3140
    allocator.Realloc = mallocAllocatorRealloc;
3141
    allocator.Free = mallocAllocatorFree;
3142
} /* setDefaultAllocator */
3143
 
3144
 
3145
int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen)
3146
{
3147
    static char rootpath[2] = { '/', '\0' };
3148
    size_t alloclen;
3149
 
3150
    assert(entrylen >= sizeof (__PHYSFS_DirTreeEntry));
3151
 
3152
    memset(dt, '\0', sizeof (*dt));
3153
 
3154
    dt->root = (__PHYSFS_DirTreeEntry *) allocator.Malloc(entrylen);
3155
    BAIL_IF(!dt->root, PHYSFS_ERR_OUT_OF_MEMORY, 0);
3156
    memset(dt->root, '\0', entrylen);
3157
    dt->root->name = rootpath;
3158
    dt->root->isdir = 1;
3159
    dt->hashBuckets = 64;
3160
    if (!dt->hashBuckets)
3161
        dt->hashBuckets = 1;
3162
    dt->entrylen = entrylen;
3163
 
3164
    alloclen = dt->hashBuckets * sizeof (__PHYSFS_DirTreeEntry *);
3165
    dt->hash = (__PHYSFS_DirTreeEntry **) allocator.Malloc(alloclen);
3166
    BAIL_IF(!dt->hash, PHYSFS_ERR_OUT_OF_MEMORY, 0);
3167
    memset(dt->hash, '\0', alloclen);
3168
 
3169
    return 1;
3170
} /* __PHYSFS_DirTreeInit */
3171
 
3172
 
3173
static inline PHYSFS_uint32 hashPathName(__PHYSFS_DirTree *dt, const char *name)
3174
{
3175
    return __PHYSFS_hashString(name, strlen(name)) % dt->hashBuckets;
3176
} /* hashPathName */
3177
 
3178
 
3179
/* Fill in missing parent directories. */
3180
static __PHYSFS_DirTreeEntry *addAncestors(__PHYSFS_DirTree *dt, char *name)
3181
{
3182
    __PHYSFS_DirTreeEntry *retval = dt->root;
3183
    char *sep = strrchr(name, '/');
3184
 
3185
    if (sep)
3186
    {
3187
        *sep = '\0';  /* chop off last piece. */
3188
        retval = (__PHYSFS_DirTreeEntry *) __PHYSFS_DirTreeFind(dt, name);
3189
 
3190
        if (retval != NULL)
3191
        {
3192
            *sep = '/';
3193
            BAIL_IF(!retval->isdir, PHYSFS_ERR_CORRUPT, NULL);
3194
            return retval;  /* already hashed. */
3195
        } /* if */
3196
 
3197
        /* okay, this is a new dir. Build and hash us. */
3198
        retval = (__PHYSFS_DirTreeEntry*)__PHYSFS_DirTreeAdd(dt, name, 1);
3199
        *sep = '/';
3200
    } /* if */
3201
 
3202
    return retval;
3203
} /* addAncestors */
3204
 
3205
 
3206
void *__PHYSFS_DirTreeAdd(__PHYSFS_DirTree *dt, char *name, const int isdir)
3207
{
3208
    __PHYSFS_DirTreeEntry *retval = __PHYSFS_DirTreeFind(dt, name);
3209
    if (!retval)
3210
    {
3211
        const size_t alloclen = strlen(name) + 1 + dt->entrylen;
3212
        PHYSFS_uint32 hashval;
3213
        __PHYSFS_DirTreeEntry *parent = addAncestors(dt, name);
3214
        BAIL_IF_ERRPASS(!parent, NULL);
3215
        assert(dt->entrylen >= sizeof (__PHYSFS_DirTreeEntry));
3216
        retval = (__PHYSFS_DirTreeEntry *) allocator.Malloc(alloclen);
3217
        BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
3218
        memset(retval, '\0', dt->entrylen);
3219
        retval->name = ((char *) retval) + dt->entrylen;
3220
        strcpy(retval->name, name);
3221
        hashval = hashPathName(dt, name);
3222
        retval->hashnext = dt->hash[hashval];
3223
        dt->hash[hashval] = retval;
3224
        retval->sibling = parent->children;
3225
        retval->isdir = isdir;
3226
        parent->children = retval;
3227
    } /* if */
3228
 
3229
    return retval;
3230
} /* __PHYSFS_DirTreeAdd */
3231
 
3232
 
3233
/* Find the __PHYSFS_DirTreeEntry for a path in platform-independent notation. */
3234
void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path)
3235
{
3236
    PHYSFS_uint32 hashval;
3237
    __PHYSFS_DirTreeEntry *prev = NULL;
3238
    __PHYSFS_DirTreeEntry *retval;
3239
 
3240
    if (*path == '\0')
3241
        return dt->root;
3242
 
3243
    hashval = hashPathName(dt, path);
3244
    for (retval = dt->hash[hashval]; retval; retval = retval->hashnext)
3245
    {
3246
        if (strcmp(retval->name, path) == 0)
3247
        {
3248
            if (prev != NULL)  /* move this to the front of the list */
3249
            {
3250
                prev->hashnext = retval->hashnext;
3251
                retval->hashnext = dt->hash[hashval];
3252
                dt->hash[hashval] = retval;
3253
            } /* if */
3254
 
3255
            return retval;
3256
        } /* if */
3257
 
3258
        prev = retval;
3259
    } /* for */
3260
 
3261
    BAIL(PHYSFS_ERR_NOT_FOUND, NULL);
3262
} /* __PHYSFS_DirTreeFind */
3263
 
3264
PHYSFS_EnumerateCallbackResult __PHYSFS_DirTreeEnumerate(void *opaque,
3265
                              const char *dname, PHYSFS_EnumerateCallback cb,
3266
                              const char *origdir, void *callbackdata)
3267
{
3268
    PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
3269
    __PHYSFS_DirTree *tree = (__PHYSFS_DirTree *) opaque;
3270
    const __PHYSFS_DirTreeEntry *entry = __PHYSFS_DirTreeFind(tree, dname);
3271
    BAIL_IF(!entry, PHYSFS_ERR_NOT_FOUND, PHYSFS_ENUM_ERROR);
3272
 
3273
    entry = entry->children;
3274
 
3275
    while (entry && (retval == PHYSFS_ENUM_OK))
3276
    {
3277
        const char *name = entry->name;
3278
        const char *ptr = strrchr(name, '/');
3279
        retval = cb(callbackdata, origdir, ptr ? ptr + 1 : name);
3280
        BAIL_IF(retval == PHYSFS_ENUM_ERROR, PHYSFS_ERR_APP_CALLBACK, retval);
3281
        entry = entry->sibling;
3282
    } /* while */
3283
 
3284
    return retval;
3285
} /* __PHYSFS_DirTreeEnumerate */
3286
 
3287
 
3288
void __PHYSFS_DirTreeDeinit(__PHYSFS_DirTree *dt)
3289
{
3290
    if (!dt)
3291
        return;
3292
 
3293
    if (dt->root)
3294
    {
3295
        assert(dt->root->sibling == NULL);
3296
        assert(dt->hash || (dt->root->children == NULL));
3297
        allocator.Free(dt->root);
3298
    } /* if */
3299
 
3300
    if (dt->hash)
3301
    {
3302
        size_t i;
3303
        for (i = 0; i < dt->hashBuckets; i++)
3304
        {
3305
            __PHYSFS_DirTreeEntry *entry;
3306
            __PHYSFS_DirTreeEntry *next;
3307
            for (entry = dt->hash[i]; entry; entry = next)
3308
            {
3309
                next = entry->hashnext;
3310
                allocator.Free(entry);
3311
            } /* for */
3312
        } /* for */
3313
        allocator.Free(dt->hash);
3314
    } /* if */
3315
} /* __PHYSFS_DirTreeDeinit */
3316
 
3317
/* end of physfs.c ... */
3318