misc/libphysfs/physfs.c
changeset 12218 bb5522e88ab2
parent 10017 de822cd3df3a
child 12458 89423b1db329
equal deleted inserted replaced
12217:ea891871f481 12218:bb5522e88ab2
    42 {
    42 {
    43     void *tid;
    43     void *tid;
    44     PHYSFS_ErrorCode code;
    44     PHYSFS_ErrorCode code;
    45     struct __PHYSFS_ERRSTATETYPE__ *next;
    45     struct __PHYSFS_ERRSTATETYPE__ *next;
    46 } ErrState;
    46 } ErrState;
    47 
       
    48 
       
    49 /* The various i/o drivers...some of these may not be compiled in. */
       
    50 extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP;
       
    51 extern const PHYSFS_Archiver __PHYSFS_Archiver_LZMA;
       
    52 extern const PHYSFS_Archiver __PHYSFS_Archiver_GRP;
       
    53 extern const PHYSFS_Archiver __PHYSFS_Archiver_QPAK;
       
    54 extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG;
       
    55 extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL;
       
    56 extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD;
       
    57 extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR;
       
    58 extern const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660;
       
    59 
       
    60 static const PHYSFS_Archiver *staticArchivers[] =
       
    61 {
       
    62 #if PHYSFS_SUPPORTS_ZIP
       
    63     &__PHYSFS_Archiver_ZIP,
       
    64 #endif
       
    65 #if PHYSFS_SUPPORTS_7Z
       
    66     &__PHYSFS_Archiver_LZMA,
       
    67 #endif
       
    68 #if PHYSFS_SUPPORTS_GRP
       
    69     &__PHYSFS_Archiver_GRP,
       
    70 #endif
       
    71 #if PHYSFS_SUPPORTS_QPAK
       
    72     &__PHYSFS_Archiver_QPAK,
       
    73 #endif
       
    74 #if PHYSFS_SUPPORTS_HOG
       
    75     &__PHYSFS_Archiver_HOG,
       
    76 #endif
       
    77 #if PHYSFS_SUPPORTS_MVL
       
    78     &__PHYSFS_Archiver_MVL,
       
    79 #endif
       
    80 #if PHYSFS_SUPPORTS_WAD
       
    81     &__PHYSFS_Archiver_WAD,
       
    82 #endif
       
    83 #if PHYSFS_SUPPORTS_ISO9660
       
    84     &__PHYSFS_Archiver_ISO9660,
       
    85 #endif
       
    86     NULL
       
    87 };
       
    88 
       
    89 
    47 
    90 
    48 
    91 /* General PhysicsFS state ... */
    49 /* General PhysicsFS state ... */
    92 static int initialized = 0;
    50 static int initialized = 0;
    93 static ErrState *errorStates = NULL;
    51 static ErrState *errorStates = NULL;
    99 static char *userDir = NULL;
    57 static char *userDir = NULL;
   100 static char *prefDir = NULL;
    58 static char *prefDir = NULL;
   101 static int allowSymLinks = 0;
    59 static int allowSymLinks = 0;
   102 static const PHYSFS_Archiver **archivers = NULL;
    60 static const PHYSFS_Archiver **archivers = NULL;
   103 static const PHYSFS_ArchiveInfo **archiveInfo = NULL;
    61 static const PHYSFS_ArchiveInfo **archiveInfo = NULL;
       
    62 static volatile size_t numArchivers = 0;
   104 
    63 
   105 /* mutexes ... */
    64 /* mutexes ... */
   106 static void *errorLock = NULL;     /* protects error message list.        */
    65 static void *errorLock = NULL;     /* protects error message list.        */
   107 static void *stateLock = NULL;     /* protects other PhysFS static state. */
    66 static void *stateLock = NULL;     /* protects other PhysFS static state. */
   108 
    67 
   484     __PHYSFS_platformReleaseMutex(stateLock);
   443     __PHYSFS_platformReleaseMutex(stateLock);
   485 
   444 
   486     memcpy(retval, io, sizeof (PHYSFS_Io));
   445     memcpy(retval, io, sizeof (PHYSFS_Io));
   487     retval->opaque = newfh;
   446     retval->opaque = newfh;
   488     return retval;
   447     return retval;
   489 
   448     
   490 handleIo_dupe_failed:
   449 handleIo_dupe_failed:
   491     if (newfh)
   450     if (newfh)
   492     {
   451     {
   493         if (newfh->io != NULL) newfh->io->destroy(newfh->io);
   452         if (newfh->io != NULL) newfh->io->destroy(newfh->io);
   494         if (newfh->buffer != NULL) allocator.Free(newfh->buffer);
   453         if (newfh->buffer != NULL) allocator.Free(newfh->buffer);
   578     BAIL_IF_MACRO(!ecd.list, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
   537     BAIL_IF_MACRO(!ecd.list, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
   579     func(enumStringListCallback, &ecd);
   538     func(enumStringListCallback, &ecd);
   580 
   539 
   581     if (ecd.errcode)
   540     if (ecd.errcode)
   582     {
   541     {
   583         __PHYSFS_setError(ecd.errcode);
   542         PHYSFS_setErrorCode(ecd.errcode);
   584         return NULL;
   543         return NULL;
   585     } /* if */
   544     } /* if */
   586 
   545 
   587     ecd.list[ecd.size] = NULL;
   546     ecd.list[ecd.size] = NULL;
   588     return ecd.list;
   547     return ecd.list;
   653                    int (*cmpfn)(void *, size_t, size_t),
   612                    int (*cmpfn)(void *, size_t, size_t),
   654                    void (*swapfn)(void *, size_t, size_t))
   613                    void (*swapfn)(void *, size_t, size_t))
   655 {
   614 {
   656     /*
   615     /*
   657      * Quicksort w/ Bubblesort fallback algorithm inspired by code from here:
   616      * Quicksort w/ Bubblesort fallback algorithm inspired by code from here:
   658      *   http://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html
   617      *   https://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html
   659      */
   618      */
   660     if (max > 0)
   619     if (max > 0)
   661         __PHYSFS_quick_sort(entries, 0, max - 1, cmpfn, swapfn);
   620         __PHYSFS_quick_sort(entries, 0, max - 1, cmpfn, swapfn);
   662 } /* __PHYSFS_sort */
   621 } /* __PHYSFS_sort */
   663 
   622 
   690 
   649 
   691     return NULL;   /* no error available. */
   650     return NULL;   /* no error available. */
   692 } /* findErrorForCurrentThread */
   651 } /* findErrorForCurrentThread */
   693 
   652 
   694 
   653 
   695 void __PHYSFS_setError(const PHYSFS_ErrorCode errcode)
   654 /* this doesn't reset the error state. */
   696 {
   655 static inline PHYSFS_ErrorCode currentErrorCode(void)
   697     ErrState *err;
   656 {
   698 
   657     const ErrState *err = findErrorForCurrentThread();
   699     if (!errcode)
   658     return err ? err->code : PHYSFS_ERR_OK;
   700         return;
   659 } /* currentErrorCode */
   701 
       
   702     err = findErrorForCurrentThread();
       
   703     if (err == NULL)
       
   704     {
       
   705         err = (ErrState *) allocator.Malloc(sizeof (ErrState));
       
   706         if (err == NULL)
       
   707             return;   /* uhh...? */
       
   708 
       
   709         memset(err, '\0', sizeof (ErrState));
       
   710         err->tid = __PHYSFS_platformGetThreadID();
       
   711 
       
   712         if (errorLock != NULL)
       
   713             __PHYSFS_platformGrabMutex(errorLock);
       
   714 
       
   715         err->next = errorStates;
       
   716         errorStates = err;
       
   717 
       
   718         if (errorLock != NULL)
       
   719             __PHYSFS_platformReleaseMutex(errorLock);
       
   720     } /* if */
       
   721 
       
   722     err->code = errcode;
       
   723 } /* __PHYSFS_setError */
       
   724 
   660 
   725 
   661 
   726 PHYSFS_ErrorCode PHYSFS_getLastErrorCode(void)
   662 PHYSFS_ErrorCode PHYSFS_getLastErrorCode(void)
   727 {
   663 {
   728     ErrState *err = findErrorForCurrentThread();
   664     ErrState *err = findErrorForCurrentThread();
   746         case PHYSFS_ERR_UNSUPPORTED: return "unsupported";
   682         case PHYSFS_ERR_UNSUPPORTED: return "unsupported";
   747         case PHYSFS_ERR_PAST_EOF: return "past end of file";
   683         case PHYSFS_ERR_PAST_EOF: return "past end of file";
   748         case PHYSFS_ERR_FILES_STILL_OPEN: return "files still open";
   684         case PHYSFS_ERR_FILES_STILL_OPEN: return "files still open";
   749         case PHYSFS_ERR_INVALID_ARGUMENT: return "invalid argument";
   685         case PHYSFS_ERR_INVALID_ARGUMENT: return "invalid argument";
   750         case PHYSFS_ERR_NOT_MOUNTED: return "not mounted";
   686         case PHYSFS_ERR_NOT_MOUNTED: return "not mounted";
   751         case PHYSFS_ERR_NO_SUCH_PATH: return "no such path";
   687         case PHYSFS_ERR_NOT_FOUND: return "not found";
   752         case PHYSFS_ERR_SYMLINK_FORBIDDEN: return "symlinks are forbidden";
   688         case PHYSFS_ERR_SYMLINK_FORBIDDEN: return "symlinks are forbidden";
   753         case PHYSFS_ERR_NO_WRITE_DIR: return "write directory is not set";
   689         case PHYSFS_ERR_NO_WRITE_DIR: return "write directory is not set";
   754         case PHYSFS_ERR_OPEN_FOR_READING: return "file open for reading";
   690         case PHYSFS_ERR_OPEN_FOR_READING: return "file open for reading";
   755         case PHYSFS_ERR_OPEN_FOR_WRITING: return "file open for writing";
   691         case PHYSFS_ERR_OPEN_FOR_WRITING: return "file open for writing";
   756         case PHYSFS_ERR_NOT_A_FILE: return "not a file";
   692         case PHYSFS_ERR_NOT_A_FILE: return "not a file";
   762         case PHYSFS_ERR_NO_SPACE: return "no space available for writing";
   698         case PHYSFS_ERR_NO_SPACE: return "no space available for writing";
   763         case PHYSFS_ERR_BAD_FILENAME: return "filename is illegal or insecure";
   699         case PHYSFS_ERR_BAD_FILENAME: return "filename is illegal or insecure";
   764         case PHYSFS_ERR_BUSY: return "tried to modify a file the OS needs";
   700         case PHYSFS_ERR_BUSY: return "tried to modify a file the OS needs";
   765         case PHYSFS_ERR_DIR_NOT_EMPTY: return "directory isn't empty";
   701         case PHYSFS_ERR_DIR_NOT_EMPTY: return "directory isn't empty";
   766         case PHYSFS_ERR_OS_ERROR: return "OS reported an error";
   702         case PHYSFS_ERR_OS_ERROR: return "OS reported an error";
       
   703         case PHYSFS_ERR_DUPLICATE: return "duplicate resource";
       
   704         case PHYSFS_ERR_BAD_PASSWORD: return "bad password";
   767     } /* switch */
   705     } /* switch */
   768 
   706 
   769     return NULL;  /* don't know this error code. */
   707     return NULL;  /* don't know this error code. */
   770 } /* PHYSFS_getErrorByCode */
   708 } /* PHYSFS_getErrorByCode */
   771 
   709 
   772 
   710 
   773 void PHYSFS_setErrorCode(PHYSFS_ErrorCode code)
   711 void PHYSFS_setErrorCode(PHYSFS_ErrorCode errcode)
   774 {
   712 {
   775     __PHYSFS_setError(code);
   713     ErrState *err;
       
   714 
       
   715     if (!errcode)
       
   716         return;
       
   717 
       
   718     err = findErrorForCurrentThread();
       
   719     if (err == NULL)
       
   720     {
       
   721         err = (ErrState *) allocator.Malloc(sizeof (ErrState));
       
   722         if (err == NULL)
       
   723             return;   /* uhh...? */
       
   724 
       
   725         memset(err, '\0', sizeof (ErrState));
       
   726         err->tid = __PHYSFS_platformGetThreadID();
       
   727 
       
   728         if (errorLock != NULL)
       
   729             __PHYSFS_platformGrabMutex(errorLock);
       
   730 
       
   731         err->next = errorStates;
       
   732         errorStates = err;
       
   733 
       
   734         if (errorLock != NULL)
       
   735             __PHYSFS_platformReleaseMutex(errorLock);
       
   736     } /* if */
       
   737 
       
   738     err->code = errcode;
   776 } /* PHYSFS_setErrorCode */
   739 } /* PHYSFS_setErrorCode */
   777 
   740 
   778 
   741 
   779 const char *PHYSFS_getLastError(void)
   742 const char *PHYSFS_getLastError(void)
   780 {
   743 {
   864 static DirHandle *openDirectory(PHYSFS_Io *io, const char *d, int forWriting)
   827 static DirHandle *openDirectory(PHYSFS_Io *io, const char *d, int forWriting)
   865 {
   828 {
   866     DirHandle *retval = NULL;
   829     DirHandle *retval = NULL;
   867     const PHYSFS_Archiver **i;
   830     const PHYSFS_Archiver **i;
   868     const char *ext;
   831     const char *ext;
       
   832     int created_io = 0;
   869 
   833 
   870     assert((io != NULL) || (d != NULL));
   834     assert((io != NULL) || (d != NULL));
   871 
   835 
   872     if (io == NULL)
   836     if (io == NULL)
   873     {
   837     {
   874         /* DIR gets first shot (unlike the rest, it doesn't deal with files). */
   838         /* DIR gets first shot (unlike the rest, it doesn't deal with files). */
       
   839         extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR;
   875         retval = tryOpenDir(io, &__PHYSFS_Archiver_DIR, d, forWriting);
   840         retval = tryOpenDir(io, &__PHYSFS_Archiver_DIR, d, forWriting);
   876         if (retval != NULL)
   841         if (retval != NULL)
   877             return retval;
   842             return retval;
   878 
   843 
   879         io = __PHYSFS_createNativeIo(d, forWriting ? 'w' : 'r');
   844         io = __PHYSFS_createNativeIo(d, forWriting ? 'w' : 'r');
   880         BAIL_IF_MACRO(!io, ERRPASS, 0);
   845         BAIL_IF_MACRO(!io, ERRPASS, 0);
       
   846         created_io = 1;
   881     } /* if */
   847     } /* if */
   882 
   848 
   883     ext = find_filename_extension(d);
   849     ext = find_filename_extension(d);
   884     if (ext != NULL)
   850     if (ext != NULL)
   885     {
   851     {
   886         /* Look for archivers with matching file extensions first... */
   852         /* Look for archivers with matching file extensions first... */
   887         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
   853         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
   888         {
   854         {
   889             if (__PHYSFS_stricmpASCII(ext, (*i)->info.extension) == 0)
   855             if (__PHYSFS_utf8stricmp(ext, (*i)->info.extension) == 0)
   890                 retval = tryOpenDir(io, *i, d, forWriting);
   856                 retval = tryOpenDir(io, *i, d, forWriting);
   891         } /* for */
   857         } /* for */
   892 
   858 
   893         /* failing an exact file extension match, try all the others... */
   859         /* failing an exact file extension match, try all the others... */
   894         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
   860         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
   895         {
   861         {
   896             if (__PHYSFS_stricmpASCII(ext, (*i)->info.extension) != 0)
   862             if (__PHYSFS_utf8stricmp(ext, (*i)->info.extension) != 0)
   897                 retval = tryOpenDir(io, *i, d, forWriting);
   863                 retval = tryOpenDir(io, *i, d, forWriting);
   898         } /* for */
   864         } /* for */
   899     } /* if */
   865     } /* if */
   900 
   866 
   901     else  /* no extension? Try them all. */
   867     else  /* no extension? Try them all. */
   902     {
   868     {
   903         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
   869         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
   904             retval = tryOpenDir(io, *i, d, forWriting);
   870             retval = tryOpenDir(io, *i, d, forWriting);
   905     } /* else */
   871     } /* else */
       
   872 
       
   873     if ((!retval) && (created_io))
       
   874         io->destroy(io);
   906 
   875 
   907     BAIL_IF_MACRO(!retval, PHYSFS_ERR_UNSUPPORTED, NULL);
   876     BAIL_IF_MACRO(!retval, PHYSFS_ERR_UNSUPPORTED, NULL);
   908     return retval;
   877     return retval;
   909 } /* openDirectory */
   878 } /* openDirectory */
   910 
   879 
  1119     errorLock = stateLock = NULL;
  1088     errorLock = stateLock = NULL;
  1120     return 0;  /* failed. */
  1089     return 0;  /* failed. */
  1121 } /* initializeMutexes */
  1090 } /* initializeMutexes */
  1122 
  1091 
  1123 
  1092 
  1124 static void setDefaultAllocator(void);
  1093 static int doRegisterArchiver(const PHYSFS_Archiver *_archiver);
  1125 
  1094 
  1126 static int initStaticArchivers(void)
  1095 static int initStaticArchivers(void)
  1127 {
  1096 {
  1128     const size_t numStaticArchivers = __PHYSFS_ARRAYLEN(staticArchivers);
  1097     #define REGISTER_STATIC_ARCHIVER(arc) { \
  1129     const size_t len = numStaticArchivers * sizeof (void *);
  1098         extern const PHYSFS_Archiver __PHYSFS_Archiver_##arc; \
  1130     size_t i;
  1099         if (!doRegisterArchiver(&__PHYSFS_Archiver_##arc)) { \
  1131 
  1100             return 0; \
  1132     assert(numStaticArchivers > 0);  /* seriously, none at all?! */
  1101         } \
  1133     assert(staticArchivers[numStaticArchivers - 1] == NULL);
  1102     }
  1134 
  1103 
  1135     archiveInfo = (const PHYSFS_ArchiveInfo **) allocator.Malloc(len);
  1104     #if PHYSFS_SUPPORTS_ZIP
  1136     BAIL_IF_MACRO(!archiveInfo, PHYSFS_ERR_OUT_OF_MEMORY, 0);
  1105         REGISTER_STATIC_ARCHIVER(ZIP);
  1137     archivers = (const PHYSFS_Archiver **) allocator.Malloc(len);
  1106     #endif
  1138     BAIL_IF_MACRO(!archivers, PHYSFS_ERR_OUT_OF_MEMORY, 0);
  1107     #if PHYSFS_SUPPORTS_7Z
  1139 
  1108         REGISTER_STATIC_ARCHIVER(LZMA);
  1140     for (i = 0; i < numStaticArchivers - 1; i++)
  1109     #endif
  1141         archiveInfo[i] = &staticArchivers[i]->info;
  1110     #if PHYSFS_SUPPORTS_GRP
  1142     archiveInfo[numStaticArchivers - 1] = NULL;
  1111         REGISTER_STATIC_ARCHIVER(GRP);
  1143 
  1112     #endif
  1144     memcpy(archivers, staticArchivers, len);
  1113     #if PHYSFS_SUPPORTS_QPAK
       
  1114         REGISTER_STATIC_ARCHIVER(QPAK);
       
  1115     #endif
       
  1116     #if PHYSFS_SUPPORTS_HOG
       
  1117         REGISTER_STATIC_ARCHIVER(HOG);
       
  1118     #endif
       
  1119     #if PHYSFS_SUPPORTS_MVL
       
  1120         REGISTER_STATIC_ARCHIVER(MVL);
       
  1121     #endif
       
  1122     #if PHYSFS_SUPPORTS_WAD
       
  1123         REGISTER_STATIC_ARCHIVER(WAD);
       
  1124     #endif
       
  1125     #if PHYSFS_SUPPORTS_SLB
       
  1126         REGISTER_STATIC_ARCHIVER(SLB);
       
  1127     #endif
       
  1128     #if PHYSFS_SUPPORTS_ISO9660
       
  1129         REGISTER_STATIC_ARCHIVER(ISO9660);
       
  1130     #endif
       
  1131 
       
  1132     #undef REGISTER_STATIC_ARCHIVER
  1145 
  1133 
  1146     return 1;
  1134     return 1;
  1147 } /* initStaticArchivers */
  1135 } /* initStaticArchivers */
  1148 
  1136 
  1149 
  1137 
       
  1138 static void setDefaultAllocator(void);
  1150 static int doDeinit(void);
  1139 static int doDeinit(void);
  1151 
  1140 
  1152 int PHYSFS_init(const char *argv0)
  1141 int PHYSFS_init(const char *argv0)
  1153 {
  1142 {
  1154     BAIL_IF_MACRO(initialized, PHYSFS_ERR_IS_INITIALIZED, 0);
  1143     BAIL_IF_MACRO(initialized, PHYSFS_ERR_IS_INITIALIZED, 0);
  1181     if (!initStaticArchivers()) goto initFailed;
  1170     if (!initStaticArchivers()) goto initFailed;
  1182 
  1171 
  1183     initialized = 1;
  1172     initialized = 1;
  1184 
  1173 
  1185     /* This makes sure that the error subsystem is initialized. */
  1174     /* This makes sure that the error subsystem is initialized. */
  1186     __PHYSFS_setError(PHYSFS_getLastErrorCode());
  1175     PHYSFS_setErrorCode(PHYSFS_getLastErrorCode());
  1187 
  1176 
  1188     return 1;
  1177     return 1;
  1189 
  1178 
  1190 initFailed:
  1179 initFailed:
  1191     doDeinit();
  1180     doDeinit();
  1202     for (i = *list; i != NULL; i = next)
  1191     for (i = *list; i != NULL; i = next)
  1203     {
  1192     {
  1204         PHYSFS_Io *io = i->io;
  1193         PHYSFS_Io *io = i->io;
  1205         next = i->next;
  1194         next = i->next;
  1206 
  1195 
  1207         if (!io->flush(io))
  1196         if (io->flush && !io->flush(io))
  1208         {
  1197         {
  1209             *list = i;
  1198             *list = i;
  1210             return 0;
  1199             return 0;
  1211         } /* if */
  1200         } /* if */
  1212 
  1201 
  1237         searchPath = NULL;
  1226         searchPath = NULL;
  1238     } /* if */
  1227     } /* if */
  1239 } /* freeSearchPath */
  1228 } /* freeSearchPath */
  1240 
  1229 
  1241 
  1230 
       
  1231 /* MAKE SURE you hold stateLock before calling this! */
       
  1232 static int archiverInUse(const PHYSFS_Archiver *arc, const DirHandle *list)
       
  1233 {
       
  1234     const DirHandle *i;
       
  1235     for (i = list; i != NULL; i = i->next)
       
  1236     {
       
  1237         if (i->funcs == arc)
       
  1238             return 1;
       
  1239     } /* for */
       
  1240 
       
  1241     return 0;  /* not in use */
       
  1242 } /* archiverInUse */
       
  1243 
       
  1244 
       
  1245 /* MAKE SURE you hold stateLock before calling this! */
       
  1246 static int doDeregisterArchiver(const size_t idx)
       
  1247 {
       
  1248     const size_t len = (numArchivers - idx) * sizeof (void *);
       
  1249     const PHYSFS_ArchiveInfo *info = archiveInfo[idx];
       
  1250     const PHYSFS_Archiver *arc = archivers[idx];
       
  1251 
       
  1252     /* make sure nothing is still using this archiver */
       
  1253     if (archiverInUse(arc, searchPath) || archiverInUse(arc, writeDir))
       
  1254         BAIL_MACRO(PHYSFS_ERR_FILES_STILL_OPEN, 0);
       
  1255 
       
  1256     allocator.Free((void *) info->extension);
       
  1257     allocator.Free((void *) info->description);
       
  1258     allocator.Free((void *) info->author);
       
  1259     allocator.Free((void *) info->url);
       
  1260     allocator.Free((void *) arc);
       
  1261 
       
  1262     memmove(&archiveInfo[idx], &archiveInfo[idx+1], len);
       
  1263     memmove(&archivers[idx], &archivers[idx+1], len);
       
  1264 
       
  1265     assert(numArchivers > 0);
       
  1266     numArchivers--;
       
  1267 
       
  1268     return 1;
       
  1269 } /* doDeregisterArchiver */
       
  1270 
       
  1271 
       
  1272 /* Does NOT hold the state lock; we're shutting down. */
       
  1273 static void freeArchivers(void)
       
  1274 {
       
  1275     while (numArchivers > 0)
       
  1276     {
       
  1277         if (!doDeregisterArchiver(numArchivers - 1))
       
  1278             assert(!"nothing should be mounted during shutdown.");
       
  1279     } /* while */
       
  1280 
       
  1281     allocator.Free(archivers);
       
  1282     allocator.Free(archiveInfo);
       
  1283     archivers = NULL;
       
  1284     archiveInfo = NULL;
       
  1285 } /* freeArchivers */
       
  1286 
       
  1287 
  1242 static int doDeinit(void)
  1288 static int doDeinit(void)
  1243 {
  1289 {
  1244     BAIL_IF_MACRO(!__PHYSFS_platformDeinit(), ERRPASS, 0);
       
  1245 
       
  1246     closeFileHandleList(&openWriteList);
  1290     closeFileHandleList(&openWriteList);
  1247     BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), PHYSFS_ERR_FILES_STILL_OPEN, 0);
  1291     BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), PHYSFS_ERR_FILES_STILL_OPEN, 0);
  1248 
  1292 
  1249     freeSearchPath();
  1293     freeSearchPath();
       
  1294     freeArchivers();
  1250     freeErrorStates();
  1295     freeErrorStates();
  1251 
  1296 
  1252     if (baseDir != NULL)
  1297     if (baseDir != NULL)
  1253     {
  1298     {
  1254         allocator.Free(baseDir);
  1299         allocator.Free(baseDir);
  1287 
  1332 
  1288     if (allocator.Deinit != NULL)
  1333     if (allocator.Deinit != NULL)
  1289         allocator.Deinit();
  1334         allocator.Deinit();
  1290 
  1335 
  1291     errorLock = stateLock = NULL;
  1336     errorLock = stateLock = NULL;
       
  1337 
       
  1338     /* !!! FIXME: what on earth are you supposed to do if this fails? */
       
  1339     BAIL_IF_MACRO(!__PHYSFS_platformDeinit(), ERRPASS, 0);
       
  1340 
  1292     return 1;
  1341     return 1;
  1293 } /* doDeinit */
  1342 } /* doDeinit */
  1294 
  1343 
  1295 
  1344 
  1296 int PHYSFS_deinit(void)
  1345 int PHYSFS_deinit(void)
  1302 
  1351 
  1303 int PHYSFS_isInit(void)
  1352 int PHYSFS_isInit(void)
  1304 {
  1353 {
  1305     return initialized;
  1354     return initialized;
  1306 } /* PHYSFS_isInit */
  1355 } /* PHYSFS_isInit */
       
  1356 
       
  1357 
       
  1358 char *__PHYSFS_strdup(const char *str)
       
  1359 {
       
  1360     char *retval = (char *) allocator.Malloc(strlen(str) + 1);
       
  1361     if (retval)
       
  1362         strcpy(retval, str);
       
  1363     return retval;
       
  1364 } /* __PHYSFS_strdup */
       
  1365 
       
  1366 
       
  1367 PHYSFS_uint32 __PHYSFS_hashString(const char *str, size_t len)
       
  1368 {
       
  1369     PHYSFS_uint32 hash = 5381;
       
  1370     while (len--)
       
  1371         hash = ((hash << 5) + hash) ^ *(str++);
       
  1372     return hash;
       
  1373 } /* __PHYSFS_hashString */
       
  1374 
       
  1375 
       
  1376 /* MAKE SURE you hold stateLock before calling this! */
       
  1377 static int doRegisterArchiver(const PHYSFS_Archiver *_archiver)
       
  1378 {
       
  1379     const PHYSFS_uint32 maxver = CURRENT_PHYSFS_ARCHIVER_API_VERSION;
       
  1380     const size_t len = (numArchivers + 2) * sizeof (void *);
       
  1381     PHYSFS_Archiver *archiver = NULL;
       
  1382     PHYSFS_ArchiveInfo *info = NULL;
       
  1383     const char *ext = NULL;
       
  1384     void *ptr = NULL;
       
  1385     size_t i;
       
  1386 
       
  1387     BAIL_IF_MACRO(!_archiver, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1388     BAIL_IF_MACRO(_archiver->version > maxver, PHYSFS_ERR_UNSUPPORTED, 0);
       
  1389     BAIL_IF_MACRO(!_archiver->info.extension, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1390     BAIL_IF_MACRO(!_archiver->info.description, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1391     BAIL_IF_MACRO(!_archiver->info.author, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1392     BAIL_IF_MACRO(!_archiver->info.url, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1393     BAIL_IF_MACRO(!_archiver->openArchive, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1394     BAIL_IF_MACRO(!_archiver->enumerateFiles, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1395     BAIL_IF_MACRO(!_archiver->openRead, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1396     BAIL_IF_MACRO(!_archiver->openWrite, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1397     BAIL_IF_MACRO(!_archiver->openAppend, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1398     BAIL_IF_MACRO(!_archiver->remove, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1399     BAIL_IF_MACRO(!_archiver->mkdir, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1400     BAIL_IF_MACRO(!_archiver->closeArchive, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1401     BAIL_IF_MACRO(!_archiver->stat, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1402 
       
  1403     ext = _archiver->info.extension;
       
  1404     for (i = 0; i < numArchivers; i++)
       
  1405     {
       
  1406         if (__PHYSFS_utf8stricmp(archiveInfo[i]->extension, ext) == 0)
       
  1407             BAIL_MACRO(PHYSFS_ERR_DUPLICATE, 0);  /* !!! FIXME: better error? ERR_IN_USE? */
       
  1408     } /* for */
       
  1409 
       
  1410     /* make a copy of the data. */
       
  1411     archiver = (PHYSFS_Archiver *) allocator.Malloc(sizeof (*archiver));
       
  1412     GOTO_IF_MACRO(!archiver, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
       
  1413 
       
  1414     /* Must copy sizeof (OLD_VERSION_OF_STRUCT) when version changes! */
       
  1415     memcpy(archiver, _archiver, sizeof (*archiver));
       
  1416 
       
  1417     info = (PHYSFS_ArchiveInfo *) &archiver->info;
       
  1418     memset(info, '\0', sizeof (*info));  /* NULL in case an alloc fails. */
       
  1419     #define CPYSTR(item) \
       
  1420         info->item = __PHYSFS_strdup(_archiver->info.item); \
       
  1421         GOTO_IF_MACRO(!info->item, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
       
  1422     CPYSTR(extension);
       
  1423     CPYSTR(description);
       
  1424     CPYSTR(author);
       
  1425     CPYSTR(url);
       
  1426     info->supportsSymlinks = _archiver->info.supportsSymlinks;
       
  1427     #undef CPYSTR
       
  1428 
       
  1429     ptr = allocator.Realloc(archiveInfo, len);
       
  1430     GOTO_IF_MACRO(!ptr, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
       
  1431     archiveInfo = (const PHYSFS_ArchiveInfo **) ptr;
       
  1432 
       
  1433     ptr = allocator.Realloc(archivers, len);
       
  1434     GOTO_IF_MACRO(!ptr, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
       
  1435     archivers = (const PHYSFS_Archiver **) ptr;
       
  1436 
       
  1437     archiveInfo[numArchivers] = info;
       
  1438     archiveInfo[numArchivers + 1] = NULL;
       
  1439 
       
  1440     archivers[numArchivers] = archiver;
       
  1441     archivers[numArchivers + 1] = NULL;
       
  1442 
       
  1443     numArchivers++;
       
  1444 
       
  1445     return 1;
       
  1446 
       
  1447 regfailed:
       
  1448     if (info != NULL)
       
  1449     {
       
  1450         allocator.Free((void *) info->extension);
       
  1451         allocator.Free((void *) info->description);
       
  1452         allocator.Free((void *) info->author);
       
  1453         allocator.Free((void *) info->url);
       
  1454     } /* if */
       
  1455     allocator.Free(archiver);
       
  1456 
       
  1457     return 0;
       
  1458 } /* doRegisterArchiver */
       
  1459 
       
  1460 
       
  1461 int PHYSFS_registerArchiver(const PHYSFS_Archiver *archiver)
       
  1462 {
       
  1463     int retval;
       
  1464     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
       
  1465     __PHYSFS_platformGrabMutex(stateLock);
       
  1466     retval = doRegisterArchiver(archiver);
       
  1467     __PHYSFS_platformReleaseMutex(stateLock);
       
  1468     return retval;
       
  1469 } /* PHYSFS_registerArchiver */
       
  1470 
       
  1471 
       
  1472 int PHYSFS_deregisterArchiver(const char *ext)
       
  1473 {
       
  1474     size_t i;
       
  1475 
       
  1476     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
       
  1477     BAIL_IF_MACRO(!ext, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1478 
       
  1479     __PHYSFS_platformGrabMutex(stateLock);
       
  1480     for (i = 0; i < numArchivers; i++)
       
  1481     {
       
  1482         if (__PHYSFS_utf8stricmp(archiveInfo[i]->extension, ext) == 0)
       
  1483         {
       
  1484             const int retval = doDeregisterArchiver(i);
       
  1485             __PHYSFS_platformReleaseMutex(stateLock);
       
  1486             return retval;
       
  1487         } /* if */
       
  1488     } /* for */
       
  1489     __PHYSFS_platformReleaseMutex(stateLock);
       
  1490 
       
  1491     BAIL_MACRO(PHYSFS_ERR_NOT_FOUND, 0);
       
  1492 } /* PHYSFS_deregisterArchiver */
  1307 
  1493 
  1308 
  1494 
  1309 const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void)
  1495 const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void)
  1310 {
  1496 {
  1311     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, NULL);
  1497     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, NULL);
  1349 {
  1535 {
  1350     const char dirsep = __PHYSFS_platformDirSeparator;
  1536     const char dirsep = __PHYSFS_platformDirSeparator;
  1351     PHYSFS_Stat statbuf;
  1537     PHYSFS_Stat statbuf;
  1352     char *ptr = NULL;
  1538     char *ptr = NULL;
  1353     char *endstr = NULL;
  1539     char *endstr = NULL;
  1354     int exists = 0;
       
  1355 
  1540 
  1356     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
  1541     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
  1357     BAIL_IF_MACRO(!org, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
  1542     BAIL_IF_MACRO(!org, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
  1358     BAIL_IF_MACRO(*org == '\0', PHYSFS_ERR_INVALID_ARGUMENT, NULL);
  1543     BAIL_IF_MACRO(*org == '\0', PHYSFS_ERR_INVALID_ARGUMENT, NULL);
  1359     BAIL_IF_MACRO(!app, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
  1544     BAIL_IF_MACRO(!app, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
  1366     assert(strlen(prefDir) > 0);
  1551     assert(strlen(prefDir) > 0);
  1367     endstr = prefDir + (strlen(prefDir) - 1);
  1552     endstr = prefDir + (strlen(prefDir) - 1);
  1368     assert(*endstr == dirsep);
  1553     assert(*endstr == dirsep);
  1369     *endstr = '\0';  /* mask out the final dirsep for now. */
  1554     *endstr = '\0';  /* mask out the final dirsep for now. */
  1370 
  1555 
  1371     if (!__PHYSFS_platformStat(prefDir, &exists, &statbuf))
  1556     if (!__PHYSFS_platformStat(prefDir, &statbuf))
  1372     {
  1557     {
  1373         for (ptr = strchr(prefDir, dirsep); ptr; ptr = strchr(ptr+1, dirsep))
  1558         for (ptr = strchr(prefDir, dirsep); ptr; ptr = strchr(ptr+1, dirsep))
  1374         {
  1559         {
  1375             *ptr = '\0';
  1560             *ptr = '\0';
  1376             __PHYSFS_platformMkDir(prefDir);
  1561             __PHYSFS_platformMkDir(prefDir);
  1696         {
  1881         {
  1697             size_t l = strlen(*i);
  1882             size_t l = strlen(*i);
  1698             if ((l > extlen) && ((*i)[l - extlen - 1] == '.'))
  1883             if ((l > extlen) && ((*i)[l - extlen - 1] == '.'))
  1699             {
  1884             {
  1700                 ext = (*i) + (l - extlen);
  1885                 ext = (*i) + (l - extlen);
  1701                 if (__PHYSFS_stricmpASCII(ext, archiveExt) == 0)
  1886                 if (__PHYSFS_utf8stricmp(ext, archiveExt) == 0)
  1702                     setSaneCfgAddPath(*i, l, dirsep, archivesFirst);
  1887                     setSaneCfgAddPath(*i, l, dirsep, archivesFirst);
  1703             } /* if */
  1888             } /* if */
  1704         } /* for */
  1889         } /* for */
  1705 
  1890 
  1706         PHYSFS_freeList(rc);
  1891         PHYSFS_freeList(rc);
  1757     {
  1942     {
  1758         size_t mntpntlen = strlen(h->mountPoint);
  1943         size_t mntpntlen = strlen(h->mountPoint);
  1759         size_t len = strlen(fname);
  1944         size_t len = strlen(fname);
  1760         assert(mntpntlen > 1); /* root mount points should be NULL. */
  1945         assert(mntpntlen > 1); /* root mount points should be NULL. */
  1761         /* not under the mountpoint, so skip this archive. */
  1946         /* not under the mountpoint, so skip this archive. */
  1762         BAIL_IF_MACRO(len < mntpntlen-1, PHYSFS_ERR_NO_SUCH_PATH, 0);
  1947         BAIL_IF_MACRO(len < mntpntlen-1, PHYSFS_ERR_NOT_FOUND, 0);
  1763         /* !!! FIXME: Case insensitive? */
  1948         /* !!! FIXME: Case insensitive? */
  1764         retval = strncmp(h->mountPoint, fname, mntpntlen-1);
  1949         retval = strncmp(h->mountPoint, fname, mntpntlen-1);
  1765         BAIL_IF_MACRO(retval != 0, PHYSFS_ERR_NO_SUCH_PATH, 0);
  1950         BAIL_IF_MACRO(retval != 0, PHYSFS_ERR_NOT_FOUND, 0);
  1766         if (len > mntpntlen-1)  /* corner case... */
  1951         if (len > mntpntlen-1)  /* corner case... */
  1767             BAIL_IF_MACRO(fname[mntpntlen-1]!='/', PHYSFS_ERR_NO_SUCH_PATH, 0);
  1952             BAIL_IF_MACRO(fname[mntpntlen-1]!='/', PHYSFS_ERR_NOT_FOUND, 0);
  1768         fname += mntpntlen-1;  /* move to start of actual archive path. */
  1953         fname += mntpntlen-1;  /* move to start of actual archive path. */
  1769         if (*fname == '/')
  1954         if (*fname == '/')
  1770             fname++;
  1955             fname++;
  1771         *_fname = fname;  /* skip mountpoint for later use. */
  1956         *_fname = fname;  /* skip mountpoint for later use. */
  1772         retval = 1;  /* may be reset, below. */
  1957         retval = 1;  /* may be reset, below. */
  1780             PHYSFS_Stat statbuf;
  1965             PHYSFS_Stat statbuf;
  1781             int rc = 0;
  1966             int rc = 0;
  1782             end = strchr(start, '/');
  1967             end = strchr(start, '/');
  1783 
  1968 
  1784             if (end != NULL) *end = '\0';
  1969             if (end != NULL) *end = '\0';
  1785             rc = h->funcs->stat(h->opaque, fname, &retval, &statbuf);
  1970             rc = h->funcs->stat(h->opaque, fname, &statbuf);
  1786             if (rc)
  1971             if (rc)
  1787                 rc = (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK);
  1972                 rc = (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK);
       
  1973             else if (currentErrorCode() == PHYSFS_ERR_NOT_FOUND)
       
  1974                 retval = 0;
       
  1975 
  1788             if (end != NULL) *end = '/';
  1976             if (end != NULL) *end = '/';
  1789 
  1977 
  1790             /* insecure path (has a disallowed symlink in it)? */
  1978             /* insecure path (has a disallowed symlink in it)? */
  1791             BAIL_IF_MACRO(rc, PHYSFS_ERR_SYMLINK_FORBIDDEN, 0);
  1979             BAIL_IF_MACRO(rc, PHYSFS_ERR_SYMLINK_FORBIDDEN, 0);
  1792 
  1980 
  1838 
  2026 
  1839         /* only check for existance if all parent dirs existed, too... */
  2027         /* only check for existance if all parent dirs existed, too... */
  1840         if (exists)
  2028         if (exists)
  1841         {
  2029         {
  1842             PHYSFS_Stat statbuf;
  2030             PHYSFS_Stat statbuf;
  1843             const int rc = h->funcs->stat(h->opaque, dname, &exists, &statbuf);
  2031             const int rc = h->funcs->stat(h->opaque, dname, &statbuf);
       
  2032             if ((!rc) && (currentErrorCode() == PHYSFS_ERR_NOT_FOUND))
       
  2033                 exists = 0;
  1844             retval = ((rc) && (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY));
  2034             retval = ((rc) && (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY));
  1845         } /* if */
  2035         } /* if */
  1846 
  2036 
  1847         if (!exists)
  2037         if (!exists)
  1848             retval = h->funcs->mkdir(h->opaque, dname);
  2038             retval = h->funcs->mkdir(h->opaque, dname);
  1935                 break;
  2125                 break;
  1936             } /* if */
  2126             } /* if */
  1937             else if (verifyPath(i, &arcfname, 0))
  2127             else if (verifyPath(i, &arcfname, 0))
  1938             {
  2128             {
  1939                 PHYSFS_Stat statbuf;
  2129                 PHYSFS_Stat statbuf;
  1940                 int exists = 0;
  2130                 if (i->funcs->stat(i->opaque, arcfname, &statbuf))
  1941                 if (i->funcs->stat(i->opaque, arcfname, &exists, &statbuf))
       
  1942                 {
  2131                 {
  1943                     if (exists)
  2132                     retval = i->dirName;
  1944                         retval = i->dirName;
       
  1945                     break;
  2133                     break;
  1946                 } /* if */
  2134                 } /* if */
  1947             } /* if */
  2135             } /* if */
  1948         } /* for */
  2136         } /* for */
  1949         __PHYSFS_platformReleaseMutex(stateLock);
  2137         __PHYSFS_platformReleaseMutex(stateLock);
  2058     callback(data, _fname, ptr);
  2246     callback(data, _fname, ptr);
  2059     __PHYSFS_smallFree(mountPoint);
  2247     __PHYSFS_smallFree(mountPoint);
  2060 } /* enumerateFromMountPoint */
  2248 } /* enumerateFromMountPoint */
  2061 
  2249 
  2062 
  2250 
       
  2251 typedef struct SymlinkFilterData
       
  2252 {
       
  2253     PHYSFS_EnumFilesCallback callback;
       
  2254     void *callbackData;
       
  2255     DirHandle *dirhandle;
       
  2256 } SymlinkFilterData;
       
  2257 
       
  2258 /* !!! FIXME: broken if in a virtual mountpoint (stat call fails). */
       
  2259 static void enumCallbackFilterSymLinks(void *_data, const char *origdir,
       
  2260                                        const char *fname)
       
  2261 {
       
  2262     const char *trimmedDir = (*origdir == '/') ? (origdir+1) : origdir;
       
  2263     const size_t slen = strlen(trimmedDir) + strlen(fname) + 2;
       
  2264     char *path = (char *) __PHYSFS_smallAlloc(slen);
       
  2265 
       
  2266     if (path != NULL)
       
  2267     {
       
  2268         SymlinkFilterData *data = (SymlinkFilterData *) _data;
       
  2269         const DirHandle *dh = data->dirhandle;
       
  2270         PHYSFS_Stat statbuf;
       
  2271 
       
  2272         sprintf(path, "%s%s%s", trimmedDir, *trimmedDir ? "/" : "", fname);
       
  2273         if (dh->funcs->stat(dh->opaque, path, &statbuf))
       
  2274         {
       
  2275             /* Pass it on to the application if it's not a symlink. */
       
  2276             if (statbuf.filetype != PHYSFS_FILETYPE_SYMLINK)
       
  2277                 data->callback(data->callbackData, origdir, fname);
       
  2278         } /* if */
       
  2279 
       
  2280         __PHYSFS_smallFree(path);
       
  2281     } /* if */
       
  2282 } /* enumCallbackFilterSymLinks */
       
  2283 
       
  2284 
  2063 /* !!! FIXME: this should report error conditions. */
  2285 /* !!! FIXME: this should report error conditions. */
  2064 void PHYSFS_enumerateFilesCallback(const char *_fname,
  2286 void PHYSFS_enumerateFilesCallback(const char *_fname,
  2065                                    PHYSFS_EnumFilesCallback callback,
  2287                                    PHYSFS_EnumFilesCallback callback,
  2066                                    void *data)
  2288                                    void *data)
  2067 {
  2289 {
  2076     BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, ) /*0*/;
  2298     BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, ) /*0*/;
  2077 
  2299 
  2078     if (sanitizePlatformIndependentPath(_fname, fname))
  2300     if (sanitizePlatformIndependentPath(_fname, fname))
  2079     {
  2301     {
  2080         DirHandle *i;
  2302         DirHandle *i;
  2081         int noSyms;
  2303         SymlinkFilterData filterdata;
  2082 
  2304 
  2083         __PHYSFS_platformGrabMutex(stateLock);
  2305         __PHYSFS_platformGrabMutex(stateLock);
  2084         noSyms = !allowSymLinks;
  2306 
       
  2307         if (!allowSymLinks)
       
  2308         {
       
  2309             memset(&filterdata, '\0', sizeof (filterdata));
       
  2310             filterdata.callback = callback;
       
  2311             filterdata.callbackData = data;
       
  2312         } /* if */
       
  2313 
  2085         for (i = searchPath; i != NULL; i = i->next)
  2314         for (i = searchPath; i != NULL; i = i->next)
  2086         {
  2315         {
  2087             char *arcfname = fname;
  2316             char *arcfname = fname;
  2088             if (partOfMountPoint(i, arcfname))
  2317             if (partOfMountPoint(i, arcfname))
  2089                 enumerateFromMountPoint(i, arcfname, callback, _fname, data);
  2318                 enumerateFromMountPoint(i, arcfname, callback, _fname, data);
  2090 
  2319 
  2091             else if (verifyPath(i, &arcfname, 0))
  2320             else if (verifyPath(i, &arcfname, 0))
  2092             {
  2321             {
  2093                 i->funcs->enumerateFiles(i->opaque, arcfname, noSyms,
  2322                 if ((!allowSymLinks) && (i->funcs->info.supportsSymlinks))
  2094                                          callback, _fname, data);
  2323                 {
       
  2324                     filterdata.dirhandle = i;
       
  2325                     i->funcs->enumerateFiles(i->opaque, arcfname,
       
  2326                                              enumCallbackFilterSymLinks,
       
  2327                                              _fname, &filterdata);
       
  2328                 } /* if */
       
  2329                 else
       
  2330                 {
       
  2331                     i->funcs->enumerateFiles(i->opaque, arcfname,
       
  2332                                              callback, _fname, data);
       
  2333                 } /* else */
  2095             } /* else if */
  2334             } /* else if */
  2096         } /* for */
  2335         } /* for */
  2097         __PHYSFS_platformReleaseMutex(stateLock);
  2336         __PHYSFS_platformReleaseMutex(stateLock);
  2098     } /* if */
  2337     } /* if */
  2099 
  2338 
  2210     fname = (char *) __PHYSFS_smallAlloc(len);
  2449     fname = (char *) __PHYSFS_smallAlloc(len);
  2211     BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
  2450     BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
  2212 
  2451 
  2213     if (sanitizePlatformIndependentPath(_fname, fname))
  2452     if (sanitizePlatformIndependentPath(_fname, fname))
  2214     {
  2453     {
  2215         int fileExists = 0;
       
  2216         DirHandle *i = NULL;
  2454         DirHandle *i = NULL;
  2217         PHYSFS_Io *io = NULL;
  2455         PHYSFS_Io *io = NULL;
  2218 
  2456 
  2219         __PHYSFS_platformGrabMutex(stateLock);
  2457         __PHYSFS_platformGrabMutex(stateLock);
  2220 
  2458 
  2221         GOTO_IF_MACRO(!searchPath, PHYSFS_ERR_NO_SUCH_PATH, openReadEnd);
  2459         GOTO_IF_MACRO(!searchPath, PHYSFS_ERR_NOT_FOUND, openReadEnd);
  2222 
  2460 
  2223         for (i = searchPath; (i != NULL) && (!fileExists); i = i->next)
  2461         for (i = searchPath; i != NULL; i = i->next)
  2224         {
  2462         {
  2225             char *arcfname = fname;
  2463             char *arcfname = fname;
  2226             if (verifyPath(i, &arcfname, 0))
  2464             if (verifyPath(i, &arcfname, 0))
  2227             {
  2465             {
  2228                 io = i->funcs->openRead(i->opaque, arcfname, &fileExists);
  2466                 io = i->funcs->openRead(i->opaque, arcfname);
  2229                 if (io)
  2467                 if (io)
  2230                     break;
  2468                     break;
  2231             } /* if */
  2469             } /* if */
  2232         } /* for */
  2470         } /* for */
  2233 
  2471 
  2328     if (buffered >= len)  /* totally in the buffer, just copy and return! */
  2566     if (buffered >= len)  /* totally in the buffer, just copy and return! */
  2329     {
  2567     {
  2330         memcpy(buffer, fh->buffer + fh->bufpos, (size_t) len);
  2568         memcpy(buffer, fh->buffer + fh->bufpos, (size_t) len);
  2331         fh->bufpos += (PHYSFS_uint32) len;
  2569         fh->bufpos += (PHYSFS_uint32) len;
  2332         return (PHYSFS_sint64) len;
  2570         return (PHYSFS_sint64) len;
  2333     } /* else if */
  2571     } /* if */
  2334 
  2572 
  2335     if (buffered > 0) /* partially in the buffer... */
  2573     else if (buffered > 0) /* partially in the buffer... */
  2336     {
  2574     {
  2337         memcpy(buffer, fh->buffer + fh->bufpos, (size_t) buffered);
  2575         memcpy(buffer, fh->buffer + fh->bufpos, (size_t) buffered);
  2338         buffer = ((PHYSFS_uint8 *) buffer) + buffered;
  2576         buffer = ((PHYSFS_uint8 *) buffer) + buffered;
  2339         len -= buffered;
  2577         len -= buffered;
  2340         retval = buffered;
  2578         retval = buffered;
  2341         fh->buffill = fh->bufpos = 0;
       
  2342     } /* if */
  2579     } /* if */
  2343 
  2580 
  2344     /* if you got here, the buffer is drained and we still need bytes. */
  2581     /* if you got here, the buffer is drained and we still need bytes. */
  2345     assert(len > 0);
  2582     assert(len > 0);
       
  2583 
       
  2584     fh->buffill = fh->bufpos = 0;
  2346 
  2585 
  2347     io = fh->io;
  2586     io = fh->io;
  2348     if (len >= fh->bufsize)  /* need more than the buffer takes. */
  2587     if (len >= fh->bufsize)  /* need more than the buffer takes. */
  2349     {
  2588     {
  2350         /* leave buffer empty, go right to output instead. */
  2589         /* leave buffer empty, go right to output instead. */
  2580     /* dump buffer to disk. */
  2819     /* dump buffer to disk. */
  2581     io = fh->io;
  2820     io = fh->io;
  2582     rc = io->write(io, fh->buffer + fh->bufpos, fh->buffill - fh->bufpos);
  2821     rc = io->write(io, fh->buffer + fh->bufpos, fh->buffill - fh->bufpos);
  2583     BAIL_IF_MACRO(rc <= 0, ERRPASS, 0);
  2822     BAIL_IF_MACRO(rc <= 0, ERRPASS, 0);
  2584     fh->bufpos = fh->buffill = 0;
  2823     fh->bufpos = fh->buffill = 0;
  2585     return io->flush(io);
  2824     return io->flush ? io->flush(io) : 1;
  2586 } /* PHYSFS_flush */
  2825 } /* PHYSFS_flush */
  2587 
  2826 
  2588 
  2827 
  2589 int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat)
  2828 int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat)
  2590 {
  2829 {
  2591     int retval = 0;
  2830     int retval = 0;
  2592     char *fname;
  2831     char *fname;
  2593     size_t len;
  2832     size_t len;
  2594 
  2833 
  2595     BAIL_IF_MACRO(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, -1);
  2834     BAIL_IF_MACRO(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
  2596     BAIL_IF_MACRO(!stat, PHYSFS_ERR_INVALID_ARGUMENT, -1);
  2835     BAIL_IF_MACRO(!stat, PHYSFS_ERR_INVALID_ARGUMENT, 0);
  2597     len = strlen(_fname) + 1;
  2836     len = strlen(_fname) + 1;
  2598     fname = (char *) __PHYSFS_smallAlloc(len);
  2837     fname = (char *) __PHYSFS_smallAlloc(len);
  2599     BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, -1);
  2838     BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
  2600 
  2839 
  2601     /* set some sane defaults... */
  2840     /* set some sane defaults... */
  2602     stat->filesize = -1;
  2841     stat->filesize = -1;
  2603     stat->modtime = -1;
  2842     stat->modtime = -1;
  2604     stat->createtime = -1;
  2843     stat->createtime = -1;
  2632                 else if (verifyPath(i, &arcfname, 0))
  2871                 else if (verifyPath(i, &arcfname, 0))
  2633                 {
  2872                 {
  2634                     /* !!! FIXME: this test is wrong and should be elsewhere. */
  2873                     /* !!! FIXME: this test is wrong and should be elsewhere. */
  2635                     stat->readonly = !(writeDir &&
  2874                     stat->readonly = !(writeDir &&
  2636                                  (strcmp(writeDir->dirName, i->dirName) == 0));
  2875                                  (strcmp(writeDir->dirName, i->dirName) == 0));
  2637                     retval = i->funcs->stat(i->opaque, arcfname, &exists, stat);
  2876                     retval = i->funcs->stat(i->opaque, arcfname, stat);
       
  2877                     if ((retval) || (currentErrorCode() != PHYSFS_ERR_NOT_FOUND))
       
  2878                         exists = 1;
  2638                 } /* else if */
  2879                 } /* else if */
  2639             } /* for */
  2880             } /* for */
  2640             __PHYSFS_platformReleaseMutex(stateLock);
  2881             __PHYSFS_platformReleaseMutex(stateLock);
  2641         } /* else */
  2882         } /* else */
  2642     } /* if */
  2883     } /* if */