summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jamfile2
-rw-r--r--apps/samshow.c48
-rw-r--r--fitz/base_memory.c10
-rw-r--r--include/fitz/base.h2
-rw-r--r--include/samus.h1
-rw-r--r--include/samus/misc.h6
-rw-r--r--include/samus/pack.h21
-rw-r--r--samus/sa_misc.c29
-rw-r--r--samus/sa_pack.c561
-rw-r--r--samus/sa_xml.c9
-rw-r--r--samus/sa_zip.c4
11 files changed, 502 insertions, 191 deletions
diff --git a/Jamfile b/Jamfile
index 7a2a613e..1cb9ae3b 100644
--- a/Jamfile
+++ b/Jamfile
@@ -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);