]> granicus.if.org Git - php/commitdiff
initial work on more efficient zip implementation, read support working, write not...
authorGreg Beaver <cellog@php.net>
Wed, 23 Jan 2008 23:19:03 +0000 (23:19 +0000)
committerGreg Beaver <cellog@php.net>
Wed, 23 Jan 2008 23:19:03 +0000 (23:19 +0000)
This implementation will support read/write of extra field headers, both zlib/bzip2 compression read/write
it will also delay header comparison/crc32 check until file open, making opening a single file
within the zip much more efficient for large zip files
it also uses emalloc/php streams and is therefore less likely to leak.
this code is not yet built in config.m4/config.w32

ext/phar/pharzip.c [new file with mode: 0644]
ext/phar/pharzip.h [new file with mode: 0644]

diff --git a/ext/phar/pharzip.c b/ext/phar/pharzip.c
new file mode 100644 (file)
index 0000000..ed21c1d
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+  +----------------------------------------------------------------------+
+  | phar php single-file executable PHP extension, zip implementation    |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2005-2008 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt.                                 |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Authors: Gregory Beaver <cellog@php.net>                             |
+  |          Marcus Boerger <helly@php.net>                              |
+  +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "phar_internal.h"
+
+#ifdef PHP_WIN32
+static inline void phar_unixify_path_separators(char *path, int path_len) /* {{{ */
+{
+       char *s;
+
+       /* unixify win paths */
+       for (s = path; s - path < path_len; s++) {
+               if (*s == '\\') {
+                       *s = '/';
+               }
+       }
+}
+/* }}} */
+#endif
+
+static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_uint16 len TSRMLS_DC)
+{
+       php_uint16 left = len;
+       union {
+               phar_zip_extra_field_header header;
+               phar_zip_unix3 unix3;
+       } h;
+
+       do {
+               if (sizeof(h.header) != php_stream_read(fp, (char *) &h.header, sizeof(h.header))) {
+                       return FAILURE;
+               }
+               /* clean up header for big-endian systems */
+               if (h.header.tag != "\x75\x6e") {
+                       /* skip to next header */
+                       php_stream_seek(fp, h.header.size, SEEK_CUR);
+                       len -= h.header.size;
+                       continue;
+               }
+               /* unix3 header found */
+               /* clean up header for big-endian systems */
+               if (sizeof(h.unix3) != php_stream_read(fp, (char *) &h.unix3, sizeof(h.unix3))) {
+                       if (h.unix3.size > sizeof(h.unix3) - 4) {
+                               /* skip symlink filename - we may add this support in later */
+                               php_stream_seek(fp, h.unix3.size - sizeof(h.unix3.size), SEEK_CUR);
+                       }
+                       /* set permissions */
+                       entry->flags &= PHAR_ENT_COMPRESSION_MASK;
+                       if (entry->is_dir) {
+                               entry->flags |= h.unix3.perms & PHAR_ENT_PERM_DEF_DIR;
+                       } else {
+                               entry->flags |= h.unix3.perms & PHAR_ENT_PERM_DEF_FILE;
+                       }
+               }
+       } while (len);
+       return SUCCESS;
+}
+
+/*
+  extracted from libzip
+  zip_dirent.c -- read directory entry (local or central), clean dirent
+  Copyright (C) 1999, 2003, 2004, 2005 Dieter Baron and Thomas Klausner
+
+  This function is part of libzip, a library to manipulate ZIP archives.
+  The authors can be contacted at <nih@giga.or.at>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+static time_t phar_zip_d2u_time(int dtime, int ddate)
+{
+    struct tm *tm, tmbuf;
+    time_t now;
+
+    now = time(NULL);
+    tm = php_localtime_r(&now, &tmbuf);
+    
+    tm->tm_year = ((ddate>>9)&127) + 1980 - 1900;
+    tm->tm_mon = ((ddate>>5)&15) - 1;
+    tm->tm_mday = ddate&31;
+
+    tm->tm_hour = (dtime>>11)&31;
+    tm->tm_min = (dtime>>5)&63;
+    tm->tm_sec = (dtime<<1)&62;
+
+    return mktime(tm);
+}
+
+int phar_zip_parse(char *fname, int fname_len, char **error TSRMLS_DC)
+{
+       php_stream *fp;
+       char buf[8192], *metadata;
+       phar_zip_dir_end locator;
+       long size;
+       size_t read;
+       php_uint16 i;
+       phar_archive_data *mydata = NULL;
+       phar_entry_info entry = {0};
+
+       fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
+
+       php_stream_seek(fp, 0, SEEK_END);
+       size = php_stream_tell(fp);
+       if (size > sizeof(locator) + 65536) {
+               /* seek to max comment length + end of central directory record */
+               size = sizeof(locator) + 65536;
+               if (FAILURE == php_stream_seek(fp, -size, SEEK_END)) {
+                       php_stream_close(fp);
+                       return FAILURE;
+               }
+       } else {
+               php_stream_seek(fp, 0, SEEK_SET);
+       }
+       do {
+               char *p = buf;
+               if (!(read = php_stream_read(fp, buf, 8192))) {
+                       php_stream_close(fp);
+                       return FAILURE;
+               }
+               while ((p=(char *) memchr(p + 1, 'P', (size_t)(buf - (p+1) + 8192 - 4 + 1))) != NULL) {
+                       if (!memcmp(p + 1, "K\5\6", 3)) {
+                               if (p - buf < sizeof(locator)) {
+                                       /* didn't read in the whole thing, back up */
+                                       php_stream_seek(fp, 8192 - (p - buf), SEEK_CUR);
+                                       if (sizeof(locator) != php_stream_read(fp, (char *) &locator, sizeof(locator))) {
+                                               php_stream_close(fp);
+                                               return FAILURE;
+                                       }
+                               } else {
+                                       memcpy((void *)&locator, (void *) p, sizeof(locator));
+                               }
+                               goto foundit;
+                       }
+               }
+       } while (read == 8192);
+       php_stream_close(fp);
+       return FAILURE;
+foundit:
+       if (locator.centraldisk != 0 || locator.disknumber != 0) {
+               /* split archives not handled */
+               php_stream_close(fp);
+               return FAILURE;
+       }
+       mydata = ecalloc(sizeof(phar_archive_data), 1);
+       mydata->fname = estrndup(fname, fname_len);
+#ifdef PHP_WIN32
+       phar_unixify_path_separators(mydata->fname, fname_len);
+#endif
+       mydata->is_zip = 1;
+       mydata->fname_len = fname_len;
+       /* clean up on big-endian systems */
+       /* read in archive comment, if any */
+       if (locator.comment_len) {
+               metadata = (char *) emalloc(locator.comment_len);
+               if (locator.comment_len != php_stream_read(fp, metadata, locator.comment_len)) {
+                       php_stream_close(fp);
+                       efree(mydata->fname);
+                       efree(mydata);
+                       return FAILURE;
+               }
+               if (phar_parse_metadata(&metadata, &mydata->metadata, locator.comment_len TSRMLS_CC) == FAILURE) {
+                       /* if not valid serialized data, it is a regular string */
+                       ALLOC_INIT_ZVAL(mydata->metadata);
+                       Z_STRVAL_P(mydata->metadata) = metadata;
+                       Z_STRLEN_P(mydata->metadata) = locator.comment_len;
+                       Z_TYPE_P(mydata->metadata) = IS_STRING;
+               }
+       } else {
+               mydata->metadata = NULL;
+       }
+       /* seek to central directory */
+       php_stream_seek(fp, locator.cdir_offset, SEEK_SET);
+       /* read in central directory */
+       zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
+               zend_get_hash_value, destroy_phar_manifest_entry, 0);
+       entry.phar = mydata;
+       entry.is_zip = 1;
+#define PHAR_ZIP_FAIL \
+                       zend_hash_destroy(&mydata->manifest); \
+                       php_stream_close(fp); \
+                       efree(mydata->fname); \
+                       if (mydata->metadata) { \
+                               zval_dtor(mydata->metadata); \
+                       } \
+                       efree(mydata); \
+                       return FAILURE
+
+       /* add each central directory item to the manifest */
+       for (i = 0; i < locator.cdir_size; i++) {
+               phar_zip_central_dir_file zipentry;
+
+               /* sizeof(zipentry) reports size of contents + 2, no idea why */
+               if (sizeof(zipentry)-2 != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry)-2)) {
+                       PHAR_ZIP_FAIL;
+               }
+               /* clean up for bigendian systems */
+               if (memcmp("PK\1\2", zipentry.signature, 4)) {
+                       /* corrupted entry */
+                       PHAR_ZIP_FAIL;
+               }
+               entry.compressed_filesize = zipentry.compsize;
+               entry.uncompressed_filesize = zipentry.uncompsize;
+               entry.crc32 = zipentry.crc32;
+               entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp);
+               entry.flags = PHAR_ENT_PERM_DEF_FILE;
+               if (zipentry.flags & PHAR_ZIP_FLAG_ENCRYPTED) {
+                       PHAR_ZIP_FAIL;
+               }
+               if (!zipentry.filename_len) {
+                       PHAR_ZIP_FAIL;
+               }
+               entry.filename_len = zipentry.filename_len;
+               entry.filename = (char *) emalloc(zipentry.filename_len+1);
+               if (entry.filename_len != php_stream_read(fp, entry.filename, entry.filename_len)) {
+                       efree(entry.filename);
+                       PHAR_ZIP_FAIL;
+               }
+               entry.filename[entry.filename_len] = '\0';
+               if (entry.filename[entry.filename_len - 1] == '/') {
+                       entry.is_dir = 1;
+                       entry.filename_len--;
+                       entry.flags |= PHAR_ENT_PERM_DEF_DIR;
+               } else {
+                       entry.is_dir = 0;
+               }
+               if (zipentry.extra_len) {
+                       if (FAILURE == phar_zip_process_extra(fp, &entry, zipentry.extra_len TSRMLS_CC)) {
+                               PHAR_ZIP_FAIL;
+                       }
+               }
+               switch (zipentry.compressed) {
+                       case PHAR_ZIP_COMP_NONE :
+                               /* compression flag already set */
+                               break;
+                       case PHAR_ZIP_COMP_DEFLATE :
+                               entry.flags |= PHAR_ENT_COMPRESSED_GZ;
+                               if (!phar_has_zlib) {
+                                       if (error) {
+                                               spprintf(error, 0, "zlib extension is required for zlib compressed zip-based .phar file \"%s\"", fname);
+                                       }
+                                       PHAR_ZIP_FAIL;
+                               }
+                               break;
+                       case PHAR_ZIP_COMP_BZIP2 :
+                               entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
+                               if (!phar_has_bz2) {
+                                       if (error) {
+                                               spprintf(error, 0, "bz2 extension is required for Bzip2 compressed zip-based .phar file \"%s\"", fname);
+                                       }
+                                       PHAR_ZIP_FAIL;
+                               }
+                               break;
+                       default :
+                               PHAR_ZIP_FAIL;
+               }
+               /* get file metadata */
+               if (zipentry.comment_len) {
+                       metadata = (char *) emalloc(zipentry.comment_len);
+                       if (zipentry.comment_len != php_stream_read(fp, metadata, zipentry.comment_len)) {
+                               PHAR_ZIP_FAIL;
+                       }
+                       if (phar_parse_metadata(&metadata, &(entry.metadata), zipentry.comment_len TSRMLS_CC) == FAILURE) {
+                               /* if not valid serialized data, it is a regular string */
+                               ALLOC_INIT_ZVAL(entry.metadata);
+                               Z_STRVAL_P(entry.metadata) = metadata;
+                               Z_STRLEN_P(entry.metadata) = zipentry.comment_len;
+                               Z_TYPE_P(entry.metadata) = IS_STRING;
+                       } else {
+                               efree(metadata);
+                       }
+               } else {
+                       entry.metadata = NULL;
+               }
+               if (entry.filename_len == sizeof(".phar/alias.txt")-1 && strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
+                       size_t remember = php_stream_tell(fp);
+                       php_stream_filter *filter;
+                       /* to properly decompress, we have to tell zlib to look for a zlib or gzip header */
+                       zval filterparams;
+
+                       /* archive alias found, seek to file contents, do not validate local header.  Potentially risky, but
+                          not very. */
+                       php_stream_seek(fp, zipentry.offset + sizeof(phar_zip_file_header) + entry.filename_len + zipentry.extra_len, SEEK_SET);
+                       if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
+/* this is defined in zlib's zconf.h */
+#ifndef MAX_WBITS
+#define MAX_WBITS 15
+#endif
+                               array_init(&filterparams);
+                               add_assoc_long(&filterparams, "window", MAX_WBITS + 32);
+                               filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
+                               if (!filter) {
+                                       add_assoc_long(&filterparams, "window", MAX_WBITS);
+                                       filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
+                                       if (!filter) {
+                                               PHAR_ZIP_FAIL;
+                                       }
+                               }
+                               zval_dtor(&filterparams);
+                               php_stream_filter_append(&fp->readfilters, filter);
+                               if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) {
+                                       PHAR_ZIP_FAIL;
+                               }
+                               php_stream_filter_flush(filter, 1);
+                               php_stream_filter_remove(filter, 1 TSRMLS_CC);
+                       } else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
+                               php_stream_filter *filter;
+                               filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
+                               if (!filter) {
+                                       PHAR_ZIP_FAIL;
+                               }
+                               php_stream_filter_append(&fp->readfilters, filter);
+                               php_stream_filter_append(&fp->readfilters, filter);
+                               if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) {
+                                       PHAR_ZIP_FAIL;
+                               }
+                               php_stream_filter_flush(filter, 1);
+                               php_stream_filter_remove(filter, 1 TSRMLS_CC);
+                       }
+
+                       mydata->is_explicit_alias = 1;
+                       mydata->alias_len = zipentry.uncompsize;
+                       zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), mydata->alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
+               }
+               zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry,sizeof(phar_entry_info), NULL);
+       }
+       zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
+       if (pphar) {
+               *pphar = mydata;
+       }
+       return SUCCESS;
+}
diff --git a/ext/phar/pharzip.h b/ext/phar/pharzip.h
new file mode 100644 (file)
index 0000000..066c167
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+  +----------------------------------------------------------------------+
+  | phar php single-file executable PHP extension                        |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2006-2008 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt.                                 |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Authors: Gregory Beaver <cellog@php.net>                             |
+  |          Marcus Boerger <helly@php.net>                              |
+  +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+typedef struct _phar_zip_file_header {
+       char magic[4];           /* local file header signature     4 bytes  (0x04034b50) */
+       char zipversion[2];      /* version needed to extract       2 bytes */
+       php_uint16 flags;        /* general purpose bit flag        2 bytes */
+       php_uint16 compressed;   /* compression method              2 bytes */
+       php_uint16 timestamp;    /* last mod file time              2 bytes */
+       php_uint16 datestamp;    /* last mod file date              2 bytes */
+       php_uint32 crc32;        /* crc-32                          4 bytes */
+       php_uint32 compsize;     /* compressed size                 4 bytes */
+       php_uint32 uncompsize;   /* uncompressed size               4 bytes */
+       php_uint16 filename_len; /* file name length                2 bytes */
+       php_uint16 extra_len;    /* extra field length              2 bytes */
+/* file name (variable size) */
+/* extra field (variable size) */
+} phar_zip_file_header;
+
+typedef struct _phar_zip_file_datadesc {
+       php_uint32 crc32;      /* crc-32                          4 bytes */
+       php_uint32 compsize;   /* compressed size                 4 bytes */
+       php_uint32 uncompsize; /* uncompressed size               4 bytes */
+} phar_zip_data_desc;
+
+typedef struct _phar_zip_file_datadesc_zip64 {
+       php_uint32 crc32;       /* crc-32                          4 bytes */
+       php_uint32 compsize;    /* compressed size                 8 bytes */
+       php_uint32 compsize2;
+       php_uint32 uncompsize;  /* uncompressed size               8 bytes */
+       php_uint32 uncompsize2;
+} phar_zip_data_desc_zip64;
+
+typedef struct _phar_zip_archive_extra_data_record {
+       char signature[4];   /* archive extra data signature    4 bytes  (0x08064b50) */
+       php_uint32 len;      /* extra field length              4 bytes */
+/* extra field data                (variable size) */
+} phar_zip_archive_extra_data_record;
+
+/* madeby/extractneeded value if bzip2 compression is used */
+#define PHAR_ZIP_BZIP2 "46"
+/* madeby/extractneeded value for other cases */
+#define PHAR_ZIP_NORM  "20"
+
+#define PHAR_ZIP_FLAG_ENCRYPTED 0x0001
+/* data descriptor present for this file */
+#define PHAR_ZIP_FLAG_DATADESC  0x0008
+#define PHAR_ZIP_FLAG_UTF8      0x0400
+
+/*
+0 - The file is stored (no compression)
+1 - The file is Shrunk
+2 - The file is Reduced with compression factor 1
+3 - The file is Reduced with compression factor 2
+4 - The file is Reduced with compression factor 3
+5 - The file is Reduced with compression factor 4
+6 - The file is Imploded
+7 - Reserved for Tokenizing compression algorithm
+8 - The file is Deflated
+9 - Enhanced Deflating using Deflate64(tm)
+10 - PKWARE Data Compression Library Imploding (old IBM TERSE)
+11 - Reserved by PKWARE
+12 - File is compressed using BZIP2 algorithm
+13 - Reserved by PKWARE
+14 - LZMA (EFS)
+15 - Reserved by PKWARE
+16 - Reserved by PKWARE
+17 - Reserved by PKWARE
+18 - File is compressed using IBM TERSE (new)
+19 - IBM LZ77 z Architecture (PFS)
+97 - WavPack compressed data
+98 - PPMd version I, Rev 1
+*/
+#define PHAR_ZIP_COMP_NONE    0
+#define PHAR_ZIP_COMP_DEFLATE 8
+#define PHAR_ZIP_COMP_BZIP2   12
+
+/*
+         -ASi Unix Extra Field:
+          ====================
+
+          The following is the layout of the ASi extra block for Unix.  The
+          local-header and central-header versions are identical.
+          (Last Revision 19960916)
+
+          Value         Size        Description
+          -----         ----        -----------
+  (Unix3) 0x756e        Short       tag for this extra block type ("nu")
+          TSize         Short       total data size for this block
+          CRC           Long        CRC-32 of the remaining data
+          Mode          Short       file permissions
+          SizDev        Long        symlink'd size OR major/minor dev num
+          UID           Short       user ID
+          GID           Short       group ID
+          (var.)        variable    symbolic link filename
+
+          Mode is the standard Unix st_mode field from struct stat, containing
+          user/group/other permissions, setuid/setgid and symlink info, etc.
+
+          If Mode indicates that this file is a symbolic link, SizDev is the
+          size of the file to which the link points.  Otherwise, if the file
+          is a device, SizDev contains the standard Unix st_rdev field from
+          struct stat (includes the major and minor numbers of the device).
+          SizDev is undefined in other cases.
+
+          If Mode indicates that the file is a symbolic link, the final field
+          will be the name of the file to which the link points.  The file-
+          name length can be inferred from TSize.
+
+          [Note that TSize may incorrectly refer to the data size not counting
+           the CRC; i.e., it may be four bytes too small.]
+*/
+
+typedef struct _phar_zip_extra_field_header {
+       char tag[2];
+       php_uint16 size;
+} phar_zip_extra_field_header;
+
+typedef struct _phar_zip_unix3 {
+       char tag[2];            /* 0x756e        Short       tag for this extra block type ("nu") */
+       php_uint16 size;        /* TSize         Short       total data size for this block */
+       php_uint32 crc32;       /* CRC           Long        CRC-32 of the remaining data */
+       php_uint16 perms;       /* Mode          Short       file permissions */
+       php_uint32 smylinksize; /* SizDev        Long        symlink'd size OR major/minor dev num */
+       php_uint16 uid;         /* UID           Short       user ID */
+       php_uint16 gid;         /* GID           Short       group ID */
+/* (var.)        variable    symbolic link filename */
+} phar_zip_unix3;
+
+typedef struct _phar_zip_central_dir_file {
+       char signature[4];            /* central file header signature   4 bytes  (0x02014b50) */
+       char madeby[2];               /* version made by                 2 bytes */
+       char zipversion[2];           /* version needed to extract       2 bytes */
+       php_uint16 flags;             /* general purpose bit flag        2 bytes */
+       php_uint16 compressed;        /* compression method              2 bytes */
+       php_uint16 timestamp;         /* last mod file time              2 bytes */
+       php_uint16 datestamp;         /* last mod file date              2 bytes */
+       php_uint32 crc32;             /* crc-32                          4 bytes */
+       php_uint32 compsize;          /* compressed size                 4 bytes */
+       php_uint32 uncompsize;        /* uncompressed size               4 bytes */
+       php_uint16 filename_len;      /* file name length                2 bytes */
+       php_uint16 extra_len;         /* extra field length              2 bytes */
+       php_uint16 comment_len;       /* file comment length             2 bytes */
+       php_uint16 disknumber;        /* disk number start               2 bytes */
+       php_uint16 internal_atts;     /* internal file attributes        2 bytes */
+       php_uint32 external_atts;     /* external file attributes        4 bytes */
+       php_uint32 offset;            /* relative offset of local header 4 bytes */
+
+/* file name (variable size) */
+/* extra field (variable size) */
+/* file comment (variable size) */
+} phar_zip_central_dir_file;
+
+typedef struct _phar_zip_dir_signature {
+       char signature[4];  /* header signature                4 bytes  (0x05054b50) */
+       php_uint16 size;    /* size of data                    2 bytes */
+} phar_zip_dir_signature;
+
+typedef struct _phar_zip64_dir_end {
+       char signature[4];        /* zip64 end of central dir 
+                                 signature                       4 bytes  (0x06064b50) */
+       php_uint32 size1;         /* size of zip64 end of central
+                                 directory record                8 bytes */
+       php_uint32 size2;
+       char madeby[2];           /* version made by                 2 bytes */
+       php_uint16 extractneeded; /* version needed to extract       2 bytes */
+       php_uint32 disknum;       /* number of this disk             4 bytes */
+       php_uint32 cdir_num;      /* number of the disk with the 
+                                 start of the central directory  4 bytes */
+       php_uint32 entries1;      /* total number of entries in the
+                                 central directory on this disk  8 bytes */
+       php_uint32 entries2;
+       php_uint32 entriestotal1; /* total number of entries in the
+                                 central directory               8 bytes */
+       php_uint32 entriestotal2;
+       php_uint32 cdirsize1;     /* size of the central directory   8 bytes */
+       php_uint32 cdirsize2;
+       php_uint32 offset1;       /* offset of start of central
+                                 directory with respect to
+                                 the starting disk number        8 bytes */
+       php_uint32 offset2;
+/* zip64 extensible data sector    (variable size) */
+} phar_zip64_dir_end;
+
+typedef struct _phar_zip64_dir_locator {
+       char signature[4];     /* zip64 end of central dir locator 
+                              signature                       4 bytes  (0x07064b50) */
+       php_uint32 disknum;    /* number of the disk with the
+                              start of the zip64 end of 
+                              central directory               4 bytes */
+       php_uint32 diroffset1; /* relative offset of the zip64
+                              end of central directory record 8 bytes */
+       php_uint32 diroffset2;
+       php_uint32 totaldisks; /* total number of disks           4 bytes */
+} phar_zip64_dir_locator;
+
+typedef struct _phar_zip_dir_end {
+       char signature[4];           /* end of central dir signature    4 bytes  (0x06054b50) */
+       php_uint16 disknumber;       /* number of this disk             2 bytes */
+       php_uint16 centraldisk;      /* number of the disk with the
+                                    start of the central directory  2 bytes */
+       php_uint16 counthere;        /* total number of entries in the
+                                    central directory on this disk  2 bytes */
+       php_uint16 count;            /* total number of entries in
+                                    the central directory           2 bytes */
+       php_uint32 cdir_size;        /* size of the central directory   4 bytes */
+       php_uint32 cdir_offset;      /* offset of start of central
+                                    directory with respect to
+                                    the starting disk number        4 bytes */
+       php_uint16 comment_len;      /* .ZIP file comment length        2 bytes */
+/* .ZIP file comment       (variable size) */
+} phar_zip_dir_end;
+
+BEGIN_EXTERN_C()
+int phar_zip_parse(char *fname, int fname_len, char **error TSRMLS_DC);
+END_EXTERN_C()
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */