/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* gsf-open-pkg-utils.c: Utilities for handling Open Package zip files
* from MS Office 2007 or XPS.
*
* Copyright (C) 2006-2007 Jody Goldberg (jody@gnome.org)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2.1 of the GNU Lesser General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <gsf-config.h>
#include <gsf/gsf-open-pkg-utils.h>
#include <gsf/gsf-input.h>
#include <gsf/gsf-infile.h>
#include <gsf/gsf-outfile-impl.h>
#include <gsf/gsf-impl-utils.h>
#include <glib/gi18n-lib.h>
#include <string.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "libgsf:open_pkg"
enum {
OPEN_PKG_NS_REL
};
static GsfXMLInNS const open_pkg_ns[] = {
GSF_XML_IN_NS (OPEN_PKG_NS_REL, "http://schemas.openxmlformats.org/package/2006/relationships"),
GSF_XML_IN_NS_END
};
struct _GsfOpenPkgRel {
char *id, *type, *target;
gboolean is_extern;
};
struct _GsfOpenPkgRels {
GHashTable *by_id;
GHashTable *by_type;
};
static void
gsf_open_pkg_rels_free (GsfOpenPkgRels *rels)
{
g_hash_table_destroy (rels->by_id);
g_hash_table_destroy (rels->by_type);
g_free (rels);
}
static void
gsf_open_pkg_rel_free (GsfOpenPkgRel *rel)
{
g_free (rel->id); rel->id = NULL;
g_free (rel->type); rel->type = NULL;
g_free (rel->target); rel->target = NULL;
g_free (rel);
}
static void
open_pkg_rel_begin (GsfXMLIn *xin, xmlChar const **attrs)
{
GsfOpenPkgRels *rels = xin->user_state;
GsfOpenPkgRel *rel;
xmlChar const *id = NULL;
xmlChar const *type = NULL;
xmlChar const *target = NULL;
gboolean is_extern = FALSE;
for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
if (0 == strcmp (attrs[0], "Id"))
id = attrs[1];
else if (0 == strcmp (attrs[0], "Type"))
type = attrs[1];
else if (0 == strcmp (attrs[0], "Target"))
target = attrs[1];
else if (0 == strcmp (attrs[0], "TargetMode"))
is_extern = 0 == strcmp (attrs[1], "External");
g_return_if_fail (id != NULL);
g_return_if_fail (type != NULL);
g_return_if_fail (target != NULL);
rel = g_new0 (GsfOpenPkgRel, 1);
rel->id = g_strdup (id);
rel->type = g_strdup (type);
rel->target = g_strdup (target);
rel->is_extern = is_extern;
g_hash_table_replace (rels->by_id, rel->id, rel);
g_hash_table_replace (rels->by_type, rel->type, rel);
}
static GsfXMLInNode const open_pkg_rel_dtd[] = {
GSF_XML_IN_NODE_FULL (START, START, -1, NULL, GSF_XML_NO_CONTENT, FALSE, TRUE, NULL, NULL, 0),
GSF_XML_IN_NODE_FULL (START, RELS, OPEN_PKG_NS_REL, "Relationships", GSF_XML_NO_CONTENT, FALSE, TRUE, NULL, NULL, 0),
GSF_XML_IN_NODE (RELS, REL, OPEN_PKG_NS_REL, "Relationship", GSF_XML_NO_CONTENT, open_pkg_rel_begin, NULL),
GSF_XML_IN_NODE_END
};
/**
* gsf_open_pkg_rel_is_extern :
* @rel : #GsfOpenPkgRel
*
* Returns: %TRUE if @rel has mode 'External'
**/
gboolean
gsf_open_pkg_rel_is_extern (GsfOpenPkgRel const *rel)
{
g_return_val_if_fail (rel != NULL, FALSE);
return rel->is_extern;
}
/**
* gsf_open_pkg_rel_get_target :
* @rel : #GsfOpenPkgRel
*
* Returns: const pointer to @rel's target.
**/
char const *
gsf_open_pkg_rel_get_target (GsfOpenPkgRel const *rel)
{
g_return_val_if_fail (rel != NULL, NULL);
return rel->target;
}
/**
* gsf_open_pkg_rel_get_type :
* @rel : #GsfOpenPkgRel
*
* Returns: const pointer to @rel's type.
**/
char const *
gsf_open_pkg_rel_get_type (GsfOpenPkgRel const *rel)
{
g_return_val_if_fail (rel != NULL, NULL);
return rel->type;
}
/**
* gsf_open_pkg_get_rels :
* @in : #GsfInput
*
* Returns: a hashtable of the relationships associated with @in
**/
static GsfOpenPkgRels *
gsf_open_pkg_get_rels (GsfInput *in)
{
GsfOpenPkgRels *rels;
g_return_val_if_fail (in != NULL, NULL);
if (NULL == (rels = g_object_get_data (G_OBJECT (in), "OpenPkgRels"))) {
char const *part_name = gsf_input_name (in);
GsfXMLInDoc *rel_doc;
GsfInput *rel_stream;
if (NULL != part_name) {
GsfInfile *container = gsf_input_container (in);
char *rel_name;
g_return_val_if_fail (container != NULL, NULL);
rel_name = g_strconcat (part_name, ".rels", NULL);
rel_stream = gsf_infile_child_by_vname (container, "_rels", rel_name, NULL);
g_free (rel_name);
} else /* the root */
rel_stream = gsf_infile_child_by_vname (GSF_INFILE (in), "_rels", ".rels", NULL);
g_return_val_if_fail (rel_stream != NULL, NULL);
rels = g_new (GsfOpenPkgRels, 1);
rels->by_id = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, (GDestroyNotify)gsf_open_pkg_rel_free);
rels->by_type = g_hash_table_new (g_str_hash, g_str_equal);
rel_doc = gsf_xml_in_doc_new (open_pkg_rel_dtd, open_pkg_ns);
(void) gsf_xml_in_doc_parse (rel_doc, rel_stream, rels);
gsf_xml_in_doc_free (rel_doc);
g_object_unref (G_OBJECT (rel_stream));
g_object_set_data_full (G_OBJECT (in), "OpenPkgRels", rels,
(GDestroyNotify) gsf_open_pkg_rels_free);
}
return rels;
}
static GsfInput *
gsf_open_pkg_open_rel (GsfInput *in, GsfOpenPkgRel *rel,
G_GNUC_UNUSED GError **err /* just in case we need it one day */ )
{
GsfInfile *container, *prev;
gchar **elems;
unsigned i;
g_return_val_if_fail (rel != NULL, NULL);
g_return_val_if_fail (in != NULL, NULL);
container = gsf_input_name (in)
? gsf_input_container (in) : GSF_INFILE (in);
/* parts can not have '/' in their names ? TODO : PROVE THIS
* right now the only test is that worksheets can not have it
* in their names */
elems = g_strsplit (rel->target, "/", 0);
for (i = 0 ; elems[i] ; i++) {
prev = container;
if (0 == strcmp (elems[i], "..")) {
container = gsf_input_container (GSF_INPUT (container));
g_return_val_if_fail (container != NULL, NULL);
g_object_ref (container);
in = NULL;
} else if (0 == strcmp (elems[i], ".")) {
in = NULL; /* Be pedantic and ignore '.' */
continue;
} else {
in = gsf_infile_child_by_name (container, elems[i]);
if (NULL != elems[i+1]) {
g_return_val_if_fail (GSF_IS_INFILE (in), NULL);
container = GSF_INFILE (in);
}
}
if (i > 0)
g_object_unref (G_OBJECT (prev));
}
g_strfreev (elems);
return in;
}
/**
* gsf_open_pkg_lookup_rel_by_type :
* @in : #GsfInput
* @type :
*
* New in 1.14.6
*
* Finds _a_ relation of @in with @type (no order is guaranteed)
*
* Returns: A #GsfOpenPkgRel or %NULL
**/
GsfOpenPkgRel *
gsf_open_pkg_lookup_rel_by_type (GsfInput *in, char const *type)
{
GsfOpenPkgRels *rels = gsf_open_pkg_get_rels (in);
g_return_val_if_fail (rels != NULL, NULL);
return g_hash_table_lookup (rels->by_type, type);
}
/**
* gsf_open_pkg_open_rel_by_id :
* @in : #GsfInput
* @id :
*
* New in 1.14.6
*
* Finds @in's relation with @id
*
* Returns: A #GsfOpenPkgRel or %NULL
**/
GsfOpenPkgRel *
gsf_open_pkg_lookup_rel_by_id (GsfInput *in, char const *id)
{
GsfOpenPkgRels *rels = gsf_open_pkg_get_rels (in);
g_return_val_if_fail (rels != NULL, NULL);
return g_hash_table_lookup (rels->by_id, id);
}
/**
* gsf_open_pkg_open_rel_by_id :
* @in : #GsfInput
* @id :
* @err : optionally %NULL
*
* New in 1.15.0
*
* Open @in's relation @id
*
* Returns: A new GsfInput or %NULL, and sets @err if possible.
**/
GsfInput *
gsf_open_pkg_open_rel_by_id (GsfInput *in, char const *id, GError **err)
{
GsfOpenPkgRel *rel = NULL;
GsfOpenPkgRels *rels = gsf_open_pkg_get_rels (in);
g_return_val_if_fail (rels != NULL, NULL);
if (NULL != (rel = g_hash_table_lookup (rels->by_id, id)))
return gsf_open_pkg_open_rel (in, rel, err);
if (err)
*err = g_error_new (gsf_input_error_id(), gsf_open_pkg_error_id (),
_("Unable to find part id='%s' for '%s'"),
id, gsf_input_name (in) );
return NULL;
}
/**
* gsf_open_pkg_open_rel_by_type :
* @in : #GsfInput
* @type :
* @err : optionally %NULL
*
* New in 1.15.0
*
* Open one of @in's relationships with type=@type.
*
* Returns: A new GsfInput or %NULL, and sets @err if possible.
**/
GsfInput *
gsf_open_pkg_open_rel_by_type (GsfInput *in, char const *type, GError **err)
{
GsfOpenPkgRel *rel = NULL;
GsfOpenPkgRels *rels = gsf_open_pkg_get_rels (in);
g_return_val_if_fail (rels != NULL, NULL);
if (NULL != (rel = g_hash_table_lookup (rels->by_type, type)))
return gsf_open_pkg_open_rel (in, rel, err);
if (err)
*err = g_error_new (gsf_input_error_id(), gsf_open_pkg_error_id (),
_("Unable to find part with type='%s' for '%s'"),
type, gsf_input_name (in) );
return NULL;
}
/**
* gsf_open_pkg_parse_rel_by_id :
* @xin : #GsfXMLIn
* @id :
* @dtd : #GsfXMLInNode
* @ns : #GsfXMLInNS
*
* Convenience function to parse a related part.
*
* Returns: NULL on success or a GError which callerss need to free on failure.
**/
GError *
gsf_open_pkg_parse_rel_by_id (GsfXMLIn *xin, char const *id,
GsfXMLInNode const *dtd,
GsfXMLInNS const *ns)
{
GError *res = NULL;
GsfInput *cur_stream, *part_stream;
g_return_val_if_fail (xin != NULL, NULL);
cur_stream = gsf_xml_in_get_input (xin);
if (NULL == id)
return g_error_new (gsf_input_error_id(), gsf_open_pkg_error_id (),
_("Missing id for part in '%s'"),
gsf_input_name (cur_stream) );
part_stream = gsf_open_pkg_open_rel_by_id (cur_stream, id, &res);
if (NULL != part_stream) {
GsfXMLInDoc *doc = gsf_xml_in_doc_new (dtd, ns);
if (!gsf_xml_in_doc_parse (doc, part_stream, xin->user_state))
res = g_error_new (gsf_input_error_id(), gsf_open_pkg_error_id (),
_("Part '%s' in '%s' from '%s' is corrupt!"),
id,
gsf_input_name (part_stream),
gsf_input_name (cur_stream) );
gsf_xml_in_doc_free (doc);
g_object_unref (G_OBJECT (part_stream));
}
return res;
}
/* DEPRECATED in 1.14.6 */
GsfInput *gsf_open_pkg_get_rel_by_type (GsfInput *in, char const *type) { return gsf_open_pkg_open_rel_by_type (in, type, NULL); }
GsfInput *gsf_open_pkg_get_rel_by_id (GsfInput *in, char const *id) { return gsf_open_pkg_open_rel_by_id (in, id, NULL); }
/*************************************************************/
struct _GsfOutfileOpenPkg {
GsfOutfile parent;
GsfOutput *sink;
gboolean is_dir;
char *content_type;
GSList *children;
GSList *relations;
};
typedef GsfOutfileClass GsfOutfileOpenPkgClass;
enum {
PROP_0,
PROP_SINK,
PROP_CONTENT_TYPE,
PROP_IS_DIR,
PROP_IS_ROOT
};
static GObjectClass *parent_class;
static void
gsf_outfile_open_pkg_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GsfOutfileOpenPkg *open_pkg = (GsfOutfileOpenPkg *)object;
switch (property_id) {
case PROP_SINK:
g_value_set_object (value, open_pkg->sink);
break;
case PROP_CONTENT_TYPE:
g_value_set_string (value, open_pkg->content_type);
break;
case PROP_IS_DIR:
g_value_set_boolean (value, open_pkg->is_dir);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gsf_outfile_open_pkg_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
GsfOutfileOpenPkg *open_pkg = (GsfOutfileOpenPkg *)object;
switch (property_id) {
case PROP_SINK:
gsf_outfile_open_pkg_set_sink (open_pkg, g_value_get_object (value));
break;
case PROP_CONTENT_TYPE:
gsf_outfile_open_pkg_set_content_type (open_pkg, g_value_get_string (value));
break;
case PROP_IS_DIR:
open_pkg->is_dir = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gsf_outfile_open_pkg_init (GObject *obj)
{
GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (obj);
open_pkg->sink = NULL;
open_pkg->content_type = NULL;
open_pkg->is_dir = FALSE;
open_pkg->children = NULL;
open_pkg->relations = NULL;
}
static void
gsf_outfile_open_pkg_finalize (GObject *obj)
{
GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (obj);
GSList *ptr;
if (open_pkg->sink != NULL) {
g_object_unref (open_pkg->sink);
open_pkg->sink = NULL;
}
g_free (open_pkg->content_type);
open_pkg->content_type = NULL;
for (ptr = open_pkg->children ; ptr != NULL ; ptr = ptr->next)
g_object_unref (ptr->data);
g_slist_free (open_pkg->children);
parent_class->finalize (obj);
}
static gboolean
gsf_outfile_open_pkg_write (GsfOutput *output, size_t num_bytes, guint8 const *data)
{
GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (output);
return gsf_output_write (open_pkg->sink, num_bytes, data);
}
static gboolean
gsf_outfile_open_pkg_seek (GsfOutput *output, gsf_off_t offset, GSeekType whence)
{
GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (output);
return gsf_output_seek (open_pkg->sink, offset, whence);
}
static GsfOutput *
gsf_outfile_open_pkg_new_child (GsfOutfile *parent,
char const *name, gboolean is_dir,
char const *first_property_name, va_list args)
{
GsfOutfileOpenPkg *child, *open_pkg = GSF_OUTFILE_OPEN_PKG (parent);
GsfOutput *sink;
if (!open_pkg->is_dir)
return NULL;
child = (GsfOutfileOpenPkg *)g_object_new_valist (
GSF_OUTFILE_OPEN_PKG_TYPE, first_property_name, args);
gsf_output_set_name (GSF_OUTPUT (child), name);
gsf_output_set_container (GSF_OUTPUT (child), parent);
child->is_dir = is_dir;
sink = gsf_outfile_new_child (GSF_OUTFILE (open_pkg->sink), name, is_dir);
gsf_outfile_open_pkg_set_sink (child, sink);
g_object_unref (sink);
open_pkg->children = g_slist_prepend (open_pkg->children, child);
g_object_ref (child);
return GSF_OUTPUT (child);
}
static void
gsf_open_pkg_write_content_default (GsfXMLOut *xml, char const *ext, char const *type)
{
gsf_xml_out_start_element (xml, "Default");
gsf_xml_out_add_cstr (xml, "Extension", ext);
gsf_xml_out_add_cstr (xml, "ContentType", type);
gsf_xml_out_end_element (xml); /* </Default> */
}
static void
gsf_open_pkg_write_content_override (GsfOutfileOpenPkg const *open_pkg,
char const *base,
GsfXMLOut *xml)
{
GsfOutfileOpenPkg const *child;
char *path;
GSList *ptr;
for (ptr = open_pkg->children ; ptr != NULL ; ptr = ptr->next) {
child = ptr->data;
if (child->is_dir) {
path = g_strconcat (base, gsf_output_name (GSF_OUTPUT (child)), "/", NULL);
gsf_open_pkg_write_content_override (child, path, xml);
} else {
path = g_strconcat (base, gsf_output_name (GSF_OUTPUT (child)), NULL);
/* rels files do need content types, the defaults handle them */
if (NULL != child->content_type) {
gsf_xml_out_start_element (xml, "Override");
gsf_xml_out_add_cstr (xml, "PartName", path);
gsf_xml_out_add_cstr (xml, "ContentType", child->content_type);
gsf_xml_out_end_element (xml); /* </Override> */
}
}
g_free (path);
}
}
static gboolean
gsf_outfile_open_pkg_close (GsfOutput *output)
{
GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (output);
GsfOutput *dir;
gboolean res = FALSE;
char *rels_name;
if (NULL == open_pkg->sink || gsf_output_is_closed (open_pkg->sink))
return TRUE;
/* Generate [Content_types].xml when we close the root dir */
if (NULL == gsf_output_name (output)) {
GsfOutput *out = gsf_outfile_new_child (GSF_OUTFILE (open_pkg->sink),
"[Content_Types].xml", FALSE);
GsfXMLOut *xml = gsf_xml_out_new (out);
gsf_xml_out_start_element (xml, "Types");
gsf_xml_out_add_cstr_unchecked (xml, "xmlns",
"http://schemas.openxmlformats.org/package/2006/content-types");
gsf_open_pkg_write_content_default (xml, "rels",
"application/vnd.openxmlformats-package.relationships+xml");
gsf_open_pkg_write_content_default (xml, "xlbin",
"application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings");
gsf_open_pkg_write_content_default (xml, "xml",
"application/xml");
gsf_open_pkg_write_content_override (open_pkg, "/", xml);
gsf_xml_out_end_element (xml); /* </Types> */
g_object_unref (xml);
gsf_output_close (out);
g_object_unref (out);
dir = open_pkg->sink;
rels_name = g_strdup (".rels");
} else {
res = gsf_output_close (open_pkg->sink);
dir = (GsfOutput *)gsf_output_container (open_pkg->sink);
rels_name = g_strconcat (gsf_output_name (output), ".rels", NULL);
}
if (NULL != open_pkg->relations) {
GsfOutput *rels;
GsfXMLOut *xml;
GsfOpenPkgRel *rel;
GSList *ptr;
dir = gsf_outfile_new_child (GSF_OUTFILE (dir), "_rels", TRUE);
rels = gsf_outfile_new_child (GSF_OUTFILE (dir), rels_name, FALSE);
xml = gsf_xml_out_new (rels);
gsf_xml_out_start_element (xml, "Relationships");
gsf_xml_out_add_cstr_unchecked (xml, "xmlns",
"http://schemas.openxmlformats.org/package/2006/relationships");
for (ptr = open_pkg->relations ; ptr != NULL ; ptr = ptr->next) {
rel = ptr->data;
gsf_xml_out_start_element (xml, "Relationship");
gsf_xml_out_add_cstr (xml, "Id", rel->id);
gsf_xml_out_add_cstr (xml, "Type", rel->type);
gsf_xml_out_add_cstr (xml, "Target", rel->target);
if (rel->is_extern)
gsf_xml_out_add_cstr_unchecked (xml, "TargetMode", "External");
gsf_xml_out_end_element (xml); /* </Relationship> */
g_free (rel->id);
g_free (rel->type);
g_free (rel->target);
g_free (rel);
}
g_slist_free (open_pkg->relations);
gsf_xml_out_end_element (xml); /* </Relationships> */
g_object_unref (xml);
gsf_output_close (rels);
g_object_unref (rels);
g_object_unref (dir);
}
g_free (rels_name);
/* close the container */
if (NULL == gsf_output_name (output))
return gsf_output_close (open_pkg->sink);
return res;
}
static void
gsf_outfile_open_pkg_class_init (GObjectClass *gobject_class)
{
GsfOutputClass *output_class = GSF_OUTPUT_CLASS (gobject_class);
GsfOutfileClass *outfile_class = GSF_OUTFILE_CLASS (gobject_class);
gobject_class->finalize = gsf_outfile_open_pkg_finalize;
gobject_class->get_property = gsf_outfile_open_pkg_get_property;
gobject_class->set_property = gsf_outfile_open_pkg_set_property;
output_class->Write = gsf_outfile_open_pkg_write;
output_class->Seek = gsf_outfile_open_pkg_seek;
output_class->Close = gsf_outfile_open_pkg_close;
outfile_class->new_child = gsf_outfile_open_pkg_new_child;
parent_class = g_type_class_peek_parent (gobject_class);
g_object_class_install_property (gobject_class, PROP_SINK,
g_param_spec_object ("sink", "Sink", "The GsfOutput that stores the Open Package content.",
GSF_OUTFILE_TYPE, GSF_PARAM_STATIC | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (gobject_class, PROP_CONTENT_TYPE,
g_param_spec_string ("content-type", "ContentType", "The ContentType stored in the root [Content_Types].xml file.",
"", GSF_PARAM_STATIC | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (gobject_class, PROP_IS_DIR,
g_param_spec_boolean ("is-dir", "IsDir", "Can the outfile have children.",
FALSE, GSF_PARAM_STATIC | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
GSF_CLASS (GsfOutfileOpenPkg, gsf_outfile_open_pkg,
gsf_outfile_open_pkg_class_init, gsf_outfile_open_pkg_init,
GSF_OUTFILE_TYPE)
/**
* gsf_outfile_open_pkg_new :
* @sink : #GsfOutfile
*
* Convenience routine to create a GsfOutfileOpenPkg inside @sink.
*
* Returns: a GsfOutfile that the caller is responsible for.
**/
GsfOutfile *
gsf_outfile_open_pkg_new (GsfOutfile *sink)
{
return g_object_new (GSF_OUTFILE_OPEN_PKG_TYPE,
"sink", sink, "is-dir", TRUE,
NULL);
}
/**
* gsf_outfile_open_pkg_set_sink :
* @open_pkg : #GsfOutfileOpenPkg
* @sink : #GsfOutput
*
* Assigns a GsfOutput (@sink) to store the package into.
**/
void
gsf_outfile_open_pkg_set_sink (GsfOutfileOpenPkg *open_pkg, GsfOutput *sink)
{
if (sink)
g_object_ref (sink);
if (open_pkg->sink)
g_object_unref (open_pkg->sink);
open_pkg->sink = sink;
}
/**
* gsf_outfile_open_pkg_set_content_type :
* @open_pkg : #GsfOutfileOpenPkg
* @content_type :
*
**/
void
gsf_outfile_open_pkg_set_content_type (GsfOutfileOpenPkg *open_pkg,
char const *content_type)
{
g_return_if_fail (content_type != NULL);
if (open_pkg->content_type != content_type) {
g_free (open_pkg->content_type);
open_pkg->content_type = g_strdup (content_type);
}
}
static char const *
gsf_outfile_open_pkg_create_rel (GsfOutfileOpenPkg *parent,
char *target,
char const *type,
gboolean is_extern)
{
GsfOpenPkgRel *rel = g_new0 (GsfOpenPkgRel, 1);
rel->target = target;
rel->type = g_strdup (type);
rel->id = g_strdup_printf ("rId%u", g_slist_length (parent->relations) + 1);
rel->is_extern = is_extern;
parent->relations = g_slist_prepend (parent->relations, rel);
return rel->id;
}
/**
* gsf_outfile_open_pkg_relate:
* @child : #GsfOutfileOpenPkg
* @parent : #GsfOutfileOpenPkg
* @type :
*
* Create a relationship between @child and @parent of @type.
*
* Returns: the relID which the caller does not own but will live as long as
* @parent.
**/
char const *
gsf_outfile_open_pkg_relate (GsfOutfileOpenPkg *child,
GsfOutfileOpenPkg *parent,
char const *type)
{
GString *path;
int up = -1;
GsfOutfile *child_dir, *parent_dir;
/* Calculate the path from @child to @parent */
parent_dir = parent->is_dir ? GSF_OUTFILE (parent)
: gsf_output_container (GSF_OUTPUT (parent));
do {
up++;
child_dir = GSF_OUTFILE (child);
while (NULL != (child_dir = gsf_output_container (GSF_OUTPUT (child_dir))))
if (child_dir == parent_dir)
goto found; /* break out of both loops */
} while (NULL != (parent_dir = gsf_output_container (GSF_OUTPUT (parent_dir))));
found:
/* yes prepend is slow, this will never be preformance critical */
path = g_string_new (gsf_output_name (GSF_OUTPUT (child)));
child_dir = GSF_OUTFILE (child);
while (NULL != (child_dir = gsf_output_container (GSF_OUTPUT (child_dir))) &&
NULL != gsf_output_name (GSF_OUTPUT (child_dir)) &&
child_dir != parent_dir) {
g_string_prepend_c (path, '/');
g_string_prepend (path, gsf_output_name (GSF_OUTPUT (child_dir)));
}
while (up--)
g_string_prepend (path, "../");
return gsf_outfile_open_pkg_create_rel (parent,
g_string_free (path, FALSE), type, FALSE);
}
/**
* gsf_outfile_open_pkg_add_rel:
* @dir : #GsfOutfile
* @name :
* @content_type :
* @parent : #GsfOutfile
* @type :
*
* A convenience wrapper to create a child in @dir of @content_type then create
* a @type relation to @parent
*
* Returns: the new part.
**/
GsfOutput *
gsf_outfile_open_pkg_add_rel (GsfOutfile *dir,
char const *name,
char const *content_type,
GsfOutfile *parent,
char const *type)
{
GsfOutput *part = gsf_outfile_new_child_full (dir, name, FALSE,
"content-type", content_type,
NULL);
(void) gsf_outfile_open_pkg_relate (GSF_OUTFILE_OPEN_PKG (part),
GSF_OUTFILE_OPEN_PKG (parent), type);
return part;
}
/**
* gsf_outfile_open_pkg_add_extern_rel :
* @parent : #GsfOutfileOpenPkg
* @target :
* @content_type :
*
* Add an external relation to @parent.
*
* Returns: The id of the relation. The string is managed by the parent and
* should not be changed or freed by the caller.
**/
char const *
gsf_outfile_open_pkg_add_extern_rel (GsfOutfileOpenPkg *parent,
char const *target,
char const *content_type)
{
return gsf_outfile_open_pkg_create_rel (parent,
g_strdup (target), content_type, TRUE);
}
gint
gsf_open_pkg_error_id (void)
{
return 42; /* something arbitrary */
}
syntax highlighted by Code2HTML, v. 0.9.1