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