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