zip.c 18 KB

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