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