diff options
-rw-r--r-- | include/mupdf/fitz/archive.h | 35 | ||||
-rw-r--r-- | source/cbz/mucbz.c | 6 | ||||
-rw-r--r-- | source/fitz/archive.c | 2 | ||||
-rw-r--r-- | source/fitz/untar.c | 235 |
4 files changed, 276 insertions, 2 deletions
diff --git a/include/mupdf/fitz/archive.h b/include/mupdf/fitz/archive.h index 89f17b02..b4a62401 100644 --- a/include/mupdf/fitz/archive.h +++ b/include/mupdf/fitz/archive.h @@ -28,7 +28,7 @@ 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 + fz_open_archive: Open a zip or tar archive Open a file and identify its archive type based on the archive signature contained inside. @@ -38,7 +38,7 @@ fz_archive *fz_new_archive_of_size(fz_context *ctx, fz_stream *file, int size); fz_archive *fz_open_archive(fz_context *ctx, const char *filename); /* - fz_open_archive_with_stream: Open zip archive stream. + fz_open_archive_with_stream: Open zip or tar archive stream. Open an archive using a seekable stream object rather than opening a file or directory on disk. @@ -115,6 +115,37 @@ fz_stream *fz_open_archive_entry(fz_context *ctx, fz_archive *arch, const char * */ fz_buffer *fz_read_archive_entry(fz_context *ctx, fz_archive *arch, const char *name); + +/* + fz_is_tar_archive: Detect if stream object is a tar achive. + + Assumes that the stream object is seekable. +*/ +int fz_is_tar_archive(fz_context *ctx, fz_stream *file); + +/* + fz_open_tar_archive: Open a tar archive file. + + An exception is throw if the file is not a tar archive as + indicated by the presence of a tar signature. + + filename: a path to a tar archive file as it would be given to + open(2). +*/ +fz_archive *fz_open_tar_archive(fz_context *ctx, const char *filename); + +/* + fz_open_tar_archive: Open a tar 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 tar archive as + indicated by the presence of a tar signature. + +*/ +fz_archive *fz_open_tar_archive_with_stream(fz_context *ctx, fz_stream *file); + /* fz_is_zip_archive: Detect if stream object is a zip achive. 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; +} |