/
physfs.c
2486 lines (1989 loc) · 67.5 KB
1
2
3
4
5
/**
* PhysicsFS; a portable, flexible file i/o abstraction.
*
* Documentation is in physfs.h. It's verbose, honest. :)
*
6
* Please see the file LICENSE.txt in the source's root directory.
7
8
9
10
*
* This file written by Ryan C. Gordon.
*/
11
12
/* !!! FIXME: ERR_PAST_EOF shouldn't trigger for reads. Just return zero. */
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "physfs.h"
18
19
20
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
21
22
23
typedef struct __PHYSFS_DIRHANDLE__
{
24
25
void *opaque; /* Instance data unique to the archiver. */
char *dirName; /* Path to archive in platform-dependent notation. */
26
char *mountPoint; /* Mountpoint in virtual file tree. */
27
28
const PHYSFS_Archiver *funcs; /* Ptr to archiver info for this handle. */
struct __PHYSFS_DIRHANDLE__ *next; /* linked list stuff. */
29
30
31
} DirHandle;
32
33
typedef struct __PHYSFS_FILEHANDLE__
{
34
PHYSFS_Io *io; /* Instance data unique to the archiver for this file. */
35
36
37
38
39
40
41
42
43
44
PHYSFS_uint8 forReading; /* Non-zero if reading, zero if write/append */
const DirHandle *dirHandle; /* Archiver instance that created this */
PHYSFS_uint8 *buffer; /* Buffer, if set (NULL otherwise). Don't touch! */
PHYSFS_uint32 bufsize; /* Bufsize, if set (0 otherwise). Don't touch! */
PHYSFS_uint32 buffill; /* Buffer fill size. Don't touch! */
PHYSFS_uint32 bufpos; /* Buffer position. Don't touch! */
struct __PHYSFS_FILEHANDLE__ *next; /* linked list stuff. */
} FileHandle;
45
46
typedef struct __PHYSFS_ERRMSGTYPE__
{
47
void *tid;
48
49
50
51
52
int errorAvailable;
char errorString[80];
struct __PHYSFS_ERRMSGTYPE__ *next;
} ErrMsg;
53
54
/* The various i/o drivers...some of these may not be compiled in. */
55
56
extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP;
extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP;
57
58
extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_LZMA;
extern const PHYSFS_Archiver __PHYSFS_Archiver_LZMA;
59
60
61
62
63
64
65
66
67
68
extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP;
extern const PHYSFS_Archiver __PHYSFS_Archiver_GRP;
extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK;
extern const PHYSFS_Archiver __PHYSFS_Archiver_QPAK;
extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG;
extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG;
extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL;
extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL;
extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_WAD;
extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD;
69
extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR;
70
71
extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ISO9660;
extern const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660;
72
73
74
static const PHYSFS_ArchiveInfo *supported_types[] =
75
76
{
#if (defined PHYSFS_SUPPORTS_ZIP)
77
&__PHYSFS_ArchiveInfo_ZIP,
78
#endif
79
#if (defined PHYSFS_SUPPORTS_7Z)
80
81
&__PHYSFS_ArchiveInfo_LZMA,
#endif
82
83
84
#if (defined PHYSFS_SUPPORTS_GRP)
&__PHYSFS_ArchiveInfo_GRP,
#endif
85
86
87
#if (defined PHYSFS_SUPPORTS_QPAK)
&__PHYSFS_ArchiveInfo_QPAK,
#endif
88
89
90
91
92
93
#if (defined PHYSFS_SUPPORTS_HOG)
&__PHYSFS_ArchiveInfo_HOG,
#endif
#if (defined PHYSFS_SUPPORTS_MVL)
&__PHYSFS_ArchiveInfo_MVL,
#endif
94
95
#if (defined PHYSFS_SUPPORTS_WAD)
&__PHYSFS_ArchiveInfo_WAD,
96
#endif
97
#if (defined PHYSFS_SUPPORTS_ISO9660)
98
&__PHYSFS_ArchiveInfo_ISO9660,
99
#endif
100
101
102
NULL
};
103
static const PHYSFS_Archiver *archivers[] =
104
105
{
#if (defined PHYSFS_SUPPORTS_ZIP)
106
&__PHYSFS_Archiver_ZIP,
107
#endif
108
#if (defined PHYSFS_SUPPORTS_7Z)
109
110
&__PHYSFS_Archiver_LZMA,
#endif
111
#if (defined PHYSFS_SUPPORTS_GRP)
112
&__PHYSFS_Archiver_GRP,
113
#endif
114
#if (defined PHYSFS_SUPPORTS_QPAK)
115
&__PHYSFS_Archiver_QPAK,
116
#endif
117
#if (defined PHYSFS_SUPPORTS_HOG)
118
&__PHYSFS_Archiver_HOG,
119
120
#endif
#if (defined PHYSFS_SUPPORTS_MVL)
121
&__PHYSFS_Archiver_MVL,
122
#endif
123
#if (defined PHYSFS_SUPPORTS_WAD)
124
&__PHYSFS_Archiver_WAD,
125
#endif
126
#if (defined PHYSFS_SUPPORTS_ISO9660)
127
&__PHYSFS_Archiver_ISO9660,
128
#endif
129
130
NULL
};
131
132
133
134
135
136
/* General PhysicsFS state ... */
static int initialized = 0;
static ErrMsg *errorMessages = NULL;
137
138
139
140
static DirHandle *searchPath = NULL;
static DirHandle *writeDir = NULL;
static FileHandle *openWriteList = NULL;
static FileHandle *openReadList = NULL;
141
142
143
144
static char *baseDir = NULL;
static char *userDir = NULL;
static int allowSymLinks = 0;
145
146
147
/* mutexes ... */
static void *errorLock = NULL; /* protects error message list. */
static void *stateLock = NULL; /* protects other PhysFS static state. */
148
149
150
/* allocator ... */
static int externalAllocator = 0;
151
PHYSFS_Allocator allocator;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/* PHYSFS_Io implementation for i/o to physical filesystem... */
/* !!! FIXME: maybe refcount the paths in a string pool? */
typedef struct __PHYSFS_NativeIoInfo
{
void *handle;
const char *path;
int mode; /* 'r', 'w', or 'a' */
} NativeIoInfo;
static PHYSFS_sint64 nativeIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
{
NativeIoInfo *info = (NativeIoInfo *) io->opaque;
return __PHYSFS_platformRead(info->handle, buf, len);
} /* nativeIo_read */
static PHYSFS_sint64 nativeIo_write(PHYSFS_Io *io, const void *buffer,
PHYSFS_uint64 len)
{
NativeIoInfo *info = (NativeIoInfo *) io->opaque;
return __PHYSFS_platformWrite(info->handle, buffer, len);
} /* nativeIo_write */
static int nativeIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
{
NativeIoInfo *info = (NativeIoInfo *) io->opaque;
return __PHYSFS_platformSeek(info->handle, offset);
} /* nativeIo_seek */
static PHYSFS_sint64 nativeIo_tell(PHYSFS_Io *io)
{
NativeIoInfo *info = (NativeIoInfo *) io->opaque;
return __PHYSFS_platformTell(info->handle);
} /* nativeIo_tell */
static PHYSFS_sint64 nativeIo_length(PHYSFS_Io *io)
{
NativeIoInfo *info = (NativeIoInfo *) io->opaque;
return __PHYSFS_platformFileLength(info->handle);
} /* nativeIo_length */
static PHYSFS_Io *nativeIo_duplicate(PHYSFS_Io *io)
{
NativeIoInfo *info = (NativeIoInfo *) io->opaque;
return __PHYSFS_createNativeIo(info->path, info->mode);
} /* nativeIo_duplicate */
static int nativeIo_flush(PHYSFS_Io *io)
{
return __PHYSFS_platformFlush(io->opaque);
} /* nativeIo_flush */
static void nativeIo_destroy(PHYSFS_Io *io)
{
NativeIoInfo *info = (NativeIoInfo *) io->opaque;
__PHYSFS_platformClose(info->handle);
allocator.Free((void *) info->path);
allocator.Free(info);
allocator.Free(io);
} /* nativeIo_destroy */
static const PHYSFS_Io __PHYSFS_nativeIoInterface =
{
nativeIo_read,
nativeIo_write,
nativeIo_seek,
nativeIo_tell,
nativeIo_length,
nativeIo_duplicate,
nativeIo_flush,
nativeIo_destroy,
NULL
};
PHYSFS_Io *__PHYSFS_createNativeIo(const char *path, const int mode)
{
PHYSFS_Io *io = NULL;
NativeIoInfo *info = NULL;
void *handle = NULL;
char *pathdup = NULL;
assert((mode == 'r') || (mode == 'w') || (mode == 'a'));
io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
GOTO_IF_MACRO(io == NULL, ERR_OUT_OF_MEMORY, createNativeIo_failed);
info = (NativeIoInfo *) allocator.Malloc(sizeof (NativeIoInfo));
GOTO_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, createNativeIo_failed);
pathdup = (char *) allocator.Malloc(strlen(path) + 1);
GOTO_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, createNativeIo_failed);
if (mode == 'r')
handle = __PHYSFS_platformOpenRead(path);
else if (mode == 'w')
handle = __PHYSFS_platformOpenWrite(path);
else if (mode == 'a')
handle = __PHYSFS_platformOpenAppend(path);
GOTO_IF_MACRO(handle == NULL, NULL, createNativeIo_failed);
strcpy(pathdup, path);
info->handle = handle;
info->path = pathdup;
info->mode = mode;
memcpy(io, &__PHYSFS_nativeIoInterface, sizeof (*io));
io->opaque = info;
return io;
createNativeIo_failed:
if (handle != NULL) __PHYSFS_platformClose(handle);
if (pathdup != NULL) allocator.Free(pathdup);
if (info != NULL) allocator.Free(info);
if (io != NULL) allocator.Free(io);
return NULL;
} /* __PHYSFS_createNativeIo */
270
271
/* functions ... */
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
typedef struct
{
char **list;
PHYSFS_uint32 size;
const char *errorstr;
} EnumStringListCallbackData;
static void enumStringListCallback(void *data, const char *str)
{
void *ptr;
char *newstr;
EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data;
if (pecd->errorstr)
return;
288
289
ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *));
newstr = (char *) allocator.Malloc(strlen(str) + 1);
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
if (ptr != NULL)
pecd->list = (char **) ptr;
if ((ptr == NULL) || (newstr == NULL))
{
pecd->errorstr = ERR_OUT_OF_MEMORY;
pecd->list[pecd->size] = NULL;
PHYSFS_freeList(pecd->list);
return;
} /* if */
strcpy(newstr, str);
pecd->list[pecd->size] = newstr;
pecd->size++;
} /* enumStringListCallback */
static char **doEnumStringList(void (*func)(PHYSFS_StringCallback, void *))
{
EnumStringListCallbackData ecd;
memset(&ecd, '\0', sizeof (ecd));
311
ecd.list = (char **) allocator.Malloc(sizeof (char *));
312
313
314
315
BAIL_IF_MACRO(ecd.list == NULL, ERR_OUT_OF_MEMORY, NULL);
func(enumStringListCallback, &ecd);
BAIL_IF_MACRO(ecd.errorstr != NULL, ecd.errorstr, NULL);
ecd.list[ecd.size] = NULL;
316
return ecd.list;
317
318
319
} /* doEnumStringList */
320
static void __PHYSFS_bubble_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi,
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32))
{
PHYSFS_uint32 i;
int sorted;
do
{
sorted = 1;
for (i = lo; i < hi; i++)
{
if (cmpfn(a, i, i + 1) > 0)
{
swapfn(a, i, i + 1);
sorted = 0;
} /* if */
} /* for */
} while (!sorted);
} /* __PHYSFS_bubble_sort */
342
static void __PHYSFS_quick_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi,
343
344
345
346
347
348
349
int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32))
{
PHYSFS_uint32 i;
PHYSFS_uint32 j;
PHYSFS_uint32 v;
350
if ((hi - lo) <= PHYSFS_QUICKSORT_THRESHOLD)
351
352
__PHYSFS_bubble_sort(a, lo, hi, cmpfn, swapfn);
else
353
354
{
i = (hi + lo) / 2;
355
356
if (cmpfn(a, lo, i) > 0) swapfn(a, lo, i);
357
358
359
360
361
362
363
364
365
366
367
368
if (cmpfn(a, lo, hi) > 0) swapfn(a, lo, hi);
if (cmpfn(a, i, hi) > 0) swapfn(a, i, hi);
j = hi - 1;
swapfn(a, i, j);
i = lo;
v = j;
while (1)
{
while(cmpfn(a, ++i, v) < 0) { /* do nothing */ }
while(cmpfn(a, --j, v) > 0) { /* do nothing */ }
if (j < i)
369
break;
370
371
swapfn(a, i, j);
} /* while */
372
373
if (i != (hi-1))
swapfn(a, i, hi-1);
374
375
376
__PHYSFS_quick_sort(a, lo, j, cmpfn, swapfn);
__PHYSFS_quick_sort(a, i+1, hi, cmpfn, swapfn);
} /* else */
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
} /* __PHYSFS_quick_sort */
void __PHYSFS_sort(void *entries, PHYSFS_uint32 max,
int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32))
{
/*
* Quicksort w/ Bubblesort fallback algorithm inspired by code from here:
* http://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html
*/
__PHYSFS_quick_sort(entries, 0, max - 1, cmpfn, swapfn);
} /* __PHYSFS_sort */
392
393
394
static ErrMsg *findErrorForCurrentThread(void)
{
ErrMsg *i;
395
void *tid;
396
397
if (errorLock != NULL)
398
399
__PHYSFS_platformGrabMutex(errorLock);
400
401
if (errorMessages != NULL)
{
402
tid = __PHYSFS_platformGetThreadID();
403
404
405
406
for (i = errorMessages; i != NULL; i = i->next)
{
if (i->tid == tid)
407
{
408
409
if (errorLock != NULL)
__PHYSFS_platformReleaseMutex(errorLock);
410
return i;
411
} /* if */
412
413
} /* for */
} /* if */
414
415
if (errorLock != NULL)
416
__PHYSFS_platformReleaseMutex(errorLock);
417
418
return NULL; /* no error available. */
419
420
421
} /* findErrorForCurrentThread */
422
void __PHYSFS_setError(const char *str)
423
{
424
425
426
427
428
429
ErrMsg *err;
if (str == NULL)
return;
err = findErrorForCurrentThread();
430
431
432
if (err == NULL)
{
433
err = (ErrMsg *) allocator.Malloc(sizeof (ErrMsg));
434
435
436
if (err == NULL)
return; /* uhh...? */
437
memset((void *) err, '\0', sizeof (ErrMsg));
438
err->tid = __PHYSFS_platformGetThreadID();
439
440
441
442
if (errorLock != NULL)
__PHYSFS_platformGrabMutex(errorLock);
443
444
err->next = errorMessages;
errorMessages = err;
445
446
447
if (errorLock != NULL)
__PHYSFS_platformReleaseMutex(errorLock);
448
449
450
451
452
} /* if */
err->errorAvailable = 1;
strncpy(err->errorString, str, sizeof (err->errorString));
err->errorString[sizeof (err->errorString) - 1] = '\0';
453
} /* __PHYSFS_setError */
454
455
456
457
458
459
460
const char *PHYSFS_getLastError(void)
{
ErrMsg *err = findErrorForCurrentThread();
if ((err == NULL) || (!err->errorAvailable))
461
return NULL;
462
463
err->errorAvailable = 0;
464
return err->errorString;
465
466
467
468
} /* PHYSFS_getLastError */
/* MAKE SURE that errorLock is held before calling this! */
469
470
471
472
473
474
475
static void freeErrorMessages(void)
{
ErrMsg *i;
ErrMsg *next;
for (i = errorMessages; i != NULL; i = next)
{
476
next = i->next;
477
allocator.Free(i);
478
} /* for */
479
480
errorMessages = NULL;
481
482
483
} /* freeErrorMessages */
484
485
486
487
488
489
490
491
492
493
494
void PHYSFS_getLinkedVersion(PHYSFS_Version *ver)
{
if (ver != NULL)
{
ver->major = PHYSFS_VER_MAJOR;
ver->minor = PHYSFS_VER_MINOR;
ver->patch = PHYSFS_VER_PATCH;
} /* if */
} /* PHYSFS_getLinkedVersion */
495
496
static const char *find_filename_extension(const char *fname)
{
497
498
const char *retval = NULL;
if (fname != NULL)
499
{
500
501
retval = strchr(fname, '.');
const char *p = retval;
502
503
504
505
506
507
508
509
510
511
512
while (p != NULL)
{
p = strchr(p + 1, '.');
if (p != NULL)
retval = p;
} /* while */
if (retval != NULL)
retval++; /* skip '.' */
} /* if */
513
514
return retval;
515
516
517
} /* find_filename_extension */
518
static DirHandle *tryOpenDir(PHYSFS_Io *io, const PHYSFS_Archiver *funcs,
519
const char *d, int forWriting)
520
521
{
DirHandle *retval = NULL;
522
523
524
525
526
527
void *opaque = NULL;
if (io != NULL)
BAIL_IF_MACRO(!io->seek(io, 0), NULL, NULL);
opaque = funcs->openArchive(io, d, forWriting);
528
if (opaque != NULL)
529
{
530
531
532
533
retval = (DirHandle *) allocator.Malloc(sizeof (DirHandle));
if (retval == NULL)
funcs->dirClose(opaque);
else
534
{
535
536
537
538
539
memset(retval, '\0', sizeof (DirHandle));
retval->mountPoint = NULL;
retval->funcs = funcs;
retval->opaque = opaque;
} /* else */
540
541
} /* if */
542
return retval;
543
544
545
} /* tryOpenDir */
546
static DirHandle *openDirectory(PHYSFS_Io *io, const char *d, int forWriting)
547
{
548
DirHandle *retval = NULL;
549
const PHYSFS_Archiver **i;
550
const char *ext;
551
552
assert((io != NULL) || (d != NULL));
553
554
555
556
557
558
559
560
561
562
563
564
565
if (io == NULL)
{
BAIL_IF_MACRO(!__PHYSFS_platformExists(d), ERR_NO_SUCH_FILE, NULL);
/* DIR gets first shot (unlike the rest, it doesn't deal with files). */
retval = tryOpenDir(io, &__PHYSFS_Archiver_DIR, d, forWriting);
if (retval != NULL)
return retval;
io = __PHYSFS_createNativeIo(d, forWriting ? 'w' : 'r');
BAIL_IF_MACRO_MUTEX(io == NULL, NULL, stateLock, 0);
} /* if */
566
567
568
ext = find_filename_extension(d);
if (ext != NULL)
569
{
570
/* Look for archivers with matching file extensions first... */
571
for (i = archivers; (*i != NULL) && (retval == NULL); i++)
572
{
573
if (__PHYSFS_stricmpASCII(ext, (*i)->info->extension) == 0)
574
retval = tryOpenDir(io, *i, d, forWriting);
575
576
577
} /* for */
/* failing an exact file extension match, try all the others... */
578
for (i = archivers; (*i != NULL) && (retval == NULL); i++)
579
{
580
if (__PHYSFS_stricmpASCII(ext, (*i)->info->extension) != 0)
581
retval = tryOpenDir(io, *i, d, forWriting);
582
583
584
585
586
} /* for */
} /* if */
else /* no extension? Try them all. */
{
587
for (i = archivers; (*i != NULL) && (retval == NULL); i++)
588
retval = tryOpenDir(io, *i, d, forWriting);
589
} /* else */
590
591
BAIL_IF_MACRO(retval == NULL, ERR_UNSUPPORTED_ARCHIVE, NULL);
592
return retval;
593
594
595
} /* openDirectory */
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
/*
* Make a platform-independent path string sane. Doesn't actually check the
* file hierarchy, it just cleans up the string.
* (dst) must be a buffer at least as big as (src), as this is where the
* cleaned up string is deposited.
* If there are illegal bits in the path (".." entries, etc) then we
* return zero and (dst) is undefined. Non-zero if the path was sanitized.
*/
static int sanitizePlatformIndependentPath(const char *src, char *dst)
{
char *prev;
char ch;
while (*src == '/') /* skip initial '/' chars... */
src++;
prev = dst;
do
{
ch = *(src++);
if ((ch == ':') || (ch == '\\')) /* illegal chars in a physfs path. */
BAIL_MACRO(ERR_INSECURE_FNAME, 0);
if (ch == '/') /* path separator. */
{
*dst = '\0'; /* "." and ".." are illegal pathnames. */
if ((strcmp(prev, ".") == 0) || (strcmp(prev, "..") == 0))
BAIL_MACRO(ERR_INSECURE_FNAME, 0);
while (*src == '/') /* chop out doubles... */
src++;
if (*src == '\0') /* ends with a pathsep? */
break; /* we're done, don't add final pathsep to dst. */
prev = dst + 1;
} /* if */
*(dst++) = ch;
} while (ch != '\0');
638
return 1;
639
640
641
} /* sanitizePlatformIndependentPath */
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
/*
* Figure out if (fname) is part of (h)'s mountpoint. (fname) must be an
* output from sanitizePlatformIndependentPath(), so that it is in a known
* state.
*
* This only finds legitimate segments of a mountpoint. If the mountpoint is
* "/a/b/c" and (fname) is "/a/b/c", "/", or "/a/b/c/d", then the results are
* all zero. "/a/b" will succeed, though.
*/
static int partOfMountPoint(DirHandle *h, char *fname)
{
/* !!! FIXME: This code feels gross. */
int rc;
size_t len, mntpntlen;
if (h->mountPoint == NULL)
658
return 0;
659
else if (*fname == '\0')
660
return 1;
661
662
663
664
len = strlen(fname);
mntpntlen = strlen(h->mountPoint);
if (len > mntpntlen) /* can't be a subset of mountpoint. */
665
return 0;
666
667
668
/* if true, must be not a match or a complete match, but not a subset. */
if ((len + 1) == mntpntlen)
669
return 0;
670
671
672
rc = strncmp(fname, h->mountPoint, len); /* !!! FIXME: case insensitive? */
if (rc != 0)
673
return 0; /* not a match. */
674
675
/* make sure /a/b matches /a/b/ and not /a/bc ... */
676
return h->mountPoint[len] == '/';
677
678
679
} /* partOfMountPoint */
680
681
static DirHandle *createDirHandle(PHYSFS_Io *io, const char *newDir,
const char *mountPoint, int forWriting)
682
683
{
DirHandle *dirHandle = NULL;
684
char *tmpmntpnt = NULL;
685
686
687
if (mountPoint != NULL)
{
688
689
690
691
const size_t len = strlen(mountPoint) + 1;
tmpmntpnt = (char *) __PHYSFS_smallAlloc(len);
GOTO_IF_MACRO(!tmpmntpnt, ERR_OUT_OF_MEMORY, badDirHandle);
if (!sanitizePlatformIndependentPath(mountPoint, tmpmntpnt))
692
goto badDirHandle;
693
mountPoint = tmpmntpnt; /* sanitized version. */
694
} /* if */
695
696
dirHandle = openDirectory(io, newDir, forWriting);
697
GOTO_IF_MACRO(!dirHandle, NULL, badDirHandle);
698
699
700
701
702
703
704
705
706
if (newDir == NULL)
dirHandle->dirName = NULL;
else
{
dirHandle->dirName = (char *) allocator.Malloc(strlen(newDir) + 1);
GOTO_IF_MACRO(!dirHandle->dirName, ERR_OUT_OF_MEMORY, badDirHandle);
strcpy(dirHandle->dirName, newDir);
} /* else */
707
708
709
if ((mountPoint != NULL) && (*mountPoint != '\0'))
{
710
dirHandle->mountPoint = (char *)allocator.Malloc(strlen(mountPoint)+2);
711
712
713
714
715
GOTO_IF_MACRO(!dirHandle->mountPoint, ERR_OUT_OF_MEMORY, badDirHandle);
strcpy(dirHandle->mountPoint, mountPoint);
strcat(dirHandle->mountPoint, "/");
} /* if */
716
__PHYSFS_smallFree(tmpmntpnt);
717
return dirHandle;
718
719
720
badDirHandle:
if (dirHandle != NULL)
721
{
722
dirHandle->funcs->dirClose(dirHandle->opaque);
723
724
725
allocator.Free(dirHandle->dirName);
allocator.Free(dirHandle->mountPoint);
allocator.Free(dirHandle);
726
} /* if */
727
728
__PHYSFS_smallFree(tmpmntpnt);
729
return NULL;
730
} /* createDirHandle */
731
732
733
/* MAKE SURE you've got the stateLock held before calling this! */
734
static int freeDirHandle(DirHandle *dh, FileHandle *openList)
735
{
736
FileHandle *i;
737
738
if (dh == NULL)
739
return 1;
740
741
for (i = openList; i != NULL; i = i->next)
742
BAIL_IF_MACRO(i->dirHandle == dh, ERR_FILES_STILL_OPEN, 0);
743
744
dh->funcs->dirClose(dh->opaque);
745
746
747
allocator.Free(dh->dirName);
allocator.Free(dh->mountPoint);
allocator.Free(dh);
748
return 1;
749
} /* freeDirHandle */
750
751
752
static char *calculateUserDir(void)
753
{
754
755
756
757
758
759
760
761
char *retval = __PHYSFS_platformGetUserDir();
if (retval != NULL)
{
/* make sure it really exists and is normalized. */
char *ptr = __PHYSFS_platformRealPath(retval);
allocator.Free(retval);
retval = ptr;
} /* if */
762
763
if (retval == NULL)
764
765
766
{
const char *dirsep = PHYSFS_getDirSeparator();
const char *uname = __PHYSFS_platformGetUserName();
767
const char *str = (uname != NULL) ? uname : "default";
768
769
770
retval = (char *) allocator.Malloc(strlen(baseDir) + strlen(str) +
strlen(dirsep) + 6);
771
772
773
774
if (retval == NULL)
__PHYSFS_setError(ERR_OUT_OF_MEMORY);
else
775
sprintf(retval, "%susers%s%s", baseDir, dirsep, str);
776
777
allocator.Free((void *) uname);
778
779
} /* else */
780
return retval;
781
782
783
} /* calculateUserDir */
784
785
786
787
788
789
static int appendDirSep(char **dir)
{
const char *dirsep = PHYSFS_getDirSeparator();
char *ptr;
if (strcmp((*dir + strlen(*dir)) - strlen(dirsep), dirsep) == 0)
790
return 1;
791
792
ptr = (char *) allocator.Realloc(*dir, strlen(*dir) + strlen(dirsep) + 1);
793
794
if (!ptr)
{
795
allocator.Free(*dir);
796
return 0;
797
798
799
800
} /* if */
strcat(ptr, dirsep);
*dir = ptr;
801
return 1;
802
803
804
} /* appendDirSep */
805
806
static char *calculateBaseDir(const char *argv0)
{
807
808
809
char *retval = NULL;
const char *dirsep = NULL;
char *ptr = NULL;
810
811
/* Give the platform layer first shot at this. */
812
813
retval = __PHYSFS_platformCalcBaseDir(argv0);
if (retval != NULL)
814
return retval;
815
816
817
/* We need argv0 to go on. */
BAIL_IF_MACRO(argv0 == NULL, ERR_ARGV0_IS_NULL, NULL);
818
819
820
821
822
dirsep = PHYSFS_getDirSeparator();
if (strlen(dirsep) == 1) /* fast path. */
ptr = strrchr(argv0, *dirsep);
else
823
{
824
825
ptr = strstr(argv0, dirsep);
if (ptr != NULL)
826
{
827
828
829
830
831
832
833
834
char *p = ptr;
while (p != NULL)
{
ptr = p;
p = strstr(p + 1, dirsep);
} /* while */
} /* if */
} /* else */
835
836
837
838
if (ptr != NULL)
{
size_t size = (size_t) (ptr - argv0);
839
retval = (char *) allocator.Malloc(size + 1);
840
841
842
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
memcpy(retval, argv0, size);
retval[size] = '\0';
843
return retval;
844
845
} /* if */
846
847
/* argv0 wasn't helpful. */
BAIL_MACRO(ERR_INVALID_ARGUMENT, NULL);
848
849
850
} /* calculateBaseDir */
851
852
853
854
855
856
857
858
859
860
static int initializeMutexes(void)
{
errorLock = __PHYSFS_platformCreateMutex();
if (errorLock == NULL)
goto initializeMutexes_failed;
stateLock = __PHYSFS_platformCreateMutex();
if (stateLock == NULL)
goto initializeMutexes_failed;
861
return 1; /* success. */
862
863
864
865
866
867
868
869
870
initializeMutexes_failed:
if (errorLock != NULL)
__PHYSFS_platformDestroyMutex(errorLock);
if (stateLock != NULL)
__PHYSFS_platformDestroyMutex(stateLock);
errorLock = stateLock = NULL;
871
return 0; /* failed. */
872
873
874
} /* initializeMutexes */
875
876
static void setDefaultAllocator(void);
877
878
int PHYSFS_init(const char *argv0)
{
879
880
char *ptr;
881
BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0);
882
883
884
885
if (!externalAllocator)
setDefaultAllocator();
886
887
if (allocator.Init != NULL)
BAIL_IF_MACRO(!allocator.Init(), NULL, 0);
888
889
BAIL_IF_MACRO(!__PHYSFS_platformInit(), NULL, 0);
890
891
892
BAIL_IF_MACRO(!initializeMutexes(), NULL, 0);
893
baseDir = calculateBaseDir(argv0);
894
BAIL_IF_MACRO(baseDir == NULL, NULL, 0);
895
896
/* !!! FIXME: only call this if we got this from argv0 (unreliable). */
897
ptr = __PHYSFS_platformRealPath(baseDir);
898
allocator.Free(baseDir);
899
900
901
BAIL_IF_MACRO(ptr == NULL, NULL, 0);
baseDir = ptr;
902
BAIL_IF_MACRO(!appendDirSep(&baseDir), NULL, 0);
903
904
userDir = calculateUserDir();
905
if ((userDir == NULL) || (!appendDirSep(&userDir)))
906
{
907
allocator.Free(baseDir);
908
baseDir = NULL;
909
return 0;
910
911
} /* if */
912
initialized = 1;
913
914
915
/* This makes sure that the error subsystem is initialized. */
__PHYSFS_setError(PHYSFS_getLastError());
916
917
return 1;
918
919
920
} /* PHYSFS_init */
921
/* MAKE SURE you hold stateLock before calling this! */
922
static int closeFileHandleList(FileHandle **list)
923
{
924
925
FileHandle *i;
FileHandle *next = NULL;
926
927
928
for (i = *list; i != NULL; i = next)
{
929
PHYSFS_Io *io = i->io;
930
next = i->next;
931
932
if (!io->flush(io))
933
934
{
*list = i;
935
return 0;
936
937
} /* if */
938
io->destroy(io);
939
allocator.Free(i);
940
941
942
} /* for */
*list = NULL;
943
return 1;
944
} /* closeFileHandleList */
945
946
947
/* MAKE SURE you hold the stateLock before calling this! */
948
949
static void freeSearchPath(void)
{
950
951
DirHandle *i;
DirHandle *next = NULL;
952
953
954
closeFileHandleList(&openReadList);
955
956
957
958
if (searchPath != NULL)
{
for (i = searchPath; i != NULL; i = next)
{
959
next = i->next;
960
freeDirHandle(i, openReadList);
961
962
963
964
965
966
} /* for */
searchPath = NULL;
} /* if */
} /* freeSearchPath */
967
int PHYSFS_deinit(void)
968
969
{
BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
970
BAIL_IF_MACRO(!__PHYSFS_platformDeinit(), NULL, 0);
971
972
closeFileHandleList(&openWriteList);
973
974
BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), ERR_FILES_STILL_OPEN, 0);
975
976
977
freeSearchPath();
freeErrorMessages();
978
if (baseDir != NULL)
979
{
980
allocator.Free(baseDir);
981
982
983
984
985
baseDir = NULL;
} /* if */
if (userDir != NULL)
{
986
allocator.Free(userDir);
987
988
userDir = NULL;
} /* if */
989
990
allowSymLinks = 0;
991
initialized = 0;
992
993
994
995
__PHYSFS_platformDestroyMutex(errorLock);
__PHYSFS_platformDestroyMutex(stateLock);
996
997
if (allocator.Deinit != NULL)
allocator.Deinit();
998
999
errorLock = stateLock = NULL;
1000
return 1;