zip.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. /*
  2. * ZIP support routines for PhysicsFS.
  3. *
  4. * Please see the file LICENSE in the source's root directory.
  5. *
  6. * This file written by Ryan C. Gordon.
  7. */
  8. #if HAVE_CONFIG_H
  9. # include <config.h>
  10. #endif
  11. #if (defined PHYSFS_SUPPORTS_ZIP)
  12. /*
  13. * !!! FIXME: overall design bugs.
  14. *
  15. * Maybe add a seekToStartOfCurrentFile() in unzip.c if complete seek
  16. * semantics are impossible.
  17. *
  18. * Could be more i/o efficient if we combined unzip.c and this file.
  19. * (and thus lose all the unzGoToNextFile() dummy loops.
  20. */
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <assert.h>
  25. #include <time.h>
  26. #include "physfs.h"
  27. #include "unzip.h"
  28. #define __PHYSICSFS_INTERNAL__
  29. #include "physfs_internal.h"
  30. #define MAXZIPENTRYSIZE 256
  31. typedef struct
  32. {
  33. char *name;
  34. unz_file_info info;
  35. char *symlink;
  36. } ZIPentry;
  37. typedef struct
  38. {
  39. char *archiveName;
  40. unz_global_info global;
  41. ZIPentry *entries;
  42. } ZIPinfo;
  43. typedef struct
  44. {
  45. unzFile handle;
  46. } ZIPfileinfo;
  47. /* Number of symlinks to follow before we assume it's a recursive link... */
  48. #define SYMLINK_RECURSE_COUNT 20
  49. static PHYSFS_sint64 ZIP_read(FileHandle *handle, void *buffer,
  50. PHYSFS_uint32 objSize, PHYSFS_uint32 objCount);
  51. static int ZIP_eof(FileHandle *handle);
  52. static PHYSFS_sint64 ZIP_tell(FileHandle *handle);
  53. static int ZIP_seek(FileHandle *handle, PHYSFS_uint64 offset);
  54. static PHYSFS_sint64 ZIP_fileLength(FileHandle *handle);
  55. static int ZIP_fileClose(FileHandle *handle);
  56. static int ZIP_isArchive(const char *filename, int forWriting);
  57. static char *ZIP_realpath(unzFile fh, unz_file_info *info, ZIPentry *entry);
  58. static DirHandle *ZIP_openArchive(const char *name, int forWriting);
  59. static LinkedStringList *ZIP_enumerateFiles(DirHandle *h,
  60. const char *dirname,
  61. int omitSymLinks);
  62. static int ZIP_exists(DirHandle *h, const char *name);
  63. static int ZIP_isDirectory(DirHandle *h, const char *name);
  64. static int ZIP_isSymLink(DirHandle *h, const char *name);
  65. static PHYSFS_sint64 ZIP_getLastModTime(DirHandle *h, const char *name);
  66. static FileHandle *ZIP_openRead(DirHandle *h, const char *filename);
  67. static void ZIP_dirClose(DirHandle *h);
  68. static const FileFunctions __PHYSFS_FileFunctions_ZIP =
  69. {
  70. ZIP_read, /* read() method */
  71. NULL, /* write() method */
  72. ZIP_eof, /* eof() method */
  73. ZIP_tell, /* tell() method */
  74. ZIP_seek, /* seek() method */
  75. ZIP_fileLength, /* fileLength() method */
  76. ZIP_fileClose /* fileClose() method */
  77. };
  78. const DirFunctions __PHYSFS_DirFunctions_ZIP =
  79. {
  80. ZIP_isArchive, /* isArchive() method */
  81. ZIP_openArchive, /* openArchive() method */
  82. ZIP_enumerateFiles, /* enumerateFiles() method */
  83. ZIP_exists, /* exists() method */
  84. ZIP_isDirectory, /* isDirectory() method */
  85. ZIP_isSymLink, /* isSymLink() method */
  86. ZIP_getLastModTime, /* getLastModTime() method */
  87. ZIP_openRead, /* openRead() method */
  88. NULL, /* openWrite() method */
  89. NULL, /* openAppend() method */
  90. NULL, /* remove() method */
  91. NULL, /* mkdir() method */
  92. ZIP_dirClose /* dirClose() method */
  93. };
  94. const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP =
  95. {
  96. "ZIP",
  97. "PkZip/WinZip/Info-Zip compatible",
  98. "Ryan C. Gordon <icculus@clutteredmind.org>",
  99. "http://www.icculus.org/physfs/",
  100. };
  101. static PHYSFS_sint64 ZIP_read(FileHandle *handle, void *buffer,
  102. PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
  103. {
  104. unzFile fh = ((ZIPfileinfo *) (handle->opaque))->handle;
  105. int bytes = (int) (objSize * objCount); /* !!! FIXME: overflow? */
  106. PHYSFS_sint32 rc = unzReadCurrentFile(fh, buffer, bytes);
  107. if (rc < bytes)
  108. __PHYSFS_setError(ERR_PAST_EOF);
  109. else if (rc == UNZ_ERRNO)
  110. __PHYSFS_setError(ERR_IO_ERROR);
  111. else if (rc < 0)
  112. __PHYSFS_setError(ERR_COMPRESSION);
  113. return(rc / objSize);
  114. } /* ZIP_read */
  115. static int ZIP_eof(FileHandle *handle)
  116. {
  117. return(unzeof(((ZIPfileinfo *) (handle->opaque))->handle));
  118. } /* ZIP_eof */
  119. static PHYSFS_sint64 ZIP_tell(FileHandle *handle)
  120. {
  121. return(unztell(((ZIPfileinfo *) (handle->opaque))->handle));
  122. } /* ZIP_tell */
  123. static int ZIP_seek(FileHandle *handle, PHYSFS_uint64 offset)
  124. {
  125. /* !!! FIXME : this blows. */
  126. unzFile fh = ((ZIPfileinfo *) (handle->opaque))->handle;
  127. char *buf = NULL;
  128. PHYSFS_uint32 bufsize = 4096 * 2;
  129. BAIL_IF_MACRO(unztell(fh) == offset, NULL, 1);
  130. BAIL_IF_MACRO(ZIP_fileLength(handle) <= (PHYSFS_sint64) offset, ERR_PAST_EOF, 0);
  131. /* reset to the start of the zipfile. */
  132. unzCloseCurrentFile(fh);
  133. BAIL_IF_MACRO(unzOpenCurrentFile(fh) != UNZ_OK, ERR_IO_ERROR, 0);
  134. while ((buf == NULL) && (bufsize >= 512))
  135. {
  136. bufsize >>= 1; /* divides by two. */
  137. buf = (char *) malloc(bufsize);
  138. } /* while */
  139. BAIL_IF_MACRO(buf == NULL, ERR_OUT_OF_MEMORY, 0);
  140. while (offset > 0)
  141. {
  142. PHYSFS_uint32 chunk = (offset > bufsize) ? bufsize : (PHYSFS_uint32)offset;
  143. PHYSFS_sint32 rc = unzReadCurrentFile(fh, buf, chunk);
  144. BAIL_IF_MACRO(rc == 0, ERR_IO_ERROR, 0); /* shouldn't happen. */
  145. BAIL_IF_MACRO(rc == UNZ_ERRNO, ERR_IO_ERROR, 0);
  146. BAIL_IF_MACRO(rc < 0, ERR_COMPRESSION, 0);
  147. offset -= rc;
  148. } /* while */
  149. free(buf);
  150. return(offset == 0);
  151. } /* ZIP_seek */
  152. static PHYSFS_sint64 ZIP_fileLength(FileHandle *handle)
  153. {
  154. ZIPfileinfo *finfo = (ZIPfileinfo *) (handle->opaque);
  155. unz_file_info info;
  156. unzGetCurrentFileInfo(finfo->handle, &info, NULL, 0, NULL, 0, NULL, 0);
  157. return(info.uncompressed_size);
  158. } /* ZIP_fileLength */
  159. static int ZIP_fileClose(FileHandle *handle)
  160. {
  161. ZIPfileinfo *finfo = (ZIPfileinfo *) (handle->opaque);
  162. unzClose(finfo->handle);
  163. free(finfo);
  164. free(handle);
  165. return(1);
  166. } /* ZIP_fileClose */
  167. static int ZIP_isArchive(const char *filename, int forWriting)
  168. {
  169. int retval = 0;
  170. unzFile unz = unzOpen(filename);
  171. unz_global_info global;
  172. if (unz != NULL)
  173. {
  174. if (unzGetGlobalInfo(unz, &global) == UNZ_OK)
  175. retval = 1;
  176. unzClose(unz);
  177. } /* if */
  178. return(retval);
  179. } /* ZIP_isArchive */
  180. static void freeEntries(ZIPinfo *info, int count, const char *errmsg)
  181. {
  182. int i;
  183. for (i = 0; i < count; i++)
  184. {
  185. free(info->entries[i].name);
  186. if (info->entries[i].symlink != NULL)
  187. free(info->entries[i].symlink);
  188. } /* for */
  189. free(info->entries);
  190. if (errmsg != NULL)
  191. __PHYSFS_setError(errmsg);
  192. } /* freeEntries */
  193. /*
  194. * !!! FIXME: Really implement this.
  195. * !!! FIXME: symlinks in zipfiles can be relative paths, including
  196. * !!! FIXME: "." and ".." entries. These need to be parsed out.
  197. * !!! FIXME: For now, though, we're just copying the relative path. Oh well.
  198. */
  199. static char *expand_symlink_path(const char *path, ZIPentry *entry)
  200. {
  201. char *retval = (char *) malloc(strlen(path) + 1);
  202. BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
  203. strcpy(retval, path);
  204. return(retval);
  205. } /* expand_symlink_path */
  206. static char *ZIP_realpath(unzFile fh, unz_file_info *info, ZIPentry *entry)
  207. {
  208. char path[MAXZIPENTRYSIZE];
  209. int size = info->uncompressed_size;
  210. int rc;
  211. BAIL_IF_MACRO(size >= sizeof (path), ERR_IO_ERROR, NULL);
  212. BAIL_IF_MACRO(unzOpenCurrentFile(fh) != UNZ_OK, ERR_IO_ERROR, NULL);
  213. rc = unzReadCurrentFile(fh, path, size);
  214. unzCloseCurrentFile(fh);
  215. BAIL_IF_MACRO(rc != size, ERR_IO_ERROR, NULL);
  216. path[size] = '\0'; /* null terminate it. */
  217. return(expand_symlink_path(path, entry)); /* retval is malloc()'d. */
  218. } /* ZIP_realpath */
  219. static int version_does_symlinks(uLong version)
  220. {
  221. int retval = 0;
  222. PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF);
  223. /*
  224. * These are the platforms that can build an archive with symlinks,
  225. * according to the Info-ZIP project.
  226. */
  227. switch (hosttype)
  228. {
  229. case 3: /* Unix */
  230. case 16: /* BeOS */
  231. case 5: /* Atari */
  232. retval = 1;
  233. break;
  234. } /* switch */
  235. return(retval);
  236. } /* version_does_symlinks */
  237. static int entry_is_symlink(unz_file_info *info)
  238. {
  239. return (
  240. (version_does_symlinks(info->version)) &&
  241. (info->uncompressed_size > 0) &&
  242. (info->external_fa & 0x0120000) /* symlink flag. */
  243. );
  244. } /* entry_is_symlink */
  245. static int loadZipEntries(ZIPinfo *info, unzFile unz)
  246. {
  247. int i, max;
  248. BAIL_IF_MACRO(unzGetGlobalInfo(unz, &(info->global)) != UNZ_OK,
  249. ERR_IO_ERROR, 0);
  250. BAIL_IF_MACRO(unzGoToFirstFile(unz) != UNZ_OK, ERR_IO_ERROR, 0);
  251. max = info->global.number_entry;
  252. info->entries = (ZIPentry *) malloc(sizeof (ZIPentry) * max);
  253. BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0);
  254. for (i = 0; i < max; i++)
  255. {
  256. unz_file_info *d = &((info->entries[i]).info);
  257. if (unzGetCurrentFileInfo(unz, d, NULL, 0, NULL, 0, NULL, 0) != UNZ_OK)
  258. {
  259. freeEntries(info, i, ERR_IO_ERROR);
  260. return(0);
  261. } /* if */
  262. (info->entries[i]).name = (char *) malloc(d->size_filename + 1);
  263. if ((info->entries[i]).name == NULL)
  264. {
  265. freeEntries(info, i, ERR_OUT_OF_MEMORY);
  266. return(0);
  267. } /* if */
  268. info->entries[i].symlink = NULL;
  269. if (unzGetCurrentFileInfo(unz, NULL, (info->entries[i]).name,
  270. d->size_filename + 1, NULL, 0,
  271. NULL, 0) != UNZ_OK)
  272. {
  273. freeEntries(info, i + 1, ERR_IO_ERROR);
  274. return(0);
  275. } /* if */
  276. if (entry_is_symlink(d))
  277. {
  278. info->entries[i].symlink = ZIP_realpath(unz, d, &info->entries[i]);
  279. if (info->entries[i].symlink == NULL)
  280. {
  281. freeEntries(info, i + 1, NULL);
  282. return(0);
  283. } /* if */
  284. } /* if */
  285. if ((unzGoToNextFile(unz) != UNZ_OK) && (i + 1 < max))
  286. {
  287. freeEntries(info, i + 1, ERR_IO_ERROR);
  288. return(0);
  289. } /* if */
  290. } /* for */
  291. return(1);
  292. } /* loadZipEntries */
  293. static DirHandle *ZIP_openArchive(const char *name, int forWriting)
  294. {
  295. unzFile unz = NULL;
  296. DirHandle *retval = NULL;
  297. BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
  298. retval = malloc(sizeof (DirHandle));
  299. BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
  300. unz = unzOpen(name);
  301. if (unz == NULL)
  302. {
  303. free(retval);
  304. BAIL_IF_MACRO(1, ERR_UNSUPPORTED_ARCHIVE, NULL);
  305. } /* if */
  306. retval->opaque = malloc(sizeof (ZIPinfo));
  307. if (retval->opaque == NULL)
  308. {
  309. free(retval);
  310. unzClose(unz);
  311. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL);
  312. } /* if */
  313. ((ZIPinfo *) (retval->opaque))->archiveName = malloc(strlen(name) + 1);
  314. if ( (((ZIPinfo *) (retval->opaque))->archiveName == NULL) ||
  315. (!loadZipEntries( (ZIPinfo *) (retval->opaque), unz)) )
  316. {
  317. if (((ZIPinfo *) (retval->opaque))->archiveName != NULL)
  318. free(((ZIPinfo *) (retval->opaque))->archiveName);
  319. free(retval->opaque);
  320. free(retval);
  321. unzClose(unz);
  322. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL);
  323. } /* if */
  324. unzClose(unz);
  325. strcpy(((ZIPinfo *) (retval->opaque))->archiveName, name);
  326. retval->funcs = &__PHYSFS_DirFunctions_ZIP;
  327. return(retval);
  328. } /* ZIP_openArchive */
  329. /* !!! This is seriously ugly. */
  330. static LinkedStringList *ZIP_enumerateFiles(DirHandle *h,
  331. const char *dirname,
  332. int omitSymLinks)
  333. {
  334. ZIPinfo *zi = (ZIPinfo *) (h->opaque);
  335. unsigned int i;
  336. int dlen;
  337. LinkedStringList *retval = NULL;
  338. LinkedStringList *l = NULL;
  339. LinkedStringList *prev = NULL;
  340. char *d;
  341. ZIPentry *entry;
  342. char buf[MAXZIPENTRYSIZE];
  343. dlen = strlen(dirname);
  344. d = malloc(dlen + 1);
  345. BAIL_IF_MACRO(d == NULL, ERR_OUT_OF_MEMORY, NULL);
  346. strcpy(d, dirname);
  347. if ((dlen > 0) && (d[dlen - 1] == '/')) /* no trailing slash. */
  348. {
  349. dlen--;
  350. d[dlen] = '\0';
  351. } /* if */
  352. for (i = 0, entry = zi->entries; i < zi->global.number_entry; i++, entry++)
  353. {
  354. char *ptr;
  355. char *add_file;
  356. int this_dlen;
  357. if ((omitSymLinks) && (entry->symlink != NULL))
  358. continue;
  359. this_dlen = strlen(entry->name);
  360. if (this_dlen + 1 > MAXZIPENTRYSIZE)
  361. continue; /* ugh. */
  362. strcpy(buf, entry->name);
  363. if ((this_dlen > 0) && (buf[this_dlen - 1] == '/')) /* no trailing slash. */
  364. {
  365. this_dlen--;
  366. buf[this_dlen] = '\0';
  367. } /* if */
  368. if (this_dlen <= dlen) /* not in this dir. */
  369. continue;
  370. if (*d == '\0')
  371. add_file = buf;
  372. else
  373. {
  374. if (buf[dlen] != '/') /* can't be in same directory? */
  375. continue;
  376. buf[dlen] = '\0';
  377. if (__PHYSFS_platformStricmp(d, buf) != 0) /* not same directory? */
  378. continue;
  379. add_file = buf + dlen + 1;
  380. } /* else */
  381. /* handle subdirectories... */
  382. ptr = strchr(add_file, '/');
  383. if (ptr != NULL)
  384. {
  385. LinkedStringList *j;
  386. *ptr = '\0';
  387. for (j = retval; j != NULL; j = j->next)
  388. {
  389. if (__PHYSFS_platformStricmp(j->str, ptr) == 0)
  390. break;
  391. } /* for */
  392. if (j != NULL)
  393. continue;
  394. } /* if */
  395. l = (LinkedStringList *) malloc(sizeof (LinkedStringList));
  396. if (l == NULL)
  397. break;
  398. l->str = (char *) malloc(strlen(add_file) + 1);
  399. if (l->str == NULL)
  400. {
  401. free(l);
  402. break;
  403. } /* if */
  404. strcpy(l->str, add_file);
  405. if (retval == NULL)
  406. retval = l;
  407. else
  408. prev->next = l;
  409. prev = l;
  410. l->next = NULL;
  411. } /* for */
  412. free(d);
  413. return(retval);
  414. } /* ZIP_enumerateFiles */
  415. /* !!! This is seriously ugly. */
  416. static int ZIP_exists_symcheck(DirHandle *h, const char *name, int follow)
  417. {
  418. char buf[MAXZIPENTRYSIZE];
  419. ZIPinfo *zi = (ZIPinfo *) (h->opaque);
  420. int dlen;
  421. char *d;
  422. unsigned int i;
  423. ZIPentry *entry;
  424. dlen = strlen(name);
  425. d = malloc(dlen + 1);
  426. BAIL_IF_MACRO(d == NULL, ERR_OUT_OF_MEMORY, -1);
  427. strcpy(d, name);
  428. if ((dlen > 0) && (d[dlen - 1] == '/')) /* no trailing slash. */
  429. {
  430. dlen--;
  431. d[dlen] = '\0';
  432. } /* if */
  433. for (i = 0, entry = zi->entries; i < zi->global.number_entry; i++, entry++)
  434. {
  435. int this_dlen = strlen(entry->name);
  436. if (this_dlen + 1 > MAXZIPENTRYSIZE)
  437. continue; /* ugh. */
  438. strcpy(buf, entry->name);
  439. if ((this_dlen > 0) && (buf[this_dlen - 1] == '/')) /* no trailing slash. */
  440. {
  441. this_dlen--;
  442. buf[this_dlen] = '\0';
  443. } /* if */
  444. if ( ((buf[dlen] == '/') || (buf[dlen] == '\0')) &&
  445. (strncmp(d, buf, dlen) == 0) )
  446. {
  447. int retval = i;
  448. free(d);
  449. if (follow) /* follow symlinks? */
  450. {
  451. if (entry->symlink != NULL)
  452. retval = ZIP_exists_symcheck(h, entry->symlink, follow-1);
  453. } /* if */
  454. return(retval);
  455. } /* if */
  456. } /* for */
  457. free(d);
  458. return(-1);
  459. } /* ZIP_exists_symcheck */
  460. static int ZIP_exists(DirHandle *h, const char *name)
  461. {
  462. int retval = ZIP_exists_symcheck(h, name, SYMLINK_RECURSE_COUNT);
  463. int is_sym;
  464. if (retval == -1)
  465. return(0);
  466. /* if it's a symlink, then we ran into a possible symlink loop. */
  467. is_sym = ( ((ZIPinfo *)(h->opaque))->entries[retval].symlink != NULL );
  468. BAIL_IF_MACRO(is_sym, ERR_TOO_MANY_SYMLINKS, 0);
  469. return(1);
  470. } /* ZIP_exists */
  471. static PHYSFS_sint64 ZIP_getLastModTime(DirHandle *h, const char *name)
  472. {
  473. ZIPinfo *zi = (ZIPinfo *) (h->opaque);
  474. int pos = ZIP_exists_symcheck(h, name, SYMLINK_RECURSE_COUNT);
  475. ZIPentry *entry;
  476. struct tm t;
  477. if (pos == -1)
  478. return(0);
  479. entry = &zi->entries[pos];
  480. /* if it's a symlink, then we ran into a possible symlink loop. */
  481. BAIL_IF_MACRO(entry->symlink != NULL, ERR_TOO_MANY_SYMLINKS, 0);
  482. memset(&t, '\0', sizeof (t));
  483. t.tm_year = entry->info.tmu_date.tm_year - 1900;
  484. t.tm_mon = entry->info.tmu_date.tm_mon;
  485. t.tm_mday = entry->info.tmu_date.tm_mday;
  486. t.tm_hour = entry->info.tmu_date.tm_hour;
  487. t.tm_min = entry->info.tmu_date.tm_min;
  488. t.tm_sec = entry->info.tmu_date.tm_sec;
  489. return((PHYSFS_sint64) mktime(&t));
  490. } /* ZIP_getLastModTime */
  491. static int ZIP_isDirectory(DirHandle *h, const char *name)
  492. {
  493. int dlen;
  494. int is_sym;
  495. int retval = ZIP_exists_symcheck(h, name, SYMLINK_RECURSE_COUNT);
  496. if (retval == -1)
  497. return(0);
  498. /* if it's a symlink, then we ran into a possible symlink loop. */
  499. is_sym = ( ((ZIPinfo *)(h->opaque))->entries[retval].symlink != NULL );
  500. BAIL_IF_MACRO(is_sym, ERR_TOO_MANY_SYMLINKS, 0);
  501. dlen = strlen(name);
  502. /* !!! yikes. Better way to check? */
  503. retval = (((ZIPinfo *)(h->opaque))->entries[retval].name[dlen] == '/');
  504. return(retval);
  505. } /* ZIP_isDirectory */
  506. static int ZIP_isSymLink(DirHandle *h, const char *name)
  507. {
  508. int retval = ZIP_exists_symcheck(h, name, 0);
  509. if (retval == -1)
  510. return(0);
  511. retval = ( ((ZIPinfo *)(h->opaque))->entries[retval].symlink != NULL );
  512. return(retval);
  513. } /* ZIP_isSymLink */
  514. static FileHandle *ZIP_openRead(DirHandle *h, const char *filename)
  515. {
  516. FileHandle *retval = NULL;
  517. ZIPinfo *zi = ((ZIPinfo *) (h->opaque));
  518. ZIPfileinfo *finfo = NULL;
  519. int pos = ZIP_exists_symcheck(h, filename, SYMLINK_RECURSE_COUNT);
  520. unzFile f;
  521. BAIL_IF_MACRO(pos == -1, ERR_NO_SUCH_FILE, NULL);
  522. f = unzOpen(zi->archiveName);
  523. BAIL_IF_MACRO(f == NULL, ERR_IO_ERROR, NULL);
  524. if (unzGoToFirstFile(f) != UNZ_OK)
  525. {
  526. unzClose(f);
  527. BAIL_IF_MACRO(1, ERR_IO_ERROR, NULL);
  528. } /* if */
  529. for (; pos > 0; pos--)
  530. {
  531. if (unzGoToNextFile(f) != UNZ_OK)
  532. {
  533. unzClose(f);
  534. BAIL_IF_MACRO(1, ERR_IO_ERROR, NULL);
  535. } /* if */
  536. } /* for */
  537. if ( (unzOpenCurrentFile(f) != UNZ_OK) ||
  538. ( (finfo = (ZIPfileinfo *) malloc(sizeof (ZIPfileinfo))) == NULL ) )
  539. {
  540. unzClose(f);
  541. BAIL_IF_MACRO(1, ERR_IO_ERROR, NULL);
  542. } /* if */
  543. if ( (!(retval = (FileHandle *) malloc(sizeof (FileHandle)))) ||
  544. (!(retval->opaque = (ZIPfileinfo *) malloc(sizeof (ZIPfileinfo)))) )
  545. {
  546. if (retval)
  547. free(retval);
  548. unzClose(f);
  549. BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL);
  550. } /* if */
  551. finfo->handle = f;
  552. retval->opaque = (void *) finfo;
  553. retval->funcs = &__PHYSFS_FileFunctions_ZIP;
  554. retval->dirHandle = h;
  555. return(retval);
  556. } /* ZIP_openRead */
  557. static void ZIP_dirClose(DirHandle *h)
  558. {
  559. ZIPinfo *zi = (ZIPinfo *) (h->opaque);
  560. freeEntries(zi, zi->global.number_entry, NULL);
  561. free(zi->archiveName);
  562. free(zi);
  563. free(h);
  564. } /* ZIP_dirClose */
  565. #endif /* defined PHYSFS_SUPPORTS_ZIP */
  566. /* end of zip.c ... */