diff options
-rw-r--r-- | include/mupdf/fitz.h | 2 | ||||
-rw-r--r-- | include/mupdf/fitz/archive.h | 155 | ||||
-rw-r--r-- | include/mupdf/fitz/system.h | 2 | ||||
-rw-r--r-- | include/mupdf/fitz/unzip.h | 29 | ||||
-rw-r--r-- | source/cbz/mucbz.c | 48 | ||||
-rw-r--r-- | source/fitz/archive.c | 103 | ||||
-rw-r--r-- | source/fitz/directory.c | 87 | ||||
-rw-r--r-- | source/fitz/unzip.c | 357 |
8 files changed, 529 insertions, 254 deletions
diff --git a/include/mupdf/fitz.h b/include/mupdf/fitz.h index b299882f..902f1bf8 100644 --- a/include/mupdf/fitz.h +++ b/include/mupdf/fitz.h @@ -27,7 +27,7 @@ extern "C" { #include "mupdf/fitz/compressed-buffer.h" #include "mupdf/fitz/filter.h" #include "mupdf/fitz/output.h" -#include "mupdf/fitz/unzip.h" +#include "mupdf/fitz/archive.h" /* Resources */ #include "mupdf/fitz/store.h" diff --git a/include/mupdf/fitz/archive.h b/include/mupdf/fitz/archive.h new file mode 100644 index 00000000..89f17b02 --- /dev/null +++ b/include/mupdf/fitz/archive.h @@ -0,0 +1,155 @@ +#ifndef MUPDF_FITZ_ARCHIVE_H +#define MUPDF_FITZ_ARCHIVE_H + +#include "mupdf/fitz/system.h" +#include "mupdf/fitz/context.h" +#include "mupdf/fitz/buffer.h" +#include "mupdf/fitz/stream.h" + +typedef struct fz_archive_s fz_archive; + +struct fz_archive_s +{ + fz_stream *file; + const char *format; + + void (*drop_archive)(fz_context *ctx, fz_archive *arch); + int (*count_entries)(fz_context *ctx, fz_archive *arch); + const char *(*list_entry)(fz_context *ctx, fz_archive *arch, int idx); + int (*has_entry)(fz_context *ctx, fz_archive *arch, const char *name); + fz_buffer *(*read_entry)(fz_context *ctx, fz_archive *arch, const char *name); + fz_stream *(*open_entry)(fz_context *ctx, fz_archive *arch, const char *name); +}; + +/* + fz_new_archive: Create and initialize an archive struct. +*/ +fz_archive *fz_new_archive_of_size(fz_context *ctx, fz_stream *file, int size); +#define fz_new_archive(C,F,M) ((M*)Memento_label(fz_new_archive_of_size(C, F, sizeof(M)), #M)) + +/* + fz_open_archive: Open a zip archive + + Open a file and identify its archive type based on the archive + signature contained inside. + + filename: a path to a file as it would be given to open(2). +*/ +fz_archive *fz_open_archive(fz_context *ctx, const char *filename); + +/* + fz_open_archive_with_stream: Open zip archive stream. + + Open an archive using a seekable stream object rather than + opening a file or directory on disk. +*/ +fz_archive *fz_open_archive_with_stream(fz_context *ctx, fz_stream *file); + +/* + fz_open_directory: Open a directory as if it was an archive. + + A special case where a directory is opened as if it was an + archive. + + Note that for directories it is not possible to retrieve the + number of entries or list the entries. It is however possible + to check if the archive has a particular entry. + + path: a path to a directory as it would be given to opendir(3). +*/ +fz_archive *fz_open_directory(fz_context *ctx, const char *path); + +/* + fz_drop_archive: Release an open archive. + + Any allocations for the archive are freed. +*/ +void fz_drop_archive(fz_context *ctx, fz_archive *arch); + +/* + fz_archive_format: Returns the name of the archive format. +*/ +const char *fz_archive_format(fz_context *ctx, fz_archive *arch); + +/* + fz_count_archive_entries: Number of entries in archive. + + Will always return a value >= 0. +*/ +int fz_count_archive_entries(fz_context *ctx, fz_archive *arch); + +/* + fz_list_archive_entry: Get listed name of entry position idx. + + idx: Must be a value >= 0 < return value from + fz_count_archive_entries. If not in range NULL will be + returned. +*/ +const char *fz_list_archive_entry(fz_context *ctx, fz_archive *arch, int idx); + +/* + fz_has_archive_entry: Check if entry by given name exists. + + If named entry does not exist 0 will be returned, if it does + exist 1 is returned. + + nane: Entry name to look for, this must be an exact match to + the entry name in the archive. +*/ +int fz_has_archive_entry(fz_context *ctx, fz_archive *arch, const char *name); + +/* + fz_open_archive_entry: Opens an archive entry as a stream. + + nane: Entry name to look for, this must be an exact match to + the entry name in the archive. +*/ +fz_stream *fz_open_archive_entry(fz_context *ctx, fz_archive *arch, const char *name); + +/* + fz_read_archive_entry: Reads all bytes in an archive entry + into a buffer. + + nane: Entry name to look for, this must be an exact match to + the entry name in the archive. +*/ + +fz_buffer *fz_read_archive_entry(fz_context *ctx, fz_archive *arch, const char *name); +/* + fz_is_zip_archive: Detect if stream object is a zip achive. + + Assumes that the stream object is seekable. +*/ +int fz_is_zip_archive(fz_context *ctx, fz_stream *file); + +/* + fz_open_zip_archive: Open a zip archive file. + + An exception is throw if the file is not a zip archive as + indicated by the presence of a zip signature. + + filename: a path to a zip archive file as it would be given to + open(2). +*/ +fz_archive *fz_open_zip_archive(fz_context *ctx, const char *path); + +/* + fz_open_zip_archive: Open a zip archive stream. + + Open an archive using a seekable stream object rather than + opening a file or directory on disk. + + An exception is throw if the stream is not a zip archive as + indicated by the presence of a zip signature. + +*/ +fz_archive *fz_open_zip_archive_with_stream(fz_context *ctx, fz_stream *file); + +typedef struct fz_zip_writer_s fz_zip_writer; + +fz_zip_writer *fz_new_zip_writer(fz_context *ctx, const char *filename); +void fz_write_zip_entry(fz_context *ctx, fz_zip_writer *zip, const char *name, fz_buffer *buf, int compress); +void fz_close_zip_writer(fz_context *ctx, fz_zip_writer *zip); +void fz_drop_zip_writer(fz_context *ctx, fz_zip_writer *zip); + +#endif diff --git a/include/mupdf/fitz/system.h b/include/mupdf/fitz/system.h index a82d503f..ffa238cf 100644 --- a/include/mupdf/fitz/system.h +++ b/include/mupdf/fitz/system.h @@ -165,11 +165,13 @@ void fz_free_argv(int argc, char **argv); #define fseeko64 _fseeki64 #define ftello64 _ftelli64 #define atoll _atoi64 +#define stat _stat #else /* Unix or close enough */ #include <stdint.h> #include <unistd.h> +#include <sys/stat.h> #ifndef O_BINARY #define O_BINARY 0 diff --git a/include/mupdf/fitz/unzip.h b/include/mupdf/fitz/unzip.h deleted file mode 100644 index c6295b90..00000000 --- a/include/mupdf/fitz/unzip.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MUPDF_FITZ_UNZIP_H -#define MUPDF_FITZ_UNZIP_H - -#include "mupdf/fitz/system.h" -#include "mupdf/fitz/context.h" -#include "mupdf/fitz/buffer.h" -#include "mupdf/fitz/stream.h" - -typedef struct fz_archive_s fz_archive; - -fz_archive *fz_open_directory(fz_context *ctx, const char *dirname); -fz_archive *fz_open_archive(fz_context *ctx, const char *filename); -fz_archive *fz_open_archive_with_stream(fz_context *ctx, fz_stream *file); -int fz_has_archive_entry(fz_context *ctx, fz_archive *zip, const char *name); -fz_stream *fz_open_archive_entry(fz_context *ctx, fz_archive *zip, const char *entry); -fz_buffer *fz_read_archive_entry(fz_context *ctx, fz_archive *zip, const char *entry); -void fz_drop_archive(fz_context *ctx, fz_archive *ar); - -int fz_count_archive_entries(fz_context *ctx, fz_archive *zip); -const char *fz_list_archive_entry(fz_context *ctx, fz_archive *zip, int idx); - -typedef struct fz_zip_writer_s fz_zip_writer; - -fz_zip_writer *fz_new_zip_writer(fz_context *ctx, const char *filename); -void fz_write_zip_entry(fz_context *ctx, fz_zip_writer *zip, const char *name, fz_buffer *buf, int compress); -void fz_close_zip_writer(fz_context *ctx, fz_zip_writer *zip); -void fz_drop_zip_writer(fz_context *ctx, fz_zip_writer *zip); - -#endif diff --git a/source/cbz/mucbz.c b/source/cbz/mucbz.c index 0b5de93e..17a881cc 100644 --- a/source/cbz/mucbz.c +++ b/source/cbz/mucbz.c @@ -19,7 +19,7 @@ struct cbz_page_s struct cbz_document_s { fz_document super; - fz_archive *zip; + fz_archive *arch; int page_count; const char **page; }; @@ -75,10 +75,10 @@ cbz_compare_page_names(const void *a, const void *b) static void cbz_create_page_list(fz_context *ctx, cbz_document *doc) { - fz_archive *zip = doc->zip; + fz_archive *arch = doc->arch; int i, k, count; - count = fz_count_archive_entries(ctx, zip); + count = fz_count_archive_entries(ctx, arch); doc->page_count = 0; doc->page = fz_malloc_array(ctx, count, sizeof *doc->page); @@ -87,8 +87,13 @@ cbz_create_page_list(fz_context *ctx, cbz_document *doc) { for (k = 0; cbz_ext_list[k]; k++) { - const char *name = fz_list_archive_entry(ctx, zip, i); - const char *ext = name ? strrchr(name, '.') : NULL; + const char *name; + const char *ext; + + if (arch) + name = fz_list_archive_entry(ctx, arch, i); + + ext = name ? strrchr(name, '.') : NULL; if (ext && !fz_strcasecmp(ext, cbz_ext_list[k])) { doc->page[doc->page_count++] = name; @@ -103,7 +108,7 @@ cbz_create_page_list(fz_context *ctx, cbz_document *doc) static void cbz_drop_document(fz_context *ctx, cbz_document *doc) { - fz_drop_archive(ctx, doc->zip); + fz_drop_archive(ctx, doc->arch); fz_free(ctx, (char **)doc->page); fz_free(ctx, doc); } @@ -155,7 +160,7 @@ cbz_load_page(fz_context *ctx, cbz_document *doc, int number) { unsigned char *data = NULL; cbz_page *page = NULL; - fz_buffer *buf; + fz_buffer *buf = NULL; if (number < 0 || number >= doc->page_count) return NULL; @@ -163,7 +168,11 @@ cbz_load_page(fz_context *ctx, cbz_document *doc, int number) fz_var(data); fz_var(page); - buf = fz_read_archive_entry(ctx, doc->zip, doc->page[number]); + if (doc->arch) + buf = fz_read_archive_entry(ctx, doc->arch, doc->page[number]); + if (!buf) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot load cbz page"); + fz_try(ctx) { page = fz_new_page(ctx, sizeof *page); @@ -190,14 +199,16 @@ static int cbz_lookup_metadata(fz_context *ctx, cbz_document *doc, const char *key, char *buf, int size) { if (!strcmp(key, "format")) - return (int)fz_strlcpy(buf, "CBZ", size); + return (int) fz_strlcpy(buf, fz_archive_format(ctx, doc->arch), size); return -1; } static cbz_document * cbz_open_document_with_stream(fz_context *ctx, fz_stream *file) { - cbz_document *doc = fz_new_document(ctx, cbz_document); + cbz_document *doc; + + doc = fz_new_document(ctx, cbz_document); doc->super.drop_document = (fz_document_drop_fn *)cbz_drop_document; doc->super.count_pages = (fz_document_count_pages_fn *)cbz_count_pages; @@ -206,7 +217,7 @@ cbz_open_document_with_stream(fz_context *ctx, fz_stream *file) fz_try(ctx) { - doc->zip = fz_open_archive_with_stream(ctx, file); + doc->arch = fz_open_archive_with_stream(ctx, file); cbz_create_page_list(ctx, doc); } fz_catch(ctx) @@ -236,18 +247,15 @@ cbz_open_document(fz_context *ctx, const char *filename) } static int -cbz_recognize(fz_context *doc, const char *magic) +cbz_recognize(fz_context *ctx, const char *magic) { char *ext = strrchr(magic, '.'); - - if (ext) - { - if (!fz_strcasecmp(ext, ".cbz") || !fz_strcasecmp(ext, ".zip")) - return 100; - } - if (!strcmp(magic, "cbz") || !strcmp(magic, "application/x-cbz")) + if ((ext && !fz_strcasecmp(ext, ".cbz")) || !strcmp(magic, "cbz") || + !strcmp(magic, "application/x-cbz")) + return 100; + if ((ext && !fz_strcasecmp(ext, ".zip")) || !strcmp(magic, "zip") || + !strcmp(magic, "application/zip")) return 100; - return 0; } diff --git a/source/fitz/archive.c b/source/fitz/archive.c new file mode 100644 index 00000000..a61ceb01 --- /dev/null +++ b/source/fitz/archive.c @@ -0,0 +1,103 @@ +#include "mupdf/fitz.h" + +fz_stream * +fz_open_archive_entry(fz_context *ctx, fz_archive *arch, const char *name) +{ + if (!arch->open_entry) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open archive entry"); + return arch->open_entry(ctx, arch, name); +} + +fz_buffer * +fz_read_archive_entry(fz_context *ctx, fz_archive *arch, const char *name) +{ + if (!arch->read_entry) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot read archive entry"); + return arch->read_entry(ctx, arch, name); +} + +int +fz_has_archive_entry(fz_context *ctx, fz_archive *arch, const char *name) +{ + if (!arch->has_entry) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot check if archive has entry"); + return arch->has_entry(ctx, arch, name); +} + +const char * +fz_list_archive_entry(fz_context *ctx, fz_archive *arch, int idx) +{ + if (!arch->list_entry) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot list archive entries"); + return arch->list_entry(ctx, arch, idx); +} +int +fz_count_archive_entries(fz_context *ctx, fz_archive *arch) +{ + if (!arch->count_entries) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot count archive entries"); + return arch->count_entries(ctx, arch); +} + +const char * +fz_archive_format(fz_context *ctx, fz_archive *arch) +{ + return arch->format; +} + +fz_archive * +fz_new_archive_of_size(fz_context *ctx, fz_stream *file, int size) +{ + fz_archive *arch; + arch = Memento_label(fz_calloc(ctx, 1, size), "fz_archive"); + arch->file = fz_keep_stream(ctx, file); + return arch; +} + +fz_archive * +fz_open_archive_with_stream(fz_context *ctx, fz_stream *file) +{ + fz_archive *arch = NULL; + + fz_try(ctx) + { + if (fz_is_zip_archive(ctx, file)) + arch = fz_open_zip_archive_with_stream(ctx, file); + else + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot recognize archive"); + } + fz_catch(ctx) + fz_rethrow(ctx); + + return arch; +} + +fz_archive * +fz_open_archive(fz_context *ctx, const char *filename) +{ + fz_stream *file; + fz_archive *arch = NULL; + + file = fz_open_file(ctx, filename); + + fz_try(ctx) + arch = fz_open_archive_with_stream(ctx, file); + fz_always(ctx) + fz_drop_stream(ctx, file); + fz_catch(ctx) + fz_rethrow(ctx); + + return arch; +} + +void +fz_drop_archive(fz_context *ctx, fz_archive *arch) +{ + if (!arch) + return; + + if (arch->drop_archive) + arch->drop_archive(ctx, arch); + fz_drop_stream(ctx, arch->file); + fz_free(ctx, arch); +} diff --git a/source/fitz/directory.c b/source/fitz/directory.c new file mode 100644 index 00000000..aba11eb9 --- /dev/null +++ b/source/fitz/directory.c @@ -0,0 +1,87 @@ +#include "mupdf/fitz.h" + +typedef struct fz_directory_s fz_directory; +struct fz_directory_s +{ + fz_archive super; + + char *path; +}; + +static void drop_directory(fz_context *ctx, fz_archive *arch) +{ + fz_directory *dir = (fz_directory *) arch; + + fz_free(ctx, dir->path); +} + +static fz_stream *open_dir_entry(fz_context *ctx, fz_archive *arch, const char *name) +{ + fz_directory *dir = (fz_directory *) arch; + char path[2048]; + fz_strlcpy(path, dir->path, sizeof path); + fz_strlcat(path, "/", sizeof path); + fz_strlcat(path, name, sizeof path); + return fz_open_file(ctx, path); +} + +static fz_buffer *read_dir_entry(fz_context *ctx, fz_archive *arch, const char *name) +{ + fz_directory *dir = (fz_directory *) arch; + char path[2048]; + fz_strlcpy(path, dir->path, sizeof path); + fz_strlcat(path, "/", sizeof path); + fz_strlcat(path, name, sizeof path); + return fz_read_file(ctx, path); +} + +static int has_dir_entry(fz_context *ctx, fz_archive *arch, const char *name) +{ + fz_directory *dir = (fz_directory *) arch; + char path[2048]; + fz_strlcpy(path, dir->path, sizeof path); + fz_strlcat(path, "/", sizeof path); + fz_strlcat(path, name, sizeof path); + return fz_file_exists(ctx, path); +} + +int +fz_is_directory(fz_context *ctx, const char *path) +{ + struct stat info; + + if (stat(path, &info) < 0) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot stat: %s", strerror(errno)); + + return info.st_mode & S_IFDIR; +} + +fz_archive * +fz_open_directory(fz_context *ctx, const char *path) +{ + fz_directory *dir = NULL; + + fz_var(dir); + + fz_try(ctx) + { + if (!fz_is_directory(ctx, path)) + fz_throw(ctx, FZ_ERROR_GENERIC, "'%s' is not a directory", path); + + dir = fz_new_archive(ctx, NULL, fz_directory); + dir->super.format = "dir"; + dir->super.has_entry = has_dir_entry; + dir->super.read_entry = read_dir_entry; + dir->super.open_entry = open_dir_entry; + dir->super.drop_archive = drop_directory; + + dir->path = fz_strdup(ctx, path); + } + fz_catch(ctx) + { + fz_drop_archive(ctx, &dir->super); + fz_rethrow(ctx); + } + + return &dir->super; +} diff --git a/source/fitz/unzip.c b/source/fitz/unzip.c index 1d386209..3c5c0da4 100644 --- a/source/fitz/unzip.c +++ b/source/fitz/unzip.c @@ -17,70 +17,42 @@ #define ZIP_ENCRYPTED_FLAG 0x1 -struct zip_entry +typedef struct zip_entry_s zip_entry; +typedef struct fz_zip_archive_s fz_zip_archive; + +struct zip_entry_s { char *name; int offset, csize, usize; }; -struct fz_archive_s +struct fz_zip_archive_s { - char *directory; - fz_stream *file; + fz_archive super; + int count; - struct zip_entry *table; + zip_entry *entries; }; -static inline int zip_toupper(int c) -{ - if (c >= 'a' && c <= 'z') - return c - 'a' + 'A'; - return c; -} - -static int zip_strcasecmp(const char *a, const char *b) -{ - while (zip_toupper(*a) == zip_toupper(*b)) - { - if (*a++ == 0) - return 0; - b++; - } - return zip_toupper(*a) - zip_toupper(*b); -} - -static int case_compare_entries(const void *a_, const void *b_) -{ - const struct zip_entry *a = a_; - const struct zip_entry *b = b_; - return zip_strcasecmp(a->name, b->name); -} - -static struct zip_entry *lookup_zip_entry(fz_context *ctx, fz_archive *zip, const char *name) +static void drop_zip_archive(fz_context *ctx, fz_archive *arch) { - int l = 0; - int r = zip->count - 1; - while (l <= r) - { - int m = (l + r) >> 1; - int c = zip_strcasecmp(name, zip->table[m].name); - if (c < 0) - r = m - 1; - else if (c > 0) - l = m + 1; - else - return &zip->table[m]; - } - return NULL; + fz_zip_archive *zip = (fz_zip_archive *) arch; + int i; + for (i = 0; i < zip->count; ++i) + fz_free(ctx, zip->entries[i].name); + fz_free(ctx, zip->entries); } -static void read_zip_dir_imp(fz_context *ctx, fz_archive *zip, int start_offset) +static void read_zip_dir_imp(fz_context *ctx, fz_zip_archive *zip, int start_offset) { - fz_stream *file = zip->file; + fz_stream *file = zip->super.file; int sig; - int offset, count; + int i, count, offset, csize, usize; int namesize, metasize, commentsize; - int i; + char *name; + size_t n; + + zip->count = 0; fz_seek(ctx, file, start_offset, 0); @@ -141,10 +113,6 @@ static void read_zip_dir_imp(fz_context *ctx, fz_archive *zip, int start_offset) } } - zip->count = count; - zip->table = fz_malloc_array(ctx, count, sizeof *zip->table); - memset(zip->table, 0, count * sizeof *zip->table); - fz_seek(ctx, file, offset, 0); for (i = 0; i < count; i++) @@ -160,19 +128,21 @@ static void read_zip_dir_imp(fz_context *ctx, fz_archive *zip, int start_offset) (void) fz_read_int16_le(ctx, file); /* last mod file time */ (void) fz_read_int16_le(ctx, file); /* last mod file date */ (void) fz_read_int32_le(ctx, file); /* crc-32 */ - zip->table[i].csize = fz_read_int32_le(ctx, file); - zip->table[i].usize = fz_read_int32_le(ctx, file); + csize = fz_read_int32_le(ctx, file); + usize = fz_read_int32_le(ctx, file); namesize = fz_read_int16_le(ctx, file); metasize = fz_read_int16_le(ctx, file); commentsize = fz_read_int16_le(ctx, file); (void) fz_read_int16_le(ctx, file); /* disk number start */ (void) fz_read_int16_le(ctx, file); /* int file atts */ (void) fz_read_int32_le(ctx, file); /* ext file atts */ - zip->table[i].offset = fz_read_int32_le(ctx, file); + offset = fz_read_int32_le(ctx, file); - zip->table[i].name = fz_malloc(ctx, namesize + 1); - fz_read(ctx, file, (unsigned char*)zip->table[i].name, namesize); - zip->table[i].name[namesize] = 0; + name = fz_malloc(ctx, namesize + 1); + n = fz_read(ctx, file, (unsigned char*)name, namesize); + if (n < namesize) + fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of data in zip entry name"); + name[namesize] = '\0'; while (metasize > 0) { @@ -181,19 +151,19 @@ static void read_zip_dir_imp(fz_context *ctx, fz_archive *zip, int start_offset) if (type == ZIP64_EXTRA_FIELD_SIG) { int sizeleft = size; - if (zip->table[i].usize == 0xFFFFFFFF && sizeleft >= 8) + if (usize == 0xFFFFFFFF && sizeleft >= 8) { - zip->table[i].usize = fz_read_int64_le(ctx, file); + usize = fz_read_int64_le(ctx, file); sizeleft -= 8; } - if (zip->table[i].csize == 0xFFFFFFFF && sizeleft >= 8) + if (csize == 0xFFFFFFFF && sizeleft >= 8) { - zip->table[i].csize = fz_read_int64_le(ctx, file); + csize = fz_read_int64_le(ctx, file); sizeleft -= 8; } - if (zip->table[i].offset == 0xFFFFFFFF && sizeleft >= 8) + if (offset == 0xFFFFFFFF && sizeleft >= 8) { - zip->table[i].offset = fz_read_int64_le(ctx, file); + offset = fz_read_int64_le(ctx, file); sizeleft -= 8; } fz_seek(ctx, file, sizeleft - size, 1); @@ -201,18 +171,55 @@ static void read_zip_dir_imp(fz_context *ctx, fz_archive *zip, int start_offset) fz_seek(ctx, file, size, 1); metasize -= 4 + size; } - if (zip->table[i].usize < 0 || zip->table[i].csize < 0 || zip->table[i].offset < 0) + if (usize < 0 || csize < 0 || offset < 0) fz_throw(ctx, FZ_ERROR_GENERIC, "zip64 files larger than 2 GB are not supported"); fz_seek(ctx, file, commentsize, 1); + + zip->entries = fz_resize_array(ctx, zip->entries, zip->count + 1, sizeof *zip->entries); + + zip->entries[zip->count].name = name; + zip->entries[zip->count].offset = offset; + zip->entries[zip->count].csize = csize; + zip->entries[zip->count].usize = usize; + + zip->count++; } +} + +static int read_zip_entry_header(fz_context *ctx, fz_zip_archive *zip, zip_entry *ent) +{ + fz_stream *file = zip->super.file; + int sig, general, method, namelength, extralength; + + fz_seek(ctx, file, ent->offset, 0); - qsort(zip->table, count, sizeof *zip->table, case_compare_entries); + sig = fz_read_int32_le(ctx, file); + if (sig != ZIP_LOCAL_FILE_SIG) + fz_throw(ctx, FZ_ERROR_GENERIC, "wrong zip local file signature (0x%x)", sig); + + (void) fz_read_int16_le(ctx, file); /* version */ + general = fz_read_int16_le(ctx, file); /* general */ + if (general & ZIP_ENCRYPTED_FLAG) + fz_throw(ctx, FZ_ERROR_GENERIC, "zip content is encrypted"); + + method = fz_read_int16_le(ctx, file); + (void) fz_read_int16_le(ctx, file); /* file time */ + (void) fz_read_int16_le(ctx, file); /* file date */ + (void) fz_read_int32_le(ctx, file); /* crc-32 */ + (void) fz_read_int32_le(ctx, file); /* csize */ + (void) fz_read_int32_le(ctx, file); /* usize */ + namelength = fz_read_int16_le(ctx, file); + extralength = fz_read_int16_le(ctx, file); + + fz_seek(ctx, file, namelength + extralength, 1); + + return method; } -static void read_zip_dir(fz_context *ctx, fz_archive *zip) +static void ensure_zip_entries(fz_context *ctx, fz_zip_archive *zip) { - fz_stream *file = zip->file; + fz_stream *file = zip->super.file; unsigned char buf[512]; size_t size, back, maxback; size_t i, n; @@ -230,53 +237,38 @@ static void read_zip_dir(fz_context *ctx, fz_archive *zip) if (n < 4) break; for (i = n - 4; i > 0; i--) - { if (!memcmp(buf + i, "PK\5\6", 4)) { read_zip_dir_imp(ctx, zip, (int)(size - back + i)); return; } - } back += sizeof buf - 4; } fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find end of central directory"); } -static int read_zip_entry_header(fz_context *ctx, fz_archive *zip, struct zip_entry *ent) +static zip_entry *lookup_zip_entry(fz_context *ctx, fz_zip_archive *zip, const char *name) { - fz_stream *file = zip->file; - int sig, general, method, namelength, extralength; - - fz_seek(ctx, file, ent->offset, 0); - - sig = fz_read_int32_le(ctx, file); - if (sig != ZIP_LOCAL_FILE_SIG) - fz_throw(ctx, FZ_ERROR_GENERIC, "wrong zip local file signature (0x%x)", sig); - - (void) fz_read_int16_le(ctx, file); /* version */ - general = fz_read_int16_le(ctx, file); /* general */ - if (general & ZIP_ENCRYPTED_FLAG) - fz_throw(ctx, FZ_ERROR_GENERIC, "zip content is encrypted"); - - method = fz_read_int16_le(ctx, file); - (void) fz_read_int16_le(ctx, file); /* file time */ - (void) fz_read_int16_le(ctx, file); /* file date */ - (void) fz_read_int32_le(ctx, file); /* crc-32 */ - (void) fz_read_int32_le(ctx, file); /* csize */ - (void) fz_read_int32_le(ctx, file); /* usize */ - namelength = fz_read_int16_le(ctx, file); - extralength = fz_read_int16_le(ctx, file); - - fz_seek(ctx, file, namelength + extralength, 1); - - return method; + int i; + for (i = 0; i < zip->count; i++) + if (!fz_strcasecmp(name, zip->entries[i].name)) + return &zip->entries[i]; + return NULL; } -static fz_stream *open_zip_entry(fz_context *ctx, fz_archive *zip, struct zip_entry *ent) +static fz_stream *open_zip_entry(fz_context *ctx, fz_archive *arch, const char *name) { - fz_stream *file = zip->file; - int method = read_zip_entry_header(ctx, zip, ent); + fz_zip_archive *zip = (fz_zip_archive *) arch; + fz_stream *file = zip->super.file; + int method; + zip_entry *ent; + + ent = lookup_zip_entry(ctx, zip, name); + if (!ent) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find named zip archive entry"); + + method = read_zip_entry_header(ctx, zip, ent); if (method == 0) return fz_open_null(ctx, file, ent->usize, fz_tell(ctx, file)); if (method == 8) @@ -284,29 +276,32 @@ static fz_stream *open_zip_entry(fz_context *ctx, fz_archive *zip, struct zip_en fz_throw(ctx, FZ_ERROR_GENERIC, "unknown zip method: %d", method); } -static fz_buffer *read_zip_entry(fz_context *ctx, fz_archive *zip, struct zip_entry *ent) +static fz_buffer *read_zip_entry(fz_context *ctx, fz_archive *arch, const char *name) { - fz_stream *file = zip->file; + fz_zip_archive *zip = (fz_zip_archive *) arch; + fz_stream *file = zip->super.file; fz_buffer *ubuf; unsigned char *cbuf; int method; z_stream z; int code; int len; + zip_entry *ent; - method = read_zip_entry_header(ctx, zip, ent); + ent = lookup_zip_entry(ctx, zip, name); + if (!ent) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find named zip archive entry"); + method = read_zip_entry_header(ctx, zip, ent); ubuf = fz_new_buffer(ctx, ent->usize + 1); /* +1 because many callers will add a terminating zero */ - ubuf->len = ent->usize; if (method == 0) { fz_try(ctx) { - len = fz_read(ctx, file, ubuf->data, ubuf->len); - if (len < ubuf->len) - fz_warn(ctx, "premature end of data in stored archive entry"); - ubuf->len = len; + ubuf->len = fz_read(ctx, file, ubuf->data, ent->usize); + if (ubuf->len < ent->usize) + fz_warn(ctx, "premature end of data in stored zip archive entry"); } fz_catch(ctx) { @@ -315,8 +310,7 @@ static fz_buffer *read_zip_entry(fz_context *ctx, fz_archive *zip, struct zip_en } return ubuf; } - - if (method == 8) + else if (method == 8) { cbuf = fz_malloc(ctx, ent->csize); fz_try(ctx) @@ -349,7 +343,7 @@ static fz_buffer *read_zip_entry(fz_context *ctx, fz_archive *zip, struct zip_en } len = ent->usize - z.avail_out; - if (len < ubuf->len) + if (len < ent->usize) fz_warn(ctx, "premature end of data in compressed archive entry"); ubuf->len = len; } @@ -369,133 +363,88 @@ static fz_buffer *read_zip_entry(fz_context *ctx, fz_archive *zip, struct zip_en fz_throw(ctx, FZ_ERROR_GENERIC, "unknown zip method: %d", method); } -int -fz_has_archive_entry(fz_context *ctx, fz_archive *zip, const char *name) -{ - if (zip->directory) - { - char path[2048]; - fz_strlcpy(path, zip->directory, sizeof path); - fz_strlcat(path, "/", sizeof path); - fz_strlcat(path, name, sizeof path); - return fz_file_exists(ctx, path); - } - else - { - return lookup_zip_entry(ctx, zip, name) != NULL; - } -} - -fz_stream * -fz_open_archive_entry(fz_context *ctx, fz_archive *zip, const char *name) -{ - if (zip->directory) - { - char path[2048]; - fz_strlcpy(path, zip->directory, sizeof path); - fz_strlcat(path, "/", sizeof path); - fz_strlcat(path, name, sizeof path); - return fz_open_file(ctx, path); - } - else - { - struct zip_entry *ent = lookup_zip_entry(ctx, zip, name); - if (!ent) - fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find zip entry: '%s'", name); - return open_zip_entry(ctx, zip, ent); - } -} - -fz_buffer * -fz_read_archive_entry(fz_context *ctx, fz_archive *zip, const char *name) -{ - if (zip->directory) - { - char path[2048]; - fz_strlcpy(path, zip->directory, sizeof path); - fz_strlcat(path, "/", sizeof path); - fz_strlcat(path, name, sizeof path); - return fz_read_file(ctx, path); - } - else - { - struct zip_entry *ent = lookup_zip_entry(ctx, zip, name); - if (!ent) - fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find zip entry: '%s'", name); - return read_zip_entry(ctx, zip, ent); - } -} - -int -fz_count_archive_entries(fz_context *ctx, fz_archive *zip) +static int has_zip_entry(fz_context *ctx, fz_archive *arch, const char *name) { - return zip->count; + fz_zip_archive *zip = (fz_zip_archive *) arch; + zip_entry *ent = lookup_zip_entry(ctx, zip, name); + return ent != NULL; } -const char * -fz_list_archive_entry(fz_context *ctx, fz_archive *zip, int idx) +static const char *list_zip_entry(fz_context *ctx, fz_archive *arch, int idx) { + fz_zip_archive *zip = (fz_zip_archive *) arch; if (idx < 0 || idx >= zip->count) return NULL; - return zip->table[idx].name; + return zip->entries[idx].name; } -void -fz_drop_archive(fz_context *ctx, fz_archive *zip) +static int count_zip_entries(fz_context *ctx, fz_archive *arch) { - int i; - if (zip) - { - fz_free(ctx, zip->directory); - fz_drop_stream(ctx, zip->file); - for (i = 0; i < zip->count; ++i) - fz_free(ctx, zip->table[i].name); - fz_free(ctx, zip->table); - fz_free(ctx, zip); - } + fz_zip_archive *zip = (fz_zip_archive *) arch; + return zip->count; } -fz_archive * -fz_open_directory(fz_context *ctx, const char *dirname) +int +fz_is_zip_archive(fz_context *ctx, fz_stream *file) { - fz_archive *zip = fz_malloc_struct(ctx, fz_archive); - zip->directory = fz_strdup(ctx, dirname); - return zip; + const unsigned char signature[4] = { 'P', 'K', 0x03, 0x04 }; + unsigned char data[4]; + size_t n; + + fz_seek(ctx, file, 0, 0); + n = fz_read(ctx, file, data, nelem(data)); + if (n != nelem(signature)) + return 0; + if (memcmp(data, signature, nelem(signature))) + return 0; + + return 1; } fz_archive * -fz_open_archive_with_stream(fz_context *ctx, fz_stream *file) +fz_open_zip_archive_with_stream(fz_context *ctx, fz_stream *file) { - fz_archive *zip; + fz_zip_archive *zip; - zip = fz_malloc_struct(ctx, fz_archive); - zip->file = fz_keep_stream(ctx, file); - zip->count = 0; - zip->table = NULL; + if (!fz_is_zip_archive(ctx, file)) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot recognize zip archive"); + + zip = fz_new_archive(ctx, file, fz_zip_archive); fz_try(ctx) { - read_zip_dir(ctx, zip); + + zip->super.format = "zip"; + zip->super.count_entries = count_zip_entries; + zip->super.list_entry = list_zip_entry; + zip->super.has_entry = has_zip_entry; + zip->super.read_entry = read_zip_entry; + zip->super.open_entry = open_zip_entry; + zip->super.drop_archive = drop_zip_archive; + + ensure_zip_entries(ctx, zip); } fz_catch(ctx) { - fz_drop_archive(ctx, zip); + fz_drop_archive(ctx, &zip->super); fz_rethrow(ctx); } - return zip; + return &zip->super; } fz_archive * -fz_open_archive(fz_context *ctx, const char *filename) +fz_open_zip_archive(fz_context *ctx, const char *filename) { + fz_archive *zip = NULL; fz_stream *file; - fz_archive *zip; file = fz_open_file(ctx, filename); + fz_var(zip); + fz_try(ctx) - zip = fz_open_archive_with_stream(ctx, file); + zip = fz_open_zip_archive_with_stream(ctx, file); fz_always(ctx) fz_drop_stream(ctx, file); fz_catch(ctx) |