1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * gsf-open-pkg-utils.c: Utilities for handling Open Package zip files
4 * from MS Office 2007 or XPS.
6 * Copyright (C) 2006-2007 Jody Goldberg (jody@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of version 2.1 of the GNU Lesser General Public
10 * License as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 #include <gsf-config.h>
24 #include <gsf/gsf-open-pkg-utils.h>
25 #include <gsf/gsf-input.h>
26 #include <gsf/gsf-infile.h>
27 #include <gsf/gsf-outfile-impl.h>
28 #include <gsf/gsf-impl-utils.h>
30 #include <glib/gi18n-lib.h>
34 #define G_LOG_DOMAIN "libgsf:open_pkg"
40 static GsfXMLInNS const open_pkg_ns[] = {
41 GSF_XML_IN_NS (OPEN_PKG_NS_REL, "http://schemas.openxmlformats.org/package/2006/relationships"),
45 struct _GsfOpenPkgRel {
46 char *id, *type, *target;
50 struct _GsfOpenPkgRels {
56 gsf_open_pkg_rels_free (GsfOpenPkgRels *rels)
58 g_hash_table_destroy (rels->by_id);
59 g_hash_table_destroy (rels->by_type);
64 gsf_open_pkg_rel_free (GsfOpenPkgRel *rel)
66 g_free (rel->id); rel->id = NULL;
67 g_free (rel->type); rel->type = NULL;
68 g_free (rel->target); rel->target = NULL;
73 open_pkg_rel_begin (GsfXMLIn *xin, xmlChar const **attrs)
75 GsfOpenPkgRels *rels = xin->user_state;
77 xmlChar const *id = NULL;
78 xmlChar const *type = NULL;
79 xmlChar const *target = NULL;
80 gboolean is_extern = FALSE;
82 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
83 if (0 == strcmp (attrs[0], "Id"))
85 else if (0 == strcmp (attrs[0], "Type"))
87 else if (0 == strcmp (attrs[0], "Target"))
89 else if (0 == strcmp (attrs[0], "TargetMode"))
90 is_extern = 0 == strcmp (attrs[1], "External");
92 g_return_if_fail (id != NULL);
93 g_return_if_fail (type != NULL);
94 g_return_if_fail (target != NULL);
96 rel = g_new0 (GsfOpenPkgRel, 1);
97 rel->id = g_strdup (id);
98 rel->type = g_strdup (type);
99 rel->target = g_strdup (target);
100 rel->is_extern = is_extern;
102 g_hash_table_replace (rels->by_id, rel->id, rel);
103 g_hash_table_replace (rels->by_type, rel->type, rel);
106 static GsfXMLInNode const open_pkg_rel_dtd[] = {
107 GSF_XML_IN_NODE_FULL (START, START, -1, NULL, GSF_XML_NO_CONTENT, FALSE, TRUE, NULL, NULL, 0),
108 GSF_XML_IN_NODE_FULL (START, RELS, OPEN_PKG_NS_REL, "Relationships", GSF_XML_NO_CONTENT, FALSE, TRUE, NULL, NULL, 0),
109 GSF_XML_IN_NODE (RELS, REL, OPEN_PKG_NS_REL, "Relationship", GSF_XML_NO_CONTENT, open_pkg_rel_begin, NULL),
115 * gsf_open_pkg_rel_is_extern :
116 * @rel : #GsfOpenPkgRel
118 * Returns: %TRUE if @rel has mode 'External'
121 gsf_open_pkg_rel_is_extern (GsfOpenPkgRel const *rel)
123 g_return_val_if_fail (rel != NULL, FALSE);
124 return rel->is_extern;
128 * gsf_open_pkg_rel_get_target :
129 * @rel : #GsfOpenPkgRel
131 * Returns: const pointer to @rel's target.
134 gsf_open_pkg_rel_get_target (GsfOpenPkgRel const *rel)
136 g_return_val_if_fail (rel != NULL, NULL);
141 * gsf_open_pkg_rel_get_type :
142 * @rel : #GsfOpenPkgRel
144 * Returns: const pointer to @rel's type.
147 gsf_open_pkg_rel_get_type (GsfOpenPkgRel const *rel)
149 g_return_val_if_fail (rel != NULL, NULL);
153 static GsfOpenPkgRels *
154 gsf_open_pkg_get_rels (GsfInput *opkg)
156 GsfOpenPkgRels *rels = NULL;
158 g_return_val_if_fail (opkg != NULL, NULL);
160 if (NULL == (rels = g_object_get_data (G_OBJECT (opkg), "OpenPkgRels"))) {
161 char const *part_name = gsf_input_name (opkg);
162 GsfXMLInDoc *rel_doc;
163 GsfInput *rel_stream;
165 if (NULL != part_name) {
166 GsfInfile *container = gsf_input_container (opkg);
169 g_return_val_if_fail (container != NULL, NULL);
171 rel_name = g_strconcat (part_name, ".rels", NULL);
172 rel_stream = gsf_infile_child_by_vname (container, "_rels", rel_name, NULL);
174 } else /* the root */
175 rel_stream = gsf_infile_child_by_vname (GSF_INFILE (opkg), "_rels", ".rels", NULL);
177 if (NULL != rel_stream) {
178 rels = g_new (GsfOpenPkgRels, 1);
179 rels->by_id = g_hash_table_new_full (g_str_hash, g_str_equal,
180 NULL, (GDestroyNotify)gsf_open_pkg_rel_free);
181 rels->by_type = g_hash_table_new (g_str_hash, g_str_equal);
183 rel_doc = gsf_xml_in_doc_new (open_pkg_rel_dtd, open_pkg_ns);
184 (void) gsf_xml_in_doc_parse (rel_doc, rel_stream, rels);
186 gsf_xml_in_doc_free (rel_doc);
187 g_object_unref (G_OBJECT (rel_stream));
190 g_object_set_data_full (G_OBJECT (opkg), "OpenPkgRels", rels,
191 (GDestroyNotify) gsf_open_pkg_rels_free);
198 * gsf_open_pkg_open_rel :
200 * @rel : #GsfOpenPkgRel
203 * Returns: a new #GsfInput which the called needs to unref, or %NULL and sets @err
206 gsf_open_pkg_open_rel (GsfInput *opkg, GsfOpenPkgRel const *rel,
207 G_GNUC_UNUSED GError **err /* just in case we need it one day */ )
209 GsfInput *res = NULL;
210 GsfInfile *parent, *prev_parent;
214 g_return_val_if_fail (rel != NULL, NULL);
215 g_return_val_if_fail (opkg != NULL, NULL);
217 /* References from the root use children of opkg
218 * References from a child are relative to siblings of opkg */
219 parent = gsf_input_name (opkg)
220 ? gsf_input_container (opkg) : GSF_INFILE (opkg);
221 g_object_ref (parent);
223 elems = g_strsplit (rel->target, "/", 0);
224 for (i = 0 ; elems[i] && NULL != parent ; i++) {
225 if (0 == strcmp (elems[i], ".") || '\0' == *elems[i])
226 continue; /* ignore '.' and empty */
228 prev_parent = parent;
229 if (0 == strcmp (elems[i], "..")) {
230 parent = gsf_input_container (GSF_INPUT (parent));
231 res = NULL; /* only return newly created children */
232 if (NULL != parent) {
233 /* check for attempt to gain access outside the zip file */
234 if (G_OBJECT_TYPE (parent) == G_OBJECT_TYPE (prev_parent))
235 g_object_ref (G_OBJECT (parent));
240 res = gsf_infile_child_by_name (parent, elems[i]);
241 if (NULL != elems[i+1]) {
242 g_return_val_if_fail (GSF_IS_INFILE (res), NULL);
243 parent = GSF_INFILE (res);
246 g_object_unref (G_OBJECT (prev_parent));
254 * gsf_open_pkg_lookup_rel_by_type :
260 * Finds _a_ relation of @opkg with @type (no order is guaranteed)
262 * Returns: A #GsfOpenPkgRel or %NULL
265 gsf_open_pkg_lookup_rel_by_type (GsfInput *opkg, char const *type)
267 GsfOpenPkgRels *rels = gsf_open_pkg_get_rels (opkg);
268 return rels ? g_hash_table_lookup (rels->by_type, type) : NULL;
272 * gsf_open_pkg_open_rel_by_id :
278 * Finds @opkg's relation with @id
280 * Returns: A #GsfOpenPkgRel or %NULL
283 gsf_open_pkg_lookup_rel_by_id (GsfInput *opkg, char const *id)
285 GsfOpenPkgRels *rels = gsf_open_pkg_get_rels (opkg);
286 return rels ? g_hash_table_lookup (rels->by_id, id) : NULL;
289 struct pkg_iter_data {
296 cb_foreach_rel (G_GNUC_UNUSED gpointer id,
298 struct pkg_iter_data *dat)
300 (*dat->func) (dat->opkg, rel, dat->user_data);
304 * gsf_open_pkg_foreach_rel:
306 * @func : #GsfOpenPkgIter
307 * @user_data : gpointer
311 * Walks each relationship associated with @opkg and calls @func with @user_data.
314 gsf_open_pkg_foreach_rel (GsfInput *opkg,
318 GsfOpenPkgRels *rels = gsf_open_pkg_get_rels (opkg);
319 struct pkg_iter_data dat;
324 dat.user_data = user_data;
325 g_hash_table_foreach (rels->by_id, (GHFunc)&cb_foreach_rel, &dat);
330 * gsf_open_pkg_open_rel_by_id :
333 * @err : optionally %NULL
337 * Open @opkg's relation @id
339 * Returns: A new GsfInput or %NULL, and sets @err if possible.
342 gsf_open_pkg_open_rel_by_id (GsfInput *opkg, char const *id, GError **err)
344 GsfOpenPkgRel *rel = NULL;
345 GsfOpenPkgRels *rels = gsf_open_pkg_get_rels (opkg);
347 if (NULL != rels && NULL != (rel = g_hash_table_lookup (rels->by_id, id)))
348 return gsf_open_pkg_open_rel (opkg, rel, err);
350 *err = g_error_new (gsf_input_error_id(), gsf_open_pkg_error_id (),
351 _("Unable to find part id='%s' for '%s'"),
352 id, gsf_input_name (opkg) );
357 * gsf_open_pkg_open_rel_by_type :
359 * @type : target type
360 * @err : optionally %NULL
364 * Open one of @opkg's relationships with type=@type.
366 * Returns: A new GsfInput or %NULL, and sets @err if possible.
369 gsf_open_pkg_open_rel_by_type (GsfInput *opkg, char const *type, GError **err)
371 GsfOpenPkgRel *rel = NULL;
372 GsfOpenPkgRels *rels = gsf_open_pkg_get_rels (opkg);
374 if (NULL != rels && NULL != (rel = g_hash_table_lookup (rels->by_type, type)))
375 return gsf_open_pkg_open_rel (opkg, rel, err);
378 *err = g_error_new (gsf_input_error_id(), gsf_open_pkg_error_id (),
379 _("Unable to find part with type='%s' for '%s'"),
380 type, gsf_input_name (opkg) );
385 * gsf_open_pkg_parse_rel_by_id :
388 * @dtd : #GsfXMLInNode
391 * Convenience function to parse a related part.
393 * Returns: NULL on success or a GError which callerss need to free on failure.
396 gsf_open_pkg_parse_rel_by_id (GsfXMLIn *xin, char const *id,
397 GsfXMLInNode const *dtd,
398 GsfXMLInNS const *ns)
401 GsfInput *cur_stream, *part_stream;
403 g_return_val_if_fail (xin != NULL, NULL);
405 cur_stream = gsf_xml_in_get_input (xin);
408 return g_error_new (gsf_input_error_id(), gsf_open_pkg_error_id (),
409 _("Missing id for part in '%s'"),
410 gsf_input_name (cur_stream) );
412 part_stream = gsf_open_pkg_open_rel_by_id (cur_stream, id, &res);
413 if (NULL != part_stream) {
414 GsfXMLInDoc *doc = gsf_xml_in_doc_new (dtd, ns);
416 if (!gsf_xml_in_doc_parse (doc, part_stream, xin->user_state))
417 res = g_error_new (gsf_input_error_id(), gsf_open_pkg_error_id (),
418 _("Part '%s' in '%s' from '%s' is corrupt!"),
420 gsf_input_name (part_stream),
421 gsf_input_name (cur_stream) );
422 gsf_xml_in_doc_free (doc);
424 g_object_unref (G_OBJECT (part_stream));
429 /* DEPRECATED in 1.14.6 */
430 GsfInput *gsf_open_pkg_get_rel_by_type (GsfInput *opkg, char const *type) { return gsf_open_pkg_open_rel_by_type (opkg, type, NULL); }
431 GsfInput *gsf_open_pkg_get_rel_by_id (GsfInput *opkg, char const *id) { return gsf_open_pkg_open_rel_by_id (opkg, id, NULL); }
433 /*************************************************************/
435 struct _GsfOutfileOpenPkg {
445 typedef GsfOutfileClass GsfOutfileOpenPkgClass;
455 static GObjectClass *parent_class;
458 gsf_outfile_open_pkg_get_property (GObject *object,
463 GsfOutfileOpenPkg *open_pkg = (GsfOutfileOpenPkg *)object;
465 switch (property_id) {
467 g_value_set_object (value, open_pkg->sink);
469 case PROP_CONTENT_TYPE:
470 g_value_set_string (value, open_pkg->content_type);
473 g_value_set_boolean (value, open_pkg->is_dir);
476 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
482 gsf_outfile_open_pkg_set_property (GObject *object,
487 GsfOutfileOpenPkg *open_pkg = (GsfOutfileOpenPkg *)object;
489 switch (property_id) {
491 gsf_outfile_open_pkg_set_sink (open_pkg, g_value_get_object (value));
493 case PROP_CONTENT_TYPE:
494 gsf_outfile_open_pkg_set_content_type (open_pkg, g_value_get_string (value));
497 open_pkg->is_dir = g_value_get_boolean (value);
500 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
506 gsf_outfile_open_pkg_init (GObject *obj)
508 GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (obj);
510 open_pkg->sink = NULL;
511 open_pkg->content_type = NULL;
512 open_pkg->is_dir = FALSE;
513 open_pkg->children = NULL;
514 open_pkg->relations = NULL;
518 gsf_outfile_open_pkg_finalize (GObject *obj)
520 GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (obj);
523 if (open_pkg->sink != NULL) {
524 g_object_unref (open_pkg->sink);
525 open_pkg->sink = NULL;
527 g_free (open_pkg->content_type);
528 open_pkg->content_type = NULL;
530 for (ptr = open_pkg->children ; ptr != NULL ; ptr = ptr->next)
531 g_object_unref (ptr->data);
532 g_slist_free (open_pkg->children);
533 parent_class->finalize (obj);
537 gsf_outfile_open_pkg_write (GsfOutput *output, size_t num_bytes, guint8 const *data)
539 GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (output);
540 return gsf_output_write (open_pkg->sink, num_bytes, data);
543 gsf_outfile_open_pkg_seek (GsfOutput *output, gsf_off_t offset, GSeekType whence)
545 GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (output);
546 return gsf_output_seek (open_pkg->sink, offset, whence);
549 gsf_outfile_open_pkg_new_child (GsfOutfile *parent,
550 char const *name, gboolean is_dir,
551 char const *first_property_name, va_list args)
553 GsfOutfileOpenPkg *child, *open_pkg = GSF_OUTFILE_OPEN_PKG (parent);
556 if (!open_pkg->is_dir)
559 child = (GsfOutfileOpenPkg *)g_object_new_valist (
560 GSF_OUTFILE_OPEN_PKG_TYPE, first_property_name, args);
561 gsf_output_set_name (GSF_OUTPUT (child), name);
562 gsf_output_set_container (GSF_OUTPUT (child), parent);
563 child->is_dir = is_dir;
565 sink = gsf_outfile_new_child (GSF_OUTFILE (open_pkg->sink), name, is_dir);
566 gsf_outfile_open_pkg_set_sink (child, sink);
567 g_object_unref (sink);
569 open_pkg->children = g_slist_prepend (open_pkg->children, child);
570 g_object_ref (child);
572 return GSF_OUTPUT (child);
576 gsf_open_pkg_write_content_default (GsfXMLOut *xml, char const *ext, char const *type)
578 gsf_xml_out_start_element (xml, "Default");
579 gsf_xml_out_add_cstr (xml, "Extension", ext);
580 gsf_xml_out_add_cstr (xml, "ContentType", type);
581 gsf_xml_out_end_element (xml); /* </Default> */
584 gsf_open_pkg_write_content_override (GsfOutfileOpenPkg const *open_pkg,
588 GsfOutfileOpenPkg const *child;
592 for (ptr = open_pkg->children ; ptr != NULL ; ptr = ptr->next) {
595 path = g_strconcat (base, gsf_output_name (GSF_OUTPUT (child)), "/", NULL);
596 gsf_open_pkg_write_content_override (child, path, xml);
598 path = g_strconcat (base, gsf_output_name (GSF_OUTPUT (child)), NULL);
599 /* rels files do need content types, the defaults handle them */
600 if (NULL != child->content_type) {
601 gsf_xml_out_start_element (xml, "Override");
602 gsf_xml_out_add_cstr (xml, "PartName", path);
603 gsf_xml_out_add_cstr (xml, "ContentType", child->content_type);
604 gsf_xml_out_end_element (xml); /* </Override> */
612 gsf_outfile_open_pkg_close (GsfOutput *output)
614 GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (output);
616 gboolean res = FALSE;
619 if (NULL == open_pkg->sink || gsf_output_is_closed (open_pkg->sink))
622 /* Generate [Content_types].xml when we close the root dir */
623 if (NULL == gsf_output_name (output)) {
624 GsfOutput *out = gsf_outfile_new_child (GSF_OUTFILE (open_pkg->sink),
625 "[Content_Types].xml", FALSE);
626 GsfXMLOut *xml = gsf_xml_out_new (out);
628 gsf_xml_out_start_element (xml, "Types");
629 gsf_xml_out_add_cstr_unchecked (xml, "xmlns",
630 "http://schemas.openxmlformats.org/package/2006/content-types");
631 gsf_open_pkg_write_content_default (xml, "rels",
632 "application/vnd.openxmlformats-package.relationships+xml");
633 gsf_open_pkg_write_content_default (xml, "xlbin",
634 "application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings");
635 gsf_open_pkg_write_content_default (xml, "xml",
637 gsf_open_pkg_write_content_override (open_pkg, "/", xml);
638 gsf_xml_out_end_element (xml); /* </Types> */
639 g_object_unref (xml);
641 gsf_output_close (out);
642 g_object_unref (out);
644 dir = open_pkg->sink;
645 rels_name = g_strdup (".rels");
647 res = gsf_output_close (open_pkg->sink);
649 dir = (GsfOutput *)gsf_output_container (open_pkg->sink);
650 rels_name = g_strconcat (gsf_output_name (output), ".rels", NULL);
653 if (NULL != open_pkg->relations) {
659 dir = gsf_outfile_new_child (GSF_OUTFILE (dir), "_rels", TRUE);
660 rels = gsf_outfile_new_child (GSF_OUTFILE (dir), rels_name, FALSE);
661 xml = gsf_xml_out_new (rels);
663 gsf_xml_out_start_element (xml, "Relationships");
664 gsf_xml_out_add_cstr_unchecked (xml, "xmlns",
665 "http://schemas.openxmlformats.org/package/2006/relationships");
667 for (ptr = open_pkg->relations ; ptr != NULL ; ptr = ptr->next) {
669 gsf_xml_out_start_element (xml, "Relationship");
670 gsf_xml_out_add_cstr (xml, "Id", rel->id);
671 gsf_xml_out_add_cstr (xml, "Type", rel->type);
672 gsf_xml_out_add_cstr (xml, "Target", rel->target);
674 gsf_xml_out_add_cstr_unchecked (xml, "TargetMode", "External");
675 gsf_xml_out_end_element (xml); /* </Relationship> */
679 g_free (rel->target);
682 g_slist_free (open_pkg->relations);
684 gsf_xml_out_end_element (xml); /* </Relationships> */
685 g_object_unref (xml);
686 gsf_output_close (rels);
687 g_object_unref (rels);
688 g_object_unref (dir);
692 /* close the container */
693 if (NULL == gsf_output_name (output))
694 return gsf_output_close (open_pkg->sink);
699 gsf_outfile_open_pkg_class_init (GObjectClass *gobject_class)
701 GsfOutputClass *output_class = GSF_OUTPUT_CLASS (gobject_class);
702 GsfOutfileClass *outfile_class = GSF_OUTFILE_CLASS (gobject_class);
704 gobject_class->finalize = gsf_outfile_open_pkg_finalize;
705 gobject_class->get_property = gsf_outfile_open_pkg_get_property;
706 gobject_class->set_property = gsf_outfile_open_pkg_set_property;
708 output_class->Write = gsf_outfile_open_pkg_write;
709 output_class->Seek = gsf_outfile_open_pkg_seek;
710 output_class->Close = gsf_outfile_open_pkg_close;
711 outfile_class->new_child = gsf_outfile_open_pkg_new_child;
713 parent_class = g_type_class_peek_parent (gobject_class);
715 g_object_class_install_property (gobject_class, PROP_SINK,
716 g_param_spec_object ("sink", "Sink", "The GsfOutput that stores the Open Package content.",
717 GSF_OUTFILE_TYPE, GSF_PARAM_STATIC | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
718 g_object_class_install_property (gobject_class, PROP_CONTENT_TYPE,
719 g_param_spec_string ("content-type", "ContentType", "The ContentType stored in the root [Content_Types].xml file.",
720 "", GSF_PARAM_STATIC | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
721 g_object_class_install_property (gobject_class, PROP_IS_DIR,
722 g_param_spec_boolean ("is-dir", "IsDir", "Can the outfile have children.",
723 FALSE, GSF_PARAM_STATIC | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
726 GSF_CLASS (GsfOutfileOpenPkg, gsf_outfile_open_pkg,
727 gsf_outfile_open_pkg_class_init, gsf_outfile_open_pkg_init,
731 * gsf_outfile_open_pkg_new :
732 * @sink : #GsfOutfile
734 * Convenience routine to create a GsfOutfileOpenPkg inside @sink.
736 * Returns: a GsfOutfile that the caller is responsible for.
739 gsf_outfile_open_pkg_new (GsfOutfile *sink)
741 return g_object_new (GSF_OUTFILE_OPEN_PKG_TYPE,
742 "sink", sink, "is-dir", TRUE,
747 * gsf_outfile_open_pkg_set_sink :
748 * @open_pkg : #GsfOutfileOpenPkg
751 * Assigns a GsfOutput (@sink) to store the package into.
754 gsf_outfile_open_pkg_set_sink (GsfOutfileOpenPkg *open_pkg, GsfOutput *sink)
759 g_object_unref (open_pkg->sink);
760 open_pkg->sink = sink;
764 * gsf_outfile_open_pkg_set_content_type :
765 * @open_pkg : #GsfOutfileOpenPkg
770 gsf_outfile_open_pkg_set_content_type (GsfOutfileOpenPkg *open_pkg,
771 char const *content_type)
773 g_return_if_fail (content_type != NULL);
775 if (open_pkg->content_type != content_type) {
776 g_free (open_pkg->content_type);
777 open_pkg->content_type = g_strdup (content_type);
782 gsf_outfile_open_pkg_create_rel (GsfOutfileOpenPkg *parent,
787 GsfOpenPkgRel *rel = g_new0 (GsfOpenPkgRel, 1);
788 rel->target = target;
789 rel->type = g_strdup (type);
790 rel->id = g_strdup_printf ("rId%u", g_slist_length (parent->relations) + 1);
791 rel->is_extern = is_extern;
792 parent->relations = g_slist_prepend (parent->relations, rel);
797 * gsf_outfile_open_pkg_relate:
798 * @child : #GsfOutfileOpenPkg
799 * @parent : #GsfOutfileOpenPkg
800 * @type : target type
802 * Create a relationship between @child and @parent of @type.
804 * Returns: the relID which the caller does not own but will live as long as
808 gsf_outfile_open_pkg_relate (GsfOutfileOpenPkg *child,
809 GsfOutfileOpenPkg *parent,
814 GsfOutfile *child_dir, *parent_dir;
816 /* Calculate the path from @child to @parent */
817 parent_dir = parent->is_dir ? GSF_OUTFILE (parent)
818 : gsf_output_container (GSF_OUTPUT (parent));
821 child_dir = GSF_OUTFILE (child);
822 while (NULL != (child_dir = gsf_output_container (GSF_OUTPUT (child_dir))))
823 if (child_dir == parent_dir)
824 goto found; /* break out of both loops */
825 } while (NULL != (parent_dir = gsf_output_container (GSF_OUTPUT (parent_dir))));
828 /* yes prepend is slow, this will never be preformance critical */
829 path = g_string_new (gsf_output_name (GSF_OUTPUT (child)));
830 child_dir = GSF_OUTFILE (child);
831 while (NULL != (child_dir = gsf_output_container (GSF_OUTPUT (child_dir))) &&
832 NULL != gsf_output_name (GSF_OUTPUT (child_dir)) &&
833 child_dir != parent_dir) {
834 g_string_prepend_c (path, '/');
835 g_string_prepend (path, gsf_output_name (GSF_OUTPUT (child_dir)));
838 g_string_prepend (path, "../");
840 return gsf_outfile_open_pkg_create_rel (parent,
841 g_string_free (path, FALSE), type, FALSE);
845 * gsf_outfile_open_pkg_add_rel:
847 * @name : target name
848 * @content_type : non-%NULL content type
849 * @parent : #GsfOutfile
850 * @type : target type
852 * A convenience wrapper to create a child in @dir of @content_type then create
853 * a @type relation to @parent
855 * Returns: the new part.
858 gsf_outfile_open_pkg_add_rel (GsfOutfile *dir,
860 char const *content_type,
864 GsfOutput *part = gsf_outfile_new_child_full (dir, name, FALSE,
865 "content-type", content_type,
867 (void) gsf_outfile_open_pkg_relate (GSF_OUTFILE_OPEN_PKG (part),
868 GSF_OUTFILE_OPEN_PKG (parent), type);
873 * gsf_outfile_open_pkg_add_extern_rel :
874 * @parent : #GsfOutfileOpenPkg
875 * @target : target type
876 * @content_type : target content
878 * Add an external relation to @parent.
880 * Returns: The id of the relation. The string is managed by the parent and
881 * should not be changed or freed by the caller.
884 gsf_outfile_open_pkg_add_extern_rel (GsfOutfileOpenPkg *parent,
886 char const *content_type)
888 return gsf_outfile_open_pkg_create_rel (parent,
889 g_strdup (target), content_type, TRUE);
893 gsf_open_pkg_error_id (void)
895 return 42; /* something arbitrary */