summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorSebastian Rasmussen <sebras@gmail.com>2016-08-14 01:54:33 +0800
committerSebastian Rasmussen <sebras@gmail.com>2016-09-08 18:53:00 +0800
commit8264d4968b5a981bd1485546ec96a4e874f11d74 (patch)
treedaab0bd414c39f37de5c856bf91745862ce383af /source
parent15cab201d3c98dc6580c8cf592d94ab226f96db5 (diff)
downloadmupdf-8264d4968b5a981bd1485546ec96a4e874f11d74.tar.xz
Add support for GNU tar archives.
Diffstat (limited to 'source')
-rw-r--r--source/cbz/mucbz.c6
-rw-r--r--source/fitz/archive.c2
-rw-r--r--source/fitz/untar.c235
3 files changed, 243 insertions, 0 deletions
diff --git a/source/cbz/mucbz.c b/source/cbz/mucbz.c
index 17a881cc..8baea04f 100644
--- a/source/cbz/mucbz.c
+++ b/source/cbz/mucbz.c
@@ -256,6 +256,12 @@ cbz_recognize(fz_context *ctx, const char *magic)
if ((ext && !fz_strcasecmp(ext, ".zip")) || !strcmp(magic, "zip") ||
!strcmp(magic, "application/zip"))
return 100;
+ if ((ext && !fz_strcasecmp(ext, ".tar")) || !strcmp(magic, "tar") ||
+ !strcmp(magic, "application/x-tar"))
+ return 100;
+ if ((ext && !fz_strcasecmp(ext, ".cbt")) || !strcmp(magic, "cbt") ||
+ !strcmp(magic, "application/x-cbt"))
+ return 100;
return 0;
}
diff --git a/source/fitz/archive.c b/source/fitz/archive.c
index a61ceb01..5bd3434b 100644
--- a/source/fitz/archive.c
+++ b/source/fitz/archive.c
@@ -63,6 +63,8 @@ fz_open_archive_with_stream(fz_context *ctx, fz_stream *file)
{
if (fz_is_zip_archive(ctx, file))
arch = fz_open_zip_archive_with_stream(ctx, file);
+ else if (fz_is_tar_archive(ctx, file))
+ arch = fz_open_tar_archive_with_stream(ctx, file);
else
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot recognize archive");
}
diff --git a/source/fitz/untar.c b/source/fitz/untar.c
new file mode 100644
index 00000000..a364bf39
--- /dev/null
+++ b/source/fitz/untar.c
@@ -0,0 +1,235 @@
+#include "mupdf/fitz.h"
+
+typedef struct tar_entry_s tar_entry;
+typedef struct fz_tar_archive_s fz_tar_archive;
+
+struct tar_entry_s
+{
+ char *name;
+ int offset, size;
+};
+
+struct fz_tar_archive_s
+{
+ fz_archive super;
+
+ int count;
+ tar_entry *entries;
+};
+
+static inline int isoctdigit(char c)
+{
+ return c >= '0' && c <= '7';
+}
+
+static inline int otoi(const char *s)
+{
+ int value = 0;
+
+ while (*s && isoctdigit(*s))
+ {
+ value *= 8;
+ value += (*s) - '0';
+ s++;
+ }
+
+ return value;
+}
+
+static void drop_tar_archive(fz_context *ctx, fz_archive *arch)
+{
+ fz_tar_archive *tar = (fz_tar_archive *) arch;
+ int i;
+ for (i = 0; i < tar->count; ++i)
+ fz_free(ctx, tar->entries[i].name);
+ fz_free(ctx, tar->entries);
+}
+
+static void ensure_tar_entries(fz_context *ctx, fz_tar_archive *tar)
+{
+ fz_stream *file = tar->super.file;
+ char name[100];
+ char octsize[12];
+ char typeflag;
+ int offset, blocks, size;
+ size_t n;
+
+ tar->count = 0;
+
+ fz_seek(ctx, file, 0, SEEK_SET);
+
+ while (1)
+ {
+ offset = fz_tell(ctx, file);
+ n = fz_read(ctx, file, (unsigned char *) name, nelem(name));
+ if (n < nelem(name))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of data in zip entry name");
+ name[nelem(name) - 1] = '\0';
+
+ if (strlen(name) == 0)
+ break;
+
+ fz_seek(ctx, file, 24, 1);
+ n = fz_read(ctx, file, (unsigned char *) octsize, nelem(octsize));
+ if (n < nelem(octsize))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of data in zip entry size");
+ size = otoi(octsize);
+
+ fz_seek(ctx, file, 20, 1);
+ typeflag = fz_read_byte(ctx, file);
+
+ fz_seek(ctx, file, 355, 1);
+ blocks = (size + 511) / 512;
+ fz_seek(ctx, file, blocks * 512, 1);
+
+ if (typeflag != '0')
+ continue;
+
+ tar->entries = fz_resize_array(ctx, tar->entries, tar->count + 1, sizeof *tar->entries);
+
+ tar->entries[tar->count].name = fz_strdup(ctx, name);
+ tar->entries[tar->count].offset = offset;
+ tar->entries[tar->count].size = size;
+
+ tar->count++;
+ }
+}
+
+static tar_entry *lookup_tar_entry(fz_context *ctx, fz_tar_archive *tar, const char *name)
+{
+ int i;
+ for (i = 0; i < tar->count; i++)
+ if (!fz_strcasecmp(name, tar->entries[i].name))
+ return &tar->entries[i];
+ return NULL;
+}
+
+static fz_stream *open_tar_entry(fz_context *ctx, fz_archive *arch, const char *name)
+{
+ fz_tar_archive *tar = (fz_tar_archive *) arch;
+ fz_stream *file = tar->super.file;
+ tar_entry *ent;
+
+ ent = lookup_tar_entry(ctx, tar, name);
+ if (!ent)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find named tar archive entry");
+
+ fz_seek(ctx, file, ent->offset + 512, 0);
+ return fz_open_null(ctx, file, ent->size, fz_tell(ctx, file));
+}
+
+static fz_buffer *read_tar_entry(fz_context *ctx, fz_archive *arch, const char *name)
+{
+ fz_tar_archive *tar = (fz_tar_archive *) arch;
+ fz_stream *file = tar->super.file;
+ fz_buffer *ubuf;
+ tar_entry *ent;
+
+ ent = lookup_tar_entry(ctx, tar, name);
+ if (!ent)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find named tar archive entry");
+
+ ubuf = fz_new_buffer(ctx, ent->size);
+
+ fz_try(ctx)
+ {
+ fz_seek(ctx, file, ent->offset + 512, 0);
+ ubuf->len = fz_read(ctx, file, ubuf->data, ent->size);
+ if (ubuf->len != ent->size)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot read entire archive entry");
+ }
+ fz_catch(ctx)
+ {
+ fz_drop_buffer(ctx, ubuf);
+ fz_rethrow(ctx);
+ }
+
+ return ubuf;
+}
+
+static int has_tar_entry(fz_context *ctx, fz_archive *arch, const char *name)
+{
+ fz_tar_archive *tar = (fz_tar_archive *) arch;
+ tar_entry *ent = lookup_tar_entry(ctx, tar, name);
+ return ent != NULL;
+}
+
+static const char *list_tar_entry(fz_context *ctx, fz_archive *arch, int idx)
+{
+ fz_tar_archive *tar = (fz_tar_archive *) arch;
+ if (idx < 0 || idx >= tar->count)
+ return NULL;
+ return tar->entries[idx].name;
+}
+
+static int count_tar_entries(fz_context *ctx, fz_archive *arch)
+{
+ fz_tar_archive *tar = (fz_tar_archive *) arch;
+ return tar->count;
+}
+
+int
+fz_is_tar_archive(fz_context *ctx, fz_stream *file)
+{
+ const unsigned char signature[6] = { 'u', 's', 't', 'a', 'r', ' ' };
+ unsigned char data[6];
+ size_t n;
+
+ fz_seek(ctx, file, 257, 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_tar_archive_with_stream(fz_context *ctx, fz_stream *file)
+{
+ fz_tar_archive *tar;
+
+ if (!fz_is_tar_archive(ctx, file))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot recognize tar archive");
+
+ tar = fz_new_archive(ctx, file, fz_tar_archive);
+
+ fz_try(ctx)
+ {
+ tar->super.format = "tar";
+ tar->super.count_entries = count_tar_entries;
+ tar->super.list_entry = list_tar_entry;
+ tar->super.has_entry = has_tar_entry;
+ tar->super.read_entry = read_tar_entry;
+ tar->super.open_entry = open_tar_entry;
+ tar->super.drop_archive = drop_tar_archive;
+
+ ensure_tar_entries(ctx, tar);
+ }
+ fz_catch(ctx)
+ {
+ fz_drop_archive(ctx, &tar->super);
+ fz_rethrow(ctx);
+ }
+
+ return &tar->super;
+}
+
+fz_archive *
+fz_open_tar_archive(fz_context *ctx, const char *filename)
+{
+ fz_archive *tar = NULL;
+ fz_stream *file;
+
+ file = fz_open_file(ctx, filename);
+
+ fz_try(ctx)
+ tar = fz_open_tar_archive_with_stream(ctx, file);
+ fz_always(ctx)
+ fz_drop_stream(ctx, file);
+ fz_catch(ctx)
+ fz_rethrow(ctx);
+
+ return tar;
+}