diff options
-rw-r--r-- | Jamfile | 2 | ||||
-rw-r--r-- | apps/samshow.c | 48 | ||||
-rw-r--r-- | fitz/base_memory.c | 10 | ||||
-rw-r--r-- | include/fitz/base.h | 2 | ||||
-rw-r--r-- | include/samus.h | 1 | ||||
-rw-r--r-- | include/samus/misc.h | 6 | ||||
-rw-r--r-- | include/samus/pack.h | 21 | ||||
-rw-r--r-- | samus/sa_misc.c | 29 | ||||
-rw-r--r-- | samus/sa_pack.c | 561 | ||||
-rw-r--r-- | samus/sa_xml.c | 9 | ||||
-rw-r--r-- | samus/sa_zip.c | 4 |
11 files changed, 502 insertions, 191 deletions
@@ -271,9 +271,11 @@ Library libmupdf : SubDir TOP samus ; Library libsamus : + sa_misc.c sa_zip.c sa_xml.c sa_tiff.c + sa_pack.c ; # -------------------------------------------------------------------------- diff --git a/apps/samshow.c b/apps/samshow.c index d62e8651..c8351dc1 100644 --- a/apps/samshow.c +++ b/apps/samshow.c @@ -1,6 +1,52 @@ #include "fitz.h" #include "samus.h" +int runpack(int argc, char **argv) +{ + fz_error *error; + sa_package *pack; + sa_relation *rels; + char *s; + int i; + + error = sa_openpackage(&pack, argv[1]); + if (error) + fz_abort(error); + + sa_debugpackage(pack); + + printf("--- root ---\n"); + error = sa_loadrelations(&rels, pack, "/"); + if (error) + fz_abort(error); + sa_debugrelations(rels); + sa_droprelations(rels); + printf("\n"); + + for (i = 2; i < argc; i++) + { + printf("--- %s ---\n", argv[i]); + + s = sa_typepart(pack, argv[i]); + if (!s) + printf("has no type!\n"); + else + printf("type %s\n", s); + + error = sa_loadrelations(&rels, pack, argv[i]); + if (error) + fz_abort(error); + sa_debugrelations(rels); + sa_droprelations(rels); + + printf("\n"); + } + + sa_closepackage(pack); + + return 0; +} + int runzip(int argc, char **argv) { fz_error *error; @@ -91,10 +137,12 @@ int main(int argc, char **argv) return runxml(argc, argv); if (strstr(argv[1], "tif")) return runtiff(argc, argv); + return runpack(argc, argv); } fprintf(stderr, "usage: samshow <file>\n"); fprintf(stderr, "usage: samshow <zipfile> <partname>\n"); + fprintf(stderr, "usage: samshow <package> <partname>\n"); return 1; } diff --git a/fitz/base_memory.c b/fitz/base_memory.c index 2c2f8e0d..48988dbc 100644 --- a/fitz/base_memory.c +++ b/fitz/base_memory.c @@ -79,3 +79,13 @@ fz_free(void *p) mem->free(mem, p); } +char * +fz_strdup(char *s) +{ + int len = strlen(s); + char *ns = fz_malloc(len + 1); + if (ns) + strcpy(ns, s); + return ns; +} + diff --git a/include/fitz/base.h b/include/fitz/base.h index d6b35592..e52e3c1d 100644 --- a/include/fitz/base.h +++ b/include/fitz/base.h @@ -74,3 +74,5 @@ void *fz_malloc(int n); void *fz_realloc(void *p, int n); void fz_free(void *p); +char *fz_strdup(char *s); + diff --git a/include/samus.h b/include/samus.h index dd5e4656..8837267b 100644 --- a/include/samus.h +++ b/include/samus.h @@ -7,6 +7,7 @@ #error "fitz.h must be included before samus.h" #endif +#include "samus/misc.h" #include "samus/zip.h" #include "samus/xml.h" #include "samus/pack.h" diff --git a/include/samus/misc.h b/include/samus/misc.h new file mode 100644 index 00000000..db4bfc78 --- /dev/null +++ b/include/samus/misc.h @@ -0,0 +1,6 @@ +/* + * Misc utility functions that Samus needs. + */ + +int sa_strcmp(char *s0, char *s1); + diff --git a/include/samus/pack.h b/include/samus/pack.h index bba55d2f..cdacc217 100644 --- a/include/samus/pack.h +++ b/include/samus/pack.h @@ -3,9 +3,26 @@ */ typedef struct sa_package_s sa_package; +typedef struct sa_relation_s sa_relation; + +struct sa_relation_s +{ + int external; + char *target; + char *id; + char *type; + sa_relation *next; +}; fz_error *sa_openpackage(sa_package **packp, char *filename); -char *sa_accesspart(sa_package *pack, char *partname); -fz_error *sa_openpart(fz_stream **filep, sa_package *pack, char *partname); +void sa_debugpackage(sa_package *pack); void sa_closepackage(sa_package *pack); +fz_error *sa_openpart(fz_stream **stmp, sa_package *pack, char *partname); + +char *sa_typepart(sa_package *pack, char *partname); + +fz_error *sa_loadrelations(sa_relation **relsp, sa_package *pack, char *partname); +void sa_debugrelations(sa_relation *rels); +void sa_droprelations(sa_relation *rels); + diff --git a/samus/sa_misc.c b/samus/sa_misc.c new file mode 100644 index 00000000..109e3693 --- /dev/null +++ b/samus/sa_misc.c @@ -0,0 +1,29 @@ +#include "fitz.h" +#include "samus.h" + +/* + * Test part names for equivalence. + * + * What we *should* do here (according to the spec) is... + * - Convert part name to a Unicode string by un-escaping UTF-8 octets. + * - Convert this to upper case. + * - Normalize to NFC. + * + * But all we do is a case insensitive ASCII string comparison. + */ + +static inline int toupper(int c) +{ + if (c >= 'a' && c <= 'z') + return c + 'A' - 'a'; + return c; +} + +int sa_strcmp(char *a, char *b) +{ + while (toupper(*a) == toupper(*b++)) + if (*a++ == 0) + return 0; + return toupper(*a) - toupper(*(b-1)); +} + diff --git a/samus/sa_pack.c b/samus/sa_pack.c index e1f910ba..74640ebd 100644 --- a/samus/sa_pack.c +++ b/samus/sa_pack.c @@ -1,187 +1,374 @@ -/*
- * Metro physical packages and parts, mapped to a zip archive.
- */
-
-#include "fitz.h"
-#include "samus.h"
-
-struct sa_package_s
-{
- sa_zip *zip;
- fz_obj *defaults;
- fz_obj *overrides;
-};
-
-static fz_error *
-readcontenttypes(sa_package *pack)
-{
- fz_error *error;
- sa_xmlparser *parser;
- sa_xmlitem *item;
- fz_obj *val;
-
- error = fz_newdict(&pack->defaults, 8);
- if (error)
- return error;
-
- error = fz_newdict(&pack->overrides, 8);
- if (error)
- return error;
-
- error = sa_openzipentry(pack->zip, "[Content_Types].xml");
- if (error)
- return error;
-
- error = sa_openxml(&parser, pack->zip->file, 0);
- if (error)
- goto cleanupzip;
-
- item = sa_xmlnext(parser);
- if (item && !strcmp(sa_xmlname(item), "Types"))
- {
- sa_xmldown(parser);
- item = sa_xmlnext(parser);
- while (item)
- {
- if (!strcmp(sa_xmlname(item), "Default"))
- {
- char *ext = sa_xmlatt(item, "Extension");
- char *type = sa_xmlatt(item, "ContentType");
- if (ext && type)
- {
- if (strstr(type, ';'))
- strstr(type, ';')[0] = 0;
- error = fz_newname(&val, type);
- if (error)
- goto cleanupxml;
- error = fz_dictputs(pack->defaults, ext, val);
- if (error)
- goto cleanupval;
- val = nil;
- }
- }
-
- if (!strcmp(sa_xmlname(item), "Override"))
- {
- char *name = sa_xmlatt(item, "PartName");
- char *type = sa_xmlatt(item, "ContentType");
- if (name && type)
- {
- if (strstr(type, ';'))
- strstr(type, ';')[0] = 0;
- error = fz_newname(&val, type);
- if (error)
- goto cleanupxml;
- error = fz_dictputs(pack->overrides, name, val);
- if (error)
- goto cleanupval;
- val = nil;
- }
- }
-
- item = sa_xmlnext(parser);
- }
- sa_xmlup(parser);
- }
-
- sa_closexml(parser);
- sa_closezipentry(pack->zip);
- return nil;
-
-cleanupval:
- fz_dropobj(val);
-cleanupxml:
- sa_closexml(parser);
-cleanupzip:
- sa_closezipentry(pack->zip);
- return error;
-}
-
-fz_error *
-sa_openpackage(sa_package **packp, char *filename)
-{
- fz_error *error;
- sa_package *pack;
-
- pack = fz_malloc(sizeof(sa_package));
- if (!pack)
- return fz_outofmem;
-
- pack->zip = nil;
- pack->defaults = nil;
- pack->overrides = nil;
-
- error = sa_openzip(&pack->zip, filename);
- if (error)
- {
- sa_closepackage(pack);
- return error;
- }
-
- error = readcontenttypes(pack->zip);
- if (error)
- {
- sa_closepackage(pack);
- return error;
- }
-
- *packp = pack;
- return nil;
-}
-
-void
-sa_closepackage(sa_package *pack)
-{
- if (pack->zip) sa_closezip(pack->zip);
- if (pack->defaults) fz_dropobj(pack->defaults);
- if (pack->overrides) fz_dropobj(pack->overrides);
- fz_free(pack);
-}
-
-/*
- * Check access of a part, return either nil or its mime-type.
- */
-char *
-sa_accesspart(sa_package *pack, char *partname)
-{
- fz_obj *type;
- char *ext;
-
- if (sa_accesszipentry(pack->zip, partname))
- {
- type = fz_dictgets(pack->overrides, partname);
- if (type)
- return fz_toname(type);
-
- ext = strrstr(partname, ".");
- if (ext)
- {
- type = fz_dictgets(pack->defaults, ext + 1);
- if (type)
- return fz_toname(type);
- }
- }
-
- return nil;
-}
-
-/*
- * Open a part for reading. It is NOT safe to open more than one
- * part at a time.
- */
-fz_error *
-sa_openpart(fz_file **filep, sa_package *pack, char *partname)
-{
- *filep = pack->zip->file;
- return sa_openzipentry(pack->zip, partname);
-}
-
-/*
- * Call this instead of fz_closefile()
- * FIXME i gotto do something about this icky file API
- */
-void sa_closepart(sa_package *pack, fz_file *file)
-{
- sa_closezipentry(pack->zip);
-}
-
+/* + * Metro physical packages and parts, mapped to a zip archive. + */ + +#include "fitz.h" +#include "samus.h" + +typedef struct sa_default_s sa_default; +typedef struct sa_override_s sa_override; + +struct sa_package_s +{ + sa_zip *zip; + sa_default *defaults; + sa_override *overrides; +}; + +/* + * Load the [Content_Types].xml data structures + * that define content types for the parts in the package. + */ + +struct sa_default_s +{ + char *extension; + char *mimetype; + sa_default *next; +}; + +struct sa_override_s +{ + char *partname; + char *mimetype; + sa_override *next; +}; + +static fz_error * +readcontenttypes(sa_package *pack) +{ + fz_error *error; + fz_stream *zipstm; + sa_xmlparser *parser; + sa_xmlitem *item; + + error = sa_openzipentry(&zipstm, pack->zip, "[Content_Types].xml"); + if (error) + return error; + + error = sa_openxml(&parser, zipstm, 0); + if (error) + goto cleanupzip; + + item = sa_xmlnext(parser); + while (item) + { + if (!strcmp(sa_xmlname(item), "Types")) + { + sa_xmldown(parser); + item = sa_xmlnext(parser); + while (item) + { + if (!strcmp(sa_xmlname(item), "Default")) + { + char *ext = sa_xmlatt(item, "Extension"); + char *type = sa_xmlatt(item, "ContentType"); + if (ext && type) + { + sa_default *newdef; + if (strchr(type, ';')) + strchr(type, ';')[0] = 0; + newdef = fz_malloc(sizeof(sa_default)); + if (!newdef) { error = fz_outofmem; goto cleanupxml; } + newdef->extension = fz_strdup(ext); + newdef->mimetype = fz_strdup(type); + newdef->next = pack->defaults; + pack->defaults = newdef; + } + } + + if (!strcmp(sa_xmlname(item), "Override")) + { + char *name = sa_xmlatt(item, "PartName"); + char *type = sa_xmlatt(item, "ContentType"); + if (name && type) + { + sa_override *newovr; + if (strchr(type, ';')) + strchr(type, ';')[0] = 0; + newovr = fz_malloc(sizeof(sa_override)); + if (!newovr) { error = fz_outofmem; goto cleanupxml; } + newovr->partname = fz_strdup(name); + newovr->mimetype = fz_strdup(type); + newovr->next = pack->overrides; + pack->overrides = newovr; + } + } + + item = sa_xmlnext(parser); + } + sa_xmlup(parser); + } + } + + sa_closexml(parser); + fz_dropstream(zipstm); + return nil; + +cleanupxml: + sa_closexml(parser); +cleanupzip: + fz_dropstream(zipstm); + return error; +} + +/* + * Return the type of a part, or nil if it doenst exist or has no type. + */ +char * +sa_typepart(sa_package *pack, char *partname) +{ + sa_default *def; + sa_override *ovr; + char *ext; + + if (partname[0] != '/') + return nil; + + if (sa_accesszipentry(pack->zip, partname + 1)) + { + for (ovr = pack->overrides; ovr; ovr = ovr->next) + if (!sa_strcmp(partname, ovr->partname)) + return ovr->mimetype; + + ext = strrchr(partname, '.'); + if (ext) + { + for (def = pack->defaults; def; def = def->next) + if (!sa_strcmp(ext + 1, def->extension)) + return def->mimetype; + } + } + + return nil; +} + +/* + * Open a package... + * Open the ZIP file. + * Load the content types. + * Load the relations for the root. + */ +fz_error * +sa_openpackage(sa_package **packp, char *filename) +{ + fz_error *error; + sa_package *pack; + + pack = fz_malloc(sizeof(sa_package)); + if (!pack) + return fz_outofmem; + + pack->zip = nil; + pack->defaults = nil; + pack->overrides = nil; + + error = sa_openzip(&pack->zip, filename); + if (error) + { + sa_closepackage(pack); + return error; + } + + error = readcontenttypes(pack); + if (error) + { + sa_closepackage(pack); + return error; + } + + *packp = pack; + return nil; +} + +void +sa_closepackage(sa_package *pack) +{ + sa_default *def, *ndef; + sa_override *ovr, *novr; + + if (pack->zip) + sa_closezip(pack->zip); + + for (def = pack->defaults; def; def = ndef) + { + ndef = def->next; + fz_free(def->extension); + fz_free(def->mimetype); + fz_free(def); + } + + for (ovr = pack->overrides; ovr; ovr = novr) + { + novr = ovr->next; + fz_free(ovr->partname); + fz_free(ovr->mimetype); + fz_free(ovr); + } + + fz_free(pack); +} + +void +sa_debugpackage(sa_package *pack) +{ + sa_default *def; + sa_override *ovr; + + printf("package {\n"); + + if (pack->zip) + sa_debugzip(pack->zip); + + for (def = pack->defaults; def; def = def->next) + printf("default %s %s ;\n", def->extension, def->mimetype); + + for (ovr = pack->overrides; ovr; ovr = ovr->next) + printf("override %s %s ;\n", ovr->partname, ovr->mimetype); + + printf("}\n"); +} + +/* + * Open a part for reading. + * It is NOT safe to open more than one part at a time. + */ +fz_error * +sa_openpart(fz_stream **stmp, sa_package *pack, char *partname) +{ + if (partname[0] != '/') + return fz_throw("ioerror: invalid part name: %s", partname); + return sa_openzipentry(stmp, pack->zip, partname + 1); +} + +/* + * Load a linked list of all the relations of a part. + * This is contained in <folder>/_rels/<file>.rels + */ +fz_error * +sa_loadrelations(sa_relation **relsp, sa_package *pack, char *partname) +{ + fz_error *error; + fz_stream *zipstm; + sa_xmlparser *parser; + sa_xmlitem *item; + sa_relation *rels; + int len; + char *sep; + char *relsname; + + if (partname[0] != '/') + return fz_throw("ioerror: invalid part name: %s", partname); + + sep = strrchr(partname, '/'); + if (!sep) + return fz_throw("ioerror: invalid part name: %s", partname); + + len = strlen(partname) + 11 + 1; + relsname = fz_malloc(len); + if (!relsname) + return fz_outofmem; + + memcpy(relsname, partname, sep - partname + 1); + relsname[sep - partname + 1] = 0; + strcat(relsname, "_rels/"); + strcat(relsname, sep + 1); + strcat(relsname, ".rels"); + + rels = nil; + + if (!sa_accesszipentry(pack->zip, relsname + 1)) + { + fz_free(relsname); + *relsp = nil; + return nil; + } + + error = sa_openzipentry(&zipstm, pack->zip, relsname + 1); + if (error) + goto cleanupname; + + error = sa_openxml(&parser, zipstm, 0); + if (error) + goto cleanupzip; + + item = sa_xmlnext(parser); + while (item) + { + if (!strcmp(sa_xmlname(item), "Relationships")) + { + sa_xmldown(parser); + item = sa_xmlnext(parser); + while (item) + { + if (!strcmp(sa_xmlname(item), "Relationship")) + { + char *mode = sa_xmlatt(item, "TargetMode"); + char *id = sa_xmlatt(item, "Id"); + char *target = sa_xmlatt(item, "Target"); + char *type = sa_xmlatt(item, "Type"); + + if (!mode) + mode = "Internal"; + + if (id && target && type) + { + sa_relation *newrel; + newrel = fz_malloc(sizeof(sa_relation)); + if (!newrel) { error = fz_outofmem; goto cleanupxml; } + newrel->external = !strcmp(mode, "External"); + newrel->id = fz_strdup(id); + newrel->target = fz_strdup(target); + newrel->type = fz_strdup(type); + newrel->next = rels; + rels = newrel; + } + } + + item = sa_xmlnext(parser); + } + sa_xmlup(parser); + } + } + + sa_closexml(parser); + fz_dropstream(zipstm); + fz_free(relsname); + *relsp = rels; + return nil; + +cleanupxml: + sa_closexml(parser); +cleanupzip: + fz_dropstream(zipstm); +cleanupname: + fz_free(relsname); + return error; +} + +void +sa_debugrelations(sa_relation *rel) +{ + while (rel) + { + printf("relation %s\n", rel->type); + printf(" %s%s\n", rel->external ? "external " : "", rel->target); + rel = rel->next; + } +} + +void +sa_droprelations(sa_relation *rel) +{ + sa_relation *nrel; + while (rel) + { + nrel = rel->next; + fz_free(rel->target); + fz_free(rel->id); + fz_free(rel->type); + fz_free(rel); + rel = nrel; + } +} + diff --git a/samus/sa_xml.c b/samus/sa_xml.c index 4b9ac07a..065fa765 100644 --- a/samus/sa_xml.c +++ b/samus/sa_xml.c @@ -19,6 +19,7 @@ struct sa_xmlparser_s fz_error *error; sa_xmlitem *root; sa_xmlitem *head; + int nexted; int downed; }; @@ -172,6 +173,7 @@ sa_openxml(sa_xmlparser **spp, fz_stream *file, int ns) sp->root = nil; sp->head = nil; sp->downed = 0; + sp->nexted = 0; if (ns) xp = XML_ParserCreateNS(nil, ns); @@ -290,6 +292,12 @@ sa_xmlnext(sa_xmlparser *sp) return sp->head; } + if (!sp->nexted) + { + sp->nexted = 1; + return sp->head; + } + if (sp->head->next) { sp->head = sp->head->next; @@ -306,6 +314,7 @@ sa_xmldown(sa_xmlparser *sp) sp->head = sp->head->down; else sp->downed ++; + sp->nexted = 0; } void diff --git a/samus/sa_zip.c b/samus/sa_zip.c index 865d6129..c00f6f06 100644 --- a/samus/sa_zip.c +++ b/samus/sa_zip.c @@ -221,7 +221,7 @@ sa_accesszipentry(sa_zip *zip, char *name) { int i; for (i = 0; i < zip->len; i++) - if (!strcmp(name, zip->table[i].name)) + if (!sa_strcmp(name, zip->table[i].name)) return 1; return 0; } @@ -305,7 +305,7 @@ sa_openzipentry(fz_stream **stmp, sa_zip *zip, char *name) int i; for (i = 0; i < zip->len; i++) - if (!strcmp(name, zip->table[i].name)) + if (!sa_strcmp(name, zip->table[i].name)) return reallyopenzipentry(stmp, zip, i); return fz_throw("ioerror: file not found in zip: '%s'", name); |