summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Rasmussen <sebras@gmail.com>2016-08-13 17:45:14 +0800
committerSebastian Rasmussen <sebras@gmail.com>2016-09-08 18:53:00 +0800
commit15cab201d3c98dc6580c8cf592d94ab226f96db5 (patch)
tree352861e9bf8b5ec8289810501faa0007c19b7f68
parent793ae6a3c2d23743e66039b124f826b232a5a04e (diff)
downloadmupdf-15cab201d3c98dc6580c8cf592d94ab226f96db5.tar.xz
Make fz_archive a generic archive type.
Previously it was inherently tied to zip archives and directories. Now these are separated out into distinct subclasses. This prepares for support for further archive formats.
-rw-r--r--include/mupdf/fitz.h2
-rw-r--r--include/mupdf/fitz/archive.h155
-rw-r--r--include/mupdf/fitz/system.h2
-rw-r--r--include/mupdf/fitz/unzip.h29
-rw-r--r--source/cbz/mucbz.c48
-rw-r--r--source/fitz/archive.c103
-rw-r--r--source/fitz/directory.c87
-rw-r--r--source/fitz/unzip.c357
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)