From 9d0f8f0546692a9de161ee3e46470ccdba8f6f7f Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 23 Jul 2001 04:47:47 +0000 Subject: [PATCH] Now with all directory functions implemented (and debugged?). No file functions in place, yet. --- archivers/zip.c | 287 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 267 insertions(+), 20 deletions(-) diff --git a/archivers/zip.c b/archivers/zip.c index 2b7e3560..1ca4becd 100644 --- a/archivers/zip.c +++ b/archivers/zip.c @@ -6,10 +6,14 @@ * This file written by Ryan C. Gordon. */ +#undef __STRICT_ANSI__ + #include #include #include #include +#include +#include #include "physfs.h" #include "unzip.h" @@ -21,6 +25,7 @@ #error PHYSFS_SUPPORTS_ZIP must be defined. #endif +#define MAXZIPENTRYSIZE 256 typedef struct { @@ -30,6 +35,7 @@ typedef struct typedef struct { +int i; } ZIPfileinfo; @@ -40,38 +46,44 @@ static const FileFunctions __PHYSFS_FileFunctions_ZIP; static int ZIP_read(FileHandle *handle, void *buffer, unsigned int objSize, unsigned int objCount) { + BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, 0); /* !!! write me! */ } /* ZIP_read */ static int ZIP_eof(FileHandle *handle) { + BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, 1); /* !!! write me! */ } /* ZIP_eof */ static int ZIP_tell(FileHandle *handle) { + BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, -1); /* !!! write me! */ } /* ZIP_tell */ static int ZIP_seek(FileHandle *handle, int offset) { + BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, 0); /* !!! write me! */ } /* ZIP_seek */ static int ZIP_fileLength(FileHandle *handle) { + BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, -1); /* !!! write me! */ } /* ZIP_fileLength */ static int ZIP_fileClose(FileHandle *handle) { + BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, 0); /* !!! write me! */ } /* ZIP_fileClose */ static int ZIP_isArchive(const char *filename, int forWriting) { int retval = 0; - unzFile unz = unzOpen(name); + unzFile unz = unzOpen(filename); unz_global_info global; if (unz != NULL) @@ -118,11 +130,83 @@ static DirHandle *ZIP_openArchive(const char *name, int forWriting) ((ZIPinfo *) (retval->opaque))->handle = unz; ((ZIPinfo *) (retval->opaque))->totalEntries = global.number_entry; + retval->funcs = &__PHYSFS_DirFunctions_ZIP; return(retval); } /* ZIP_openArchive */ +/* "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 char *ZIP_realpath(unzFile fh, unz_file_info *info) +{ + char *retval = NULL; + int size; + + if (entry_is_symlink(info)) + { + 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); + } /* if */ + + return(retval); +} /* ZIP_realpath */ + + +/* !!! This is seriously ugly. */ static LinkedStringList *ZIP_enumerateFiles(DirHandle *h, const char *dirname, int omitSymLinks) @@ -130,51 +214,90 @@ static LinkedStringList *ZIP_enumerateFiles(DirHandle *h, ZIPinfo *zi = (ZIPinfo *) (h->opaque); unzFile fh = zi->handle; int i; + int dlen; LinkedStringList *retval = NULL; LinkedStringList *l = NULL; LinkedStringList *prev = NULL; - char buffer[256]; + char buf[MAXZIPENTRYSIZE]; char *d; + unz_file_info info; /* jump to first file entry... */ BAIL_IF_MACRO(unzGoToFirstFile(fh) != UNZ_OK, ERR_IO_ERROR, NULL); - - i = strlen(dirname); - d = malloc(i + 1); + dlen = strlen(dirname); + d = malloc(dlen + 1); + BAIL_IF_MACRO(d == NULL, ERR_OUT_OF_MEMORY, NULL); strcpy(d, dirname); - if ((i > 0) && (d[i - 1] == '/')) /* no trailing slash. */ - d[i - 1] = '\0'; + if ((dlen > 0) && (d[dlen - 1] == '/')) /* no trailing slash. */ + { + dlen--; + d[dlen] = '\0'; + } /* if */ - for (i = 0; i < zi->totalEntries; i++) + for (i = 0; i < zi->totalEntries; i++, unzGoToNextFile(fh)) { char *ptr; - unzGetCurrentFileInfo(fh, NULL, buf, sizeof (buf), NULL, 0, NULL, 0); - ptr = strrchr(p, '/'); /* !!! check this! */ - if (ptr == NULL) + char *add_file; + int this_dlen; + + unzGetCurrentFileInfo(fh, &info, buf, sizeof (buf), NULL, 0, NULL, 0); + + if ((omitSymLinks) && (entry_is_symlink(&info))) + continue; + + buf[sizeof (buf) - 1] = '\0'; /* safety. */ + this_dlen = strlen(buf); + if ((this_dlen > 0) && (buf[this_dlen - 1] == '/')) /* no trailing slash. */ { - if (*d != '\0') - continue; /* not for this dir; skip it. */ - ptr = buf; + this_dlen--; + buf[this_dlen] = '\0'; + } /* if */ + + if (this_dlen <= dlen) /* not in this dir. */ + continue; + + if (*d == '\0') + add_file = buf; else { - *ptr = '\0'; - ptr++; - if (strcmp(buf, d) != 0) - continue; /* not for this dir; skip it. */ + if (buf[dlen] != '/') /* can't be in same directory? */ + continue; + + buf[dlen] = '\0'; + if (strcmp(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 (strcmp(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(ptr) + 1); + l->str = (char *) malloc(strlen(add_file) + 1); if (l->str == NULL) { free(l); break; } /* if */ - strcpy(l->str, ptr); + strcpy(l->str, add_file); if (retval == NULL) retval = l; @@ -190,28 +313,152 @@ static LinkedStringList *ZIP_enumerateFiles(DirHandle *h, } /* 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); + unzFile fh = zi->handle; + int dlen; + char *d; + int i; + unz_file_info info; + + BAIL_IF_MACRO(unzGoToFirstFile(fh) != UNZ_OK, ERR_IO_ERROR, 0); + dlen = strlen(name); + d = malloc(dlen + 1); + BAIL_IF_MACRO(d == NULL, ERR_OUT_OF_MEMORY, 0); + strcpy(d, name); + if ((dlen > 0) && (d[dlen - 1] == '/')) /* no trailing slash. */ + { + dlen--; + d[dlen] = '\0'; + } /* if */ + + for (i = 0; i < zi->totalEntries; i++, unzGoToNextFile(fh)) + { + int this_dlen; + unzGetCurrentFileInfo(fh, &info, buf, sizeof (buf), NULL, 0, NULL, 0); + buf[sizeof (buf) - 1] = '\0'; /* safety. */ + this_dlen = strlen(buf); + 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 = 1; + free(d); + if (follow) /* follow symlinks? */ + { + char *real = ZIP_realpath(fh, &info); + if (real != NULL) + { + retval = ZIP_exists_symcheck(h, real, follow - 1); + free(real); + } /* if */ + } /* if */ + return(retval); + } /* if */ + } /* for */ + + free(d); + return(0); +} /* ZIP_exists_symcheck */ + + +static int ZIP_exists_nofollow(DirHandle *h, const char *name) +{ + return(ZIP_exists_symcheck(h, name, 0)); +} /* ZIP_exists_nofollow */ + + static int ZIP_exists(DirHandle *h, const char *name) { + /* follow at most 20 links to prevent recursion... */ + int retval = ZIP_exists_symcheck(h, name, 20); + unz_file_info info; + unzFile fh = ((ZIPinfo *) (h->opaque))->handle; + + if (retval) + { + /* current zip entry will be the file in question. */ + unzGetCurrentFileInfo(fh, &info, NULL, 0, NULL, 0, NULL, 0); + + /* if it's a symlink, then we ran into a possible symlink loop. */ + BAIL_IF_MACRO(entry_is_symlink(&info), ERR_TOO_MANY_SYMLINKS, 0); + } /* if */ + + return(retval); } /* ZIP_exists */ static int ZIP_isDirectory(DirHandle *h, const char *name) { + char buf[MAXZIPENTRYSIZE]; + unzFile fh = ((ZIPinfo *) (h->opaque))->handle; + int retval = ZIP_exists(h, name); + int dlen = strlen(name); + + if (retval) + { + /* current zip entry will be the file in question. */ + unzGetCurrentFileInfo(fh, NULL, buf, sizeof (buf), NULL, 0, NULL, 0); + retval = (buf[dlen] == '/'); /* !!! yikes. */ + } /* if */ + + return(retval); } /* ZIP_isDirectory */ static int ZIP_isSymLink(DirHandle *h, const char *name) { + unzFile fh = ((ZIPinfo *) (h->opaque))->handle; + int retval = ZIP_exists_nofollow(h, name); + unz_file_info info; + + if (retval) + { + /* current zip entry will be the file in question. */ + unzGetCurrentFileInfo(fh, &info, NULL, 0, NULL, 0, NULL, 0); + retval = entry_is_symlink(&info); + } /* if */ + + return(retval); } /* ZIP_isSymLink */ static FileHandle *ZIP_openRead(DirHandle *h, const char *filename) { +/* + unzFile fh = ((ZIPinfo *) (h->opaque))->handle; + unz_file_info info; +*/ + FileHandle *retval = NULL; + + BAIL_IF_MACRO(!ZIP_exists(h, filename), ERR_NO_SUCH_FILE, NULL); + +/* + finfo->startPos = ; + finfo->curPos = offset; + finfo->size = size; + retval->opaque = (void *) finfo; + retval->funcs = &__PHYSFS_FileFunctions_GRP; + retval->dirHandle = h; +*/ + + return(retval); } /* ZIP_openRead */ static void ZIP_dirClose(DirHandle *h) { + unzClose(((ZIPinfo *) (h->opaque))->handle); + free(h->opaque); + free(h); } /* ZIP_dirClose */