فهرست منبع

Now with all directory functions implemented (and debugged?). No file
functions in place, yet.

Ryan C. Gordon 25 سال پیش
والد
کامیت
9d0f8f0546
1فایلهای تغییر یافته به همراه267 افزوده شده و 20 حذف شده
  1. 267 20
      archivers/zip.c

+ 267 - 20
archivers/zip.c

@@ -6,10 +6,14 @@
  *  This file written by Ryan C. Gordon.
  */
 
+#undef __STRICT_ANSI__
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
 #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 */