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