| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664 |
- /*
- * ZIP support routines for PhysicsFS.
- *
- * Please see the file LICENSE in the source's root directory.
- *
- * This file written by Ryan C. Gordon.
- */
- /*
- * !!! FIXME: overall design bugs.
- *
- * Make unz_file_info.version into two fields of unsigned char. That's what
- * they are in the zipfile; heavens knows why unzip.c casts it...this causes
- * a byte ordering headache for me in entry_is_symlink().
- *
- * Maybe add a seekToStartOfCurrentFile() in unzip.c if complete seek
- * semantics are impossible.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include "physfs.h"
- #include "unzip.h"
- #define __PHYSICSFS_INTERNAL__
- #include "physfs_internal.h"
- #if (!defined PHYSFS_SUPPORTS_ZIP)
- #error PHYSFS_SUPPORTS_ZIP must be defined.
- #endif
- #define MAXZIPENTRYSIZE 256
- typedef struct
- {
- char *name;
- unz_file_info info;
- char *symlink;
- } ZIPentry;
- typedef struct
- {
- char *archiveName;
- unz_global_info global;
- ZIPentry *entries;
- } ZIPinfo;
- typedef struct
- {
- unzFile handle;
- } ZIPfileinfo;
- extern const DirFunctions __PHYSFS_DirFunctions_ZIP;
- static const FileFunctions __PHYSFS_FileFunctions_ZIP;
- /* Number of symlinks to follow before we assume it's a recursive link... */
- #define SYMLINK_RECURSE_COUNT 20
- static int ZIP_read(FileHandle *handle, void *buffer,
- unsigned int objSize, unsigned int objCount)
- {
- unzFile fh = ((ZIPfileinfo *) (handle->opaque))->handle;
- int bytes = objSize * objCount;
- int rc = unzReadCurrentFile(fh, buffer, bytes);
- if (rc < bytes)
- __PHYSFS_setError(ERR_PAST_EOF);
- else if (rc == UNZ_ERRNO)
- __PHYSFS_setError(ERR_IO_ERROR);
- else if (rc < 0)
- __PHYSFS_setError(ERR_COMPRESSION);
- return(rc / objSize);
- } /* ZIP_read */
- static int ZIP_eof(FileHandle *handle)
- {
- return(unzeof(((ZIPfileinfo *) (handle->opaque))->handle));
- } /* ZIP_eof */
- static int ZIP_tell(FileHandle *handle)
- {
- return(unztell(((ZIPfileinfo *) (handle->opaque))->handle));
- } /* ZIP_tell */
- static int ZIP_fileLength(FileHandle *handle);
- static int ZIP_seek(FileHandle *handle, int offset)
- {
- /* this blows. */
- unzFile fh = ((ZIPfileinfo *) (handle->opaque))->handle;
- char *buf = NULL;
- int bufsize = 4096 * 2;
- BAIL_IF_MACRO(unztell(fh) == offset, NULL, 1);
- BAIL_IF_MACRO(ZIP_fileLength(handle) <= offset, ERR_PAST_EOF, 0);
- /* reset to the start of the zipfile. */
- unzCloseCurrentFile(fh);
- BAIL_IF_MACRO(unzOpenCurrentFile(fh) != UNZ_OK, ERR_IO_ERROR, 0);
- while ((buf == NULL) && (bufsize >= 512))
- {
- bufsize >>= 1; /* divides by two. */
- buf = (char *) malloc(bufsize);
- } /* while */
- BAIL_IF_MACRO(buf == NULL, ERR_OUT_OF_MEMORY, 0);
- while (offset > 0)
- {
- int chunk = (offset > bufsize) ? bufsize : offset;
- int rc = unzReadCurrentFile(fh, buf, chunk);
- BAIL_IF_MACRO(rc == 0, ERR_IO_ERROR, 0); /* shouldn't happen. */
- BAIL_IF_MACRO(rc == UNZ_ERRNO, ERR_IO_ERROR, 0);
- BAIL_IF_MACRO(rc < 0, ERR_COMPRESSION, 0);
- offset -= rc;
- } /* while */
- free(buf);
- return(offset == 0);
- } /* ZIP_seek */
- static int ZIP_fileLength(FileHandle *handle)
- {
- ZIPfileinfo *finfo = (ZIPfileinfo *) (handle->opaque);
- unz_file_info info;
- unzGetCurrentFileInfo(finfo->handle, &info, NULL, 0, NULL, 0, NULL, 0);
- return(info.uncompressed_size);
- } /* ZIP_fileLength */
- static int ZIP_fileClose(FileHandle *handle)
- {
- ZIPfileinfo *finfo = (ZIPfileinfo *) (handle->opaque);
- unzClose(finfo->handle);
- free(finfo);
- free(handle);
- return(1);
- } /* ZIP_fileClose */
- static int ZIP_isArchive(const char *filename, int forWriting)
- {
- int retval = 0;
- unzFile unz = unzOpen(filename);
- unz_global_info global;
- if (unz != NULL)
- {
- if (unzGetGlobalInfo(unz, &global) == UNZ_OK)
- retval = 1;
- unzClose(unz);
- } /* if */
- return(retval);
- } /* ZIP_isArchive */
- static void freeEntries(ZIPinfo *info, int count, const char *errmsg)
- {
- int i;
- for (i = 0; i < count; i++)
- {
- free(info->entries[i].name);
- if (info->entries[i].symlink != NULL)
- free(info->entries[i].symlink);
- } /* for */
- free(info->entries);
- if (errmsg != NULL)
- __PHYSFS_setError(errmsg);
- } /* freeEntries */
- static char *ZIP_realpath(unzFile fh, unz_file_info *info)
- {
- char *retval = NULL;
- int size;
- BAIL_IF_MACRO(unzOpenCurrentFile(fh) != UNZ_OK, ERR_IO_ERROR, NULL);
- size = info->uncompressed_size;
- retval = (char *) malloc(size + 1);
- BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
- if (unzReadCurrentFile(fh, retval, size) != size)
- {
- free(retval);
- __PHYSFS_setError(ERR_IO_ERROR);
- retval = NULL;
- } /* if */
- retval[size] = '\0';
- unzCloseCurrentFile(fh);
- return(retval);
- } /* ZIP_realpath */
- /* "uLong" is defined by zlib and/or unzip.h ... */
- typedef union
- {
- unsigned char uchar4[4];
- uLong ul;
- } uchar4_uLong;
- static int version_does_symlinks(uLong version)
- {
- int retval = 0;
- unsigned char hosttype;
- uchar4_uLong converter;
- converter.ul = version;
- hosttype = converter.uchar4[1]; /* !!! BYTE ORDERING ALERT! */
- /*
- * These are the platforms that can build an archive with symlinks,
- * according to the Info-ZIP project.
- */
- switch (hosttype)
- {
- case 3: /* Unix */
- case 16: /* BeOS */
- case 5: /* Atari */
- retval = 1;
- break;
- } /* switch */
- return(retval);
- } /* version_does_symlinks */
- static int entry_is_symlink(unz_file_info *info)
- {
- return (
- (version_does_symlinks(info->version)) &&
- (info->uncompressed_size > 0) &&
- (info->external_fa & 0x0120000) /* symlink flag. */
- );
- } /* entry_is_symlink */
- static int loadZipEntries(ZIPinfo *info, unzFile unz)
- {
- int i, max;
- BAIL_IF_MACRO(unzGetGlobalInfo(unz, &(info->global)) != UNZ_OK,
- ERR_IO_ERROR, 0);
- BAIL_IF_MACRO(unzGoToFirstFile(unz) != UNZ_OK, ERR_IO_ERROR, 0);
- max = info->global.number_entry;
- info->entries = (ZIPentry *) malloc(sizeof (ZIPentry) * max);
- BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0);
- for (i = 0; i < max; i++)
- {
- unz_file_info *d = &((info->entries[i]).info);
- if (unzGetCurrentFileInfo(unz, d, NULL, 0, NULL, 0, NULL, 0) != UNZ_OK)
- {
- freeEntries(info, i, ERR_IO_ERROR);
- return(0);
- } /* if */
- (info->entries[i]).name = (char *) malloc(d->size_filename + 1);
- if ((info->entries[i]).name == NULL)
- {
- freeEntries(info, i, ERR_OUT_OF_MEMORY);
- return(0);
- } /* if */
- info->entries[i].symlink = NULL;
- if (unzGetCurrentFileInfo(unz, NULL, (info->entries[i]).name,
- d->size_filename + 1, NULL, 0,
- NULL, 0) != UNZ_OK)
- {
- freeEntries(info, i + 1, ERR_IO_ERROR);
- return(0);
- } /* if */
- if (entry_is_symlink(d))
- {
- info->entries[i].symlink = ZIP_realpath(unz, d);
- if (info->entries[i].symlink == NULL)
- {
- freeEntries(info, i + 1, NULL);
- return(0);
- } /* if */
- } /* if */
- if ((unzGoToNextFile(unz) != UNZ_OK) && (i + 1 < max))
- {
- freeEntries(info, i + 1, ERR_IO_ERROR);
- return(0);
- } /* if */
- } /* for */
- return(1);
- } /* loadZipEntries */
- static DirHandle *ZIP_openArchive(const char *name, int forWriting)
- {
- unzFile unz = NULL;
- DirHandle *retval = NULL;
- BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
- retval = malloc(sizeof (DirHandle));
- BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
- unz = unzOpen(name);
- if (unz == NULL)
- {
- free(retval);
- BAIL_IF_MACRO(1, ERR_UNSUPPORTED_ARCHIVE, NULL);
- } /* if */
- retval->opaque = malloc(sizeof (ZIPinfo));
- if (retval->opaque == NULL)
- {
- free(retval);
- unzClose(unz);
- BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL);
- } /* if */
- ((ZIPinfo *) (retval->opaque))->archiveName = malloc(strlen(name) + 1);
- if ( (((ZIPinfo *) (retval->opaque))->archiveName == NULL) ||
- (!loadZipEntries( (ZIPinfo *) (retval->opaque), unz)) )
- {
- if (((ZIPinfo *) (retval->opaque))->archiveName != NULL)
- free(((ZIPinfo *) (retval->opaque))->archiveName);
- free(retval->opaque);
- free(retval);
- unzClose(unz);
- BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL);
- } /* if */
- unzClose(unz);
- strcpy(((ZIPinfo *) (retval->opaque))->archiveName, name);
- retval->funcs = &__PHYSFS_DirFunctions_ZIP;
- return(retval);
- } /* ZIP_openArchive */
- /* !!! This is seriously ugly. */
- static LinkedStringList *ZIP_enumerateFiles(DirHandle *h,
- const char *dirname,
- int omitSymLinks)
- {
- ZIPinfo *zi = (ZIPinfo *) (h->opaque);
- unsigned int i;
- int dlen;
- LinkedStringList *retval = NULL;
- LinkedStringList *l = NULL;
- LinkedStringList *prev = NULL;
- char *d;
- ZIPentry *entry;
- char buf[MAXZIPENTRYSIZE];
- dlen = strlen(dirname);
- d = malloc(dlen + 1);
- BAIL_IF_MACRO(d == NULL, ERR_OUT_OF_MEMORY, NULL);
- strcpy(d, dirname);
- if ((dlen > 0) && (d[dlen - 1] == '/')) /* no trailing slash. */
- {
- dlen--;
- d[dlen] = '\0';
- } /* if */
- for (i = 0, entry = zi->entries; i < zi->global.number_entry; i++, entry++)
- {
- char *ptr;
- char *add_file;
- int this_dlen;
- if ((omitSymLinks) && (entry->symlink != NULL))
- continue;
- this_dlen = strlen(entry->name);
- if (this_dlen + 1 > MAXZIPENTRYSIZE)
- continue; /* ugh. */
- strcpy(buf, entry->name);
- if ((this_dlen > 0) && (buf[this_dlen - 1] == '/')) /* no trailing slash. */
- {
- this_dlen--;
- buf[this_dlen] = '\0';
- } /* if */
- if (this_dlen <= dlen) /* not in this dir. */
- continue;
- if (*d == '\0')
- add_file = buf;
- else
- {
- if (buf[dlen] != '/') /* can't be in same directory? */
- continue;
- buf[dlen] = '\0';
- if (__PHYSFS_platformStricmp(d, buf) != 0) /* not same directory? */
- continue;
- add_file = buf + dlen + 1;
- } /* else */
- /* handle subdirectories... */
- ptr = strchr(add_file, '/');
- if (ptr != NULL)
- {
- LinkedStringList *j;
- *ptr = '\0';
- for (j = retval; j != NULL; j = j->next)
- {
- if (__PHYSFS_platformStricmp(j->str, ptr) == 0)
- break;
- } /* for */
- if (j != NULL)
- continue;
- } /* if */
- l = (LinkedStringList *) malloc(sizeof (LinkedStringList));
- if (l == NULL)
- break;
- l->str = (char *) malloc(strlen(add_file) + 1);
- if (l->str == NULL)
- {
- free(l);
- break;
- } /* if */
- strcpy(l->str, add_file);
- if (retval == NULL)
- retval = l;
- else
- prev->next = l;
- prev = l;
- l->next = NULL;
- } /* for */
- free(d);
- return(retval);
- } /* ZIP_enumerateFiles */
- /* !!! This is seriously ugly. */
- static int ZIP_exists_symcheck(DirHandle *h, const char *name, int follow)
- {
- char buf[MAXZIPENTRYSIZE];
- ZIPinfo *zi = (ZIPinfo *) (h->opaque);
- int dlen;
- char *d;
- unsigned int i;
- ZIPentry *entry;
- dlen = strlen(name);
- d = malloc(dlen + 1);
- BAIL_IF_MACRO(d == NULL, ERR_OUT_OF_MEMORY, -1);
- strcpy(d, name);
- if ((dlen > 0) && (d[dlen - 1] == '/')) /* no trailing slash. */
- {
- dlen--;
- d[dlen] = '\0';
- } /* if */
- for (i = 0, entry = zi->entries; i < zi->global.number_entry; i++, entry++)
- {
- int this_dlen = strlen(entry->name);
- if (this_dlen + 1 > MAXZIPENTRYSIZE)
- continue; /* ugh. */
- strcpy(buf, entry->name);
- if ((this_dlen > 0) && (buf[this_dlen - 1] == '/')) /* no trailing slash. */
- {
- this_dlen--;
- buf[this_dlen] = '\0';
- } /* if */
- if ( ((buf[dlen] == '/') || (buf[dlen] == '\0')) &&
- (strncmp(d, buf, dlen) == 0) )
- {
- int retval = i;
- free(d);
- if (follow) /* follow symlinks? */
- {
- if (entry->symlink != NULL)
- retval = ZIP_exists_symcheck(h, entry->symlink, follow-1);
- } /* if */
- return(retval);
- } /* if */
- } /* for */
- free(d);
- return(-1);
- } /* ZIP_exists_symcheck */
- static int ZIP_exists(DirHandle *h, const char *name)
- {
- int retval = ZIP_exists_symcheck(h, name, SYMLINK_RECURSE_COUNT);
- int is_sym;
- if (retval == -1)
- return(0);
- /* if it's a symlink, then we ran into a possible symlink loop. */
- is_sym = ( ((ZIPinfo *)(h->opaque))->entries[retval].symlink != NULL );
- BAIL_IF_MACRO(is_sym, ERR_TOO_MANY_SYMLINKS, 0);
- return(1);
- } /* ZIP_exists */
- static int ZIP_isDirectory(DirHandle *h, const char *name)
- {
- int dlen;
- int is_sym;
- int retval = ZIP_exists_symcheck(h, name, SYMLINK_RECURSE_COUNT);
- if (retval == -1)
- return(0);
- /* if it's a symlink, then we ran into a possible symlink loop. */
- is_sym = ( ((ZIPinfo *)(h->opaque))->entries[retval].symlink != NULL );
- BAIL_IF_MACRO(is_sym, ERR_TOO_MANY_SYMLINKS, 0);
- dlen = strlen(name);
- /* !!! yikes. Better way to check? */
- retval = (((ZIPinfo *)(h->opaque))->entries[retval].name[dlen] == '/');
- return(retval);
- } /* ZIP_isDirectory */
- static int ZIP_isSymLink(DirHandle *h, const char *name)
- {
- int retval = ZIP_exists_symcheck(h, name, 0);
- if (retval == -1)
- return(0);
- retval = ( ((ZIPinfo *)(h->opaque))->entries[retval].symlink != NULL );
- return(retval);
- } /* ZIP_isSymLink */
- static FileHandle *ZIP_openRead(DirHandle *h, const char *filename)
- {
- FileHandle *retval = NULL;
- ZIPinfo *zi = ((ZIPinfo *) (h->opaque));
- ZIPfileinfo *finfo = NULL;
- int pos = ZIP_exists_symcheck(h, filename, SYMLINK_RECURSE_COUNT);
- unzFile f;
- BAIL_IF_MACRO(pos == -1, ERR_NO_SUCH_FILE, NULL);
- f = unzOpen(zi->archiveName);
- BAIL_IF_MACRO(f == NULL, ERR_IO_ERROR, NULL);
- if (unzGoToFirstFile(f) != UNZ_OK)
- {
- unzClose(f);
- BAIL_IF_MACRO(1, ERR_IO_ERROR, NULL);
- } /* if */
- for (; pos > 0; pos--)
- {
- if (unzGoToNextFile(f) != UNZ_OK)
- {
- unzClose(f);
- BAIL_IF_MACRO(1, ERR_IO_ERROR, NULL);
- } /* if */
- } /* for */
- if ( (unzOpenCurrentFile(f) != UNZ_OK) ||
- ( (finfo = (ZIPfileinfo *) malloc(sizeof (ZIPfileinfo))) == NULL ) )
- {
- unzClose(f);
- BAIL_IF_MACRO(1, ERR_IO_ERROR, NULL);
- } /* if */
- if ( (!(retval = (FileHandle *) malloc(sizeof (FileHandle)))) ||
- (!(retval->opaque = (ZIPfileinfo *) malloc(sizeof (ZIPfileinfo)))) )
- {
- if (retval)
- free(retval);
- unzClose(f);
- BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL);
- } /* if */
- finfo->handle = f;
- retval->opaque = (void *) finfo;
- retval->funcs = &__PHYSFS_FileFunctions_ZIP;
- retval->dirHandle = h;
- return(retval);
- } /* ZIP_openRead */
- static void ZIP_dirClose(DirHandle *h)
- {
- ZIPinfo *zi = (ZIPinfo *) (h->opaque);
- freeEntries(zi, zi->global.number_entry, NULL);
- free(zi->archiveName);
- free(zi);
- free(h);
- } /* ZIP_dirClose */
- static const FileFunctions __PHYSFS_FileFunctions_ZIP =
- {
- ZIP_read, /* read() method */
- NULL, /* write() method */
- ZIP_eof, /* eof() method */
- ZIP_tell, /* tell() method */
- ZIP_seek, /* seek() method */
- ZIP_fileLength, /* fileLength() method */
- ZIP_fileClose /* fileClose() method */
- };
- const DirFunctions __PHYSFS_DirFunctions_ZIP =
- {
- ZIP_isArchive, /* isArchive() method */
- ZIP_openArchive, /* openArchive() method */
- ZIP_enumerateFiles, /* enumerateFiles() method */
- ZIP_exists, /* exists() method */
- ZIP_isDirectory, /* isDirectory() method */
- ZIP_isSymLink, /* isSymLink() method */
- ZIP_openRead, /* openRead() method */
- NULL, /* openWrite() method */
- NULL, /* openAppend() method */
- NULL, /* remove() method */
- NULL, /* mkdir() method */
- ZIP_dirClose /* dirClose() method */
- };
- const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP =
- {
- "ZIP",
- "PkZip/WinZip/Info-Zip compatible",
- "Ryan C. Gordon (icculus@clutteredmind.org)",
- "http://www.icculus.org/physfs/",
- };
- /* end of zip.c ... */
|