summaryrefslogtreecommitdiff
path: root/libyelp/yelp-docbook-document.c
diff options
context:
space:
mode:
authorShaun McCance <shaunm@gnome.org>2009-11-15 16:36:27 -0600
committerShaun McCance <shaunm@gnome.org>2009-11-15 16:36:27 -0600
commit2311a1568af07235722302f9a1936a8b05699cf4 (patch)
tree098e2bb275f46a60e236b7b112100a22c38fbc7b /libyelp/yelp-docbook-document.c
parent63016128972c806708cd458c4bf4331da42a9423 (diff)
downloadyelp-2311a1568af07235722302f9a1936a8b05699cf4.tar.gz
[yelp-docbook-document] Adding DocBook support into libyelp
Diffstat (limited to 'libyelp/yelp-docbook-document.c')
-rw-r--r--libyelp/yelp-docbook-document.c803
1 files changed, 803 insertions, 0 deletions
diff --git a/libyelp/yelp-docbook-document.c b/libyelp/yelp-docbook-document.c
new file mode 100644
index 00000000..028646e9
--- /dev/null
+++ b/libyelp/yelp-docbook-document.c
@@ -0,0 +1,803 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance <shaunm@gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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 General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance <shaunm@gnome.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xinclude.h>
+
+#include "yelp-docbook-document.h"
+#include "yelp-error.h"
+#include "yelp-settings.h"
+#include "yelp-transform.h"
+#include "yelp-debug.h"
+
+#define STYLESHEET DATADIR"/yelp/xslt/db2html.xsl"
+
+typedef enum {
+ DOCBOOK_STATE_BLANK, /* Brand new, run transform as needed */
+ DOCBOOK_STATE_PARSING, /* Parsing/transforming document, please wait */
+ DOCBOOK_STATE_PARSED, /* All done, if we ain't got it, it ain't here */
+ DOCBOOK_STATE_STOP /* Stop everything now, object to be disposed */
+} DocbookState;
+
+enum {
+ DOCBOOK_COLUMN_ID,
+ DOCBOOK_COLUMN_TITLE
+};
+
+static void yelp_docbook_document_class_init (YelpDocbookDocumentClass *klass);
+static void yelp_docbook_document_init (YelpDocbookDocument *docbook);
+static void yelp_docbook_document_dispose (GObject *object);
+static void yelp_docbook_document_finalize (GObject *object);
+
+static gboolean docbook_request_page (YelpDocument *document,
+ const gchar *page_id,
+ GCancellable *cancellable,
+ YelpDocumentCallback callback,
+ gpointer user_data);
+
+static void docbook_process (YelpDocbookDocument *docbook);
+static void docbook_disconnect (YelpDocbookDocument *docbook);
+
+static void docbook_walk (YelpDocbookDocument *docbook);
+static gboolean docbook_walk_chunkQ (YelpDocbookDocument *docbook);
+static gboolean docbook_walk_divisionQ (YelpDocbookDocument *docbook);
+static gchar * docbook_walk_get_title (YelpDocbookDocument *docbook);
+
+static void transform_chunk_ready (YelpTransform *transform,
+ gchar *chunk_id,
+ YelpDocbookDocument *docbook);
+static void transform_finished (YelpTransform *transform,
+ YelpDocbookDocument *docbook);
+static void transform_error (YelpTransform *transform,
+ YelpDocbookDocument *docbook);
+static void transform_finalized (YelpDocbookDocument *docbook,
+ gpointer transform);
+
+/* FIXME */
+#if 0
+/* static gpointer docbook_get_sections (YelpDocument *document); */
+#endif
+
+G_DEFINE_TYPE (YelpDocbookDocument, yelp_docbook_document, YELP_TYPE_DOCUMENT);
+#define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DOCBOOK_DOCUMENT, YelpDocbookDocumentPrivate))
+
+typedef struct _YelpDocbookDocumentPrivate YelpDocbookDocumentPrivate;
+struct _YelpDocbookDocumentPrivate {
+ YelpUri *uri;
+
+ DocbookState state;
+
+ GMutex *mutex;
+ GThread *thread;
+
+ gboolean process_running;
+ gboolean transform_running;
+
+ YelpTransform *transform;
+ guint chunk_ready;
+ guint finished;
+ guint error;
+
+ /* FIXME: all */
+ GtkTreeModel *sections;
+ GtkTreeIter *sections_iter; /* On the stack, do not free */
+
+ xmlDocPtr xmldoc;
+ xmlNodePtr xmlcur;
+ gint max_depth;
+ gint cur_depth;
+ gchar *cur_page_id;
+ gchar *cur_prev_id;
+};
+
+/******************************************************************************/
+
+static void
+yelp_docbook_document_class_init (YelpDocbookDocumentClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass);
+
+ object_class->dispose = yelp_docbook_document_dispose;
+ object_class->finalize = yelp_docbook_document_finalize;
+
+ document_class->request_page = docbook_request_page;
+ /*document_class->get_sections = docbook_get_sections;*/
+
+ g_type_class_add_private (klass, sizeof (YelpDocbookDocumentPrivate));
+}
+
+static void
+yelp_docbook_document_init (YelpDocbookDocument *docbook)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+
+ priv->sections = NULL;
+
+ priv->state = DOCBOOK_STATE_BLANK;
+
+ priv->mutex = g_mutex_new ();
+}
+
+static void
+yelp_docbook_document_dispose (GObject *object)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (object);
+
+ g_object_unref (priv->uri);
+
+ g_object_unref (priv->sections);
+
+ G_OBJECT_CLASS (yelp_docbook_document_parent_class)->dispose (object);
+}
+
+static void
+yelp_docbook_document_finalize (GObject *object)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (object);
+
+ if (priv->xmldoc)
+ xmlFreeDoc (priv->xmldoc);
+
+ g_free (priv->cur_page_id);
+ g_free (priv->cur_prev_id);
+
+ g_mutex_free (priv->mutex);
+
+ G_OBJECT_CLASS (yelp_docbook_document_parent_class)->finalize (object);
+}
+
+/******************************************************************************/
+
+YelpDocument *
+yelp_docbook_document_new (YelpUri *uri)
+{
+ YelpDocbookDocument *docbook;
+ YelpDocbookDocumentPrivate *priv;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ docbook = (YelpDocbookDocument *) g_object_new (YELP_TYPE_DOCBOOK_DOCUMENT, NULL);
+ priv = GET_PRIV (docbook);
+
+ priv->uri = g_object_ref (uri);
+
+ yelp_document_set_page_id (YELP_DOCUMENT (docbook), "//about", "//about");
+
+ priv->sections =
+ GTK_TREE_MODEL (gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_STRING));
+
+ return (YelpDocument *) docbook;
+}
+
+/******************************************************************************/
+
+/** YelpDocument **************************************************************/
+
+/* static gpointer */
+/* docbook_get_sections (YelpDocument *document) */
+/* { */
+/* YelpDocbook *db = (YelpDocbook *) document; */
+
+/* return (gpointer) (db->priv->sections); */
+/* } */
+
+static gboolean
+docbook_request_page (YelpDocument *document,
+ const gchar *page_id,
+ GCancellable *cancellable,
+ YelpDocumentCallback callback,
+ gpointer user_data)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (document);
+ gchar *docuri;
+ GError *error;
+ gboolean handled;
+
+ debug_print (DB_FUNCTION, "entering\n");
+ debug_print (DB_ARG, " page_id=\"%s\"\n", page_id);
+
+ if (page_id == NULL)
+ page_id = "//index";
+
+ handled =
+ YELP_DOCUMENT_CLASS (yelp_docbook_document_parent_class)->request_page (document,
+ page_id,
+ cancellable,
+ callback,
+ user_data);
+ if (handled) {
+ return;
+ }
+
+ g_mutex_lock (priv->mutex);
+
+ switch (priv->state) {
+ case DOCBOOK_STATE_BLANK:
+ priv->state = DOCBOOK_STATE_PARSING;
+ priv->process_running = TRUE;
+ g_object_ref (document);
+ priv->thread = g_thread_create ((GThreadFunc) docbook_process,
+ document, FALSE, NULL);
+ break;
+ case DOCBOOK_STATE_PARSING:
+ break;
+ case DOCBOOK_STATE_PARSED:
+ case DOCBOOK_STATE_STOP:
+ docuri = yelp_uri_get_document_uri (priv->uri);
+ error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
+ _("The page ‘%s’ was not found in the document ‘%s’."),
+ page_id, docuri);
+ g_free (docuri);
+ yelp_document_signal (document, page_id,
+ YELP_DOCUMENT_SIGNAL_ERROR,
+ error);
+ g_error_free (error);
+ break;
+ }
+
+ g_mutex_unlock (priv->mutex);
+}
+
+/******************************************************************************/
+
+static void
+docbook_process (YelpDocbookDocument *docbook)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+ YelpDocument *document = YELP_DOCUMENT (docbook);
+ GFile *file = NULL;
+ gchar *filepath = NULL;
+ xmlDocPtr xmldoc = NULL;
+ xmlChar *id = NULL;
+ xmlParserCtxtPtr parserCtxt = NULL;
+ GError *error;
+ gint params_i = 0;
+ gchar **params = NULL;
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ file = yelp_uri_get_file (priv->uri);
+ if (file == NULL) {
+ error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
+ _("The file does not exist."));
+ yelp_document_error_pending (document, error);
+ g_error_free (error);
+ goto done;
+ }
+
+ filepath = g_file_get_path (file);
+ g_object_unref (file);
+ if (!g_file_test (filepath, G_FILE_TEST_IS_REGULAR)) {
+ error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
+ _("The file ‘%s’ does not exist."),
+ filepath);
+ yelp_document_error_pending (document, error);
+ g_error_free (error);
+ goto done;
+ }
+
+ parserCtxt = xmlNewParserCtxt ();
+ xmldoc = xmlCtxtReadFile (parserCtxt,
+ filepath, NULL,
+ XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
+ XML_PARSE_NOENT | XML_PARSE_NONET );
+
+ if (xmldoc == NULL) {
+ error = g_error_new (YELP_ERROR, YELP_ERROR_PROCESSING,
+ _("The file ‘%s’ could not be parsed because it is"
+ " not a well-formed XML document."),
+ filepath);
+ yelp_document_error_pending (document, error);
+ g_error_free (error);
+ goto done;
+ }
+
+ if (xmlXIncludeProcessFlags (xmldoc,
+ XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
+ XML_PARSE_NOENT | XML_PARSE_NONET )
+ < 0) {
+ error = g_error_new (YELP_ERROR, YELP_ERROR_PROCESSING,
+ _("The file ‘%s’ could not be parsed because"
+ " one or more of its included files is not"
+ " a well-formed XML document."),
+ filepath);
+ yelp_document_error_pending (document, error);
+ g_error_free (error);
+ goto done;
+ }
+
+ g_mutex_lock (priv->mutex);
+ if (!xmlStrcmp (xmlDocGetRootElement (xmldoc)->name, BAD_CAST "book"))
+ priv->max_depth = 2;
+ else
+ priv->max_depth = 1;
+
+ priv->xmldoc = xmldoc;
+ priv->xmlcur = xmlDocGetRootElement (xmldoc);
+
+ id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
+ if (id) {
+ yelp_document_set_page_id (document, NULL, (gchar *) id);
+ yelp_document_set_page_id (document, "//index", (gchar *) id);
+ yelp_document_set_prev_id (document, (gchar *) id, "//about");
+ yelp_document_set_next_id (document, "//about", (gchar *) id);
+ }
+ else {
+ yelp_document_set_page_id (document, NULL, "//index");
+ yelp_document_set_prev_id (document, "//index", "//about");
+ yelp_document_set_next_id (document, "//about", "//index");
+ /* add the id attribute to the root element with value "index"
+ * so when we try to load the document later, it doesn't fail */
+ xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST "//index");
+ }
+ g_mutex_unlock (priv->mutex);
+
+ g_mutex_lock (priv->mutex);
+ if (priv->state == DOCBOOK_STATE_STOP) {
+ g_mutex_unlock (priv->mutex);
+ goto done;
+ }
+ g_mutex_unlock (priv->mutex);
+
+ docbook_walk (docbook);
+
+ g_mutex_lock (priv->mutex);
+ if (priv->state == DOCBOOK_STATE_STOP) {
+ g_mutex_unlock (priv->mutex);
+ goto done;
+ }
+
+ priv->transform = yelp_transform_new (STYLESHEET);
+ priv->chunk_ready =
+ g_signal_connect (priv->transform, "chunk-ready",
+ (GCallback) transform_chunk_ready,
+ docbook);
+ priv->finished =
+ g_signal_connect (priv->transform, "finished",
+ (GCallback) transform_finished,
+ docbook);
+ priv->error =
+ g_signal_connect (priv->transform, "error",
+ (GCallback) transform_error,
+ docbook);
+
+ params = yelp_settings_get_all_params (yelp_settings_get_default (), 2, &params_i);
+ params[params_i++] = g_strdup ("db.chunk.max_depth");
+ params[params_i++] = g_strdup_printf ("%i", priv->max_depth);
+ params[params_i] = NULL;
+
+ priv->transform_running = TRUE;
+ yelp_transform_start (priv->transform,
+ priv->xmldoc,
+ NULL,
+ (const gchar * const *) params);
+ g_strfreev (params);
+ g_mutex_unlock (priv->mutex);
+
+ done:
+ g_free (filepath);
+ if (id)
+ xmlFree (id);
+ if (parserCtxt)
+ xmlFreeParserCtxt (parserCtxt);
+
+ priv->process_running = FALSE;
+ g_object_unref (docbook);
+}
+
+static void
+docbook_disconnect (YelpDocbookDocument *docbook)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+ if (priv->chunk_ready) {
+ g_signal_handler_disconnect (priv->transform, priv->chunk_ready);
+ priv->chunk_ready = 0;
+ }
+ if (priv->finished) {
+ g_signal_handler_disconnect (priv->transform, priv->finished);
+ priv->finished = 0;
+ }
+ if (priv->error) {
+ g_signal_handler_disconnect (priv->transform, priv->error);
+ priv->error = 0;
+ }
+ yelp_transform_cancel (priv->transform);
+ g_object_unref (priv->transform);
+ priv->transform = NULL;
+ priv->transform_running = FALSE;
+}
+
+/******************************************************************************/
+
+static void
+docbook_walk (YelpDocbookDocument *docbook)
+{
+ static gint autoid = 0;
+ gchar autoidstr[20];
+ xmlChar *id = NULL;
+ xmlChar *title = NULL;
+ gchar *old_page_id = NULL;
+ xmlNodePtr cur, old_cur;
+ GtkTreeIter iter;
+ GtkTreeIter *old_iter = NULL;
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+ YelpDocument *document = YELP_DOCUMENT (docbook);
+
+ debug_print (DB_FUNCTION, "entering\n");
+ debug_print (DB_DEBUG, " priv->xmlcur->name: %s\n", priv->xmlcur->name);
+
+ /* Check for the db.chunk.max_depth PI and set max chunk depth */
+ if (priv->cur_depth == 0)
+ for (cur = priv->xmlcur; cur; cur = cur->prev)
+ if (cur->type == XML_PI_NODE)
+ if (!xmlStrcmp (cur->name, (const xmlChar *) "db.chunk.max_depth")) {
+ gint max = atoi ((gchar *) cur->content);
+ if (max)
+ priv->max_depth = max;
+ break;
+ }
+
+ id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
+
+ if (docbook_walk_divisionQ (docbook) && !id) {
+ /* If id attribute is not present, autogenerate a
+ * unique value, and insert it into the in-memory tree */
+ g_snprintf (autoidstr, 20, "//autoid-%d", ++autoid);
+ xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST autoidstr);
+ id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
+ }
+
+ if (docbook_walk_chunkQ (docbook)) {
+ title = BAD_CAST docbook_walk_get_title (docbook);
+
+ debug_print (DB_DEBUG, " id: \"%s\"\n", id);
+ debug_print (DB_DEBUG, " title: \"%s\"\n", title);
+
+ yelp_document_set_title (document, (gchar *) id, (gchar *) title);
+
+ gdk_threads_enter ();
+ gtk_tree_store_append (GTK_TREE_STORE (priv->sections),
+ &iter,
+ priv->sections_iter);
+ gtk_tree_store_set (GTK_TREE_STORE (priv->sections),
+ &iter,
+ DOCBOOK_COLUMN_ID, id,
+ DOCBOOK_COLUMN_TITLE, title,
+ -1);
+ gdk_threads_leave ();
+
+ if (priv->cur_prev_id) {
+ yelp_document_set_prev_id (document, (gchar *) id, priv->cur_prev_id);
+ yelp_document_set_next_id (document, priv->cur_prev_id, (gchar *) id);
+ g_free (priv->cur_prev_id);
+ }
+ priv->cur_prev_id = g_strdup ((gchar *) id);
+
+ if (priv->cur_page_id)
+ yelp_document_set_up_id (document, (gchar *) id, priv->cur_page_id);
+ old_page_id = priv->cur_page_id;
+ priv->cur_page_id = g_strdup ((gchar *) id);
+
+ old_iter = priv->sections_iter;
+ if (priv->xmlcur->parent->type != XML_DOCUMENT_NODE)
+ priv->sections_iter = &iter;
+ }
+
+ old_cur = priv->xmlcur;
+ priv->cur_depth++;
+ if (id)
+ yelp_document_set_page_id (document, (gchar *) id, priv->cur_page_id);
+
+ for (cur = priv->xmlcur->children; cur; cur = cur->next) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ priv->xmlcur = cur;
+ docbook_walk (docbook);
+ }
+ }
+ priv->cur_depth--;
+ priv->xmlcur = old_cur;
+
+ if (docbook_walk_chunkQ (docbook)) {
+ priv->sections_iter = old_iter;
+ g_free (priv->cur_page_id);
+ priv->cur_page_id = old_page_id;
+ }
+
+ if (priv->cur_depth == 0) {
+ g_free (priv->cur_prev_id);
+ priv->cur_prev_id = NULL;
+
+ g_free (priv->cur_page_id);
+ priv->cur_page_id = NULL;
+ }
+
+ if (id != NULL)
+ xmlFree (id);
+ if (title != NULL)
+ xmlFree (title);
+}
+
+static gboolean
+docbook_walk_chunkQ (YelpDocbookDocument *docbook)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+ if (priv->cur_depth <= priv->max_depth)
+ return docbook_walk_divisionQ (docbook);
+ else
+ return FALSE;
+}
+
+static gboolean
+docbook_walk_divisionQ (YelpDocbookDocument *docbook)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+ xmlNodePtr node = priv->xmlcur;
+ return (!xmlStrcmp (node->name, (const xmlChar *) "appendix") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "article") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "book") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "bibliography") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "chapter") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "colophon") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "glossary") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "index") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "part") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "preface") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "reference") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "refentry") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "refsect1") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "refsect2") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "refsect3") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "refsection") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "sect1") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "sect2") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "sect3") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "sect4") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "sect5") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "section") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "set") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "setindex") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "simplesect") );
+}
+
+static gchar *
+docbook_walk_get_title (YelpDocbookDocument *docbook)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+ gchar *infoname = NULL;
+ xmlNodePtr child = NULL;
+ xmlNodePtr title = NULL;
+ xmlNodePtr title_tmp = NULL;
+
+ if (!xmlStrcmp (priv->xmlcur->name, BAD_CAST "refentry")) {
+ /* The title for a refentry element can come from the following:
+ * refmeta/refentrytitle
+ * refentryinfo/title[abbrev]
+ * refnamediv/refname
+ * We take the first one we find.
+ */
+ for (child = priv->xmlcur->children; child; child = child->next) {
+ if (!xmlStrcmp (child->name, BAD_CAST "refmeta")) {
+ for (title = child->children; title; title = title->next) {
+ if (!xmlStrcmp (title->name, BAD_CAST "refentrytitle"))
+ break;
+ }
+ if (title)
+ goto done;
+ }
+ else if (!xmlStrcmp (child->name, BAD_CAST "refentryinfo")) {
+ for (title = child->children; title; title = title->next) {
+ if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev"))
+ break;
+ else if (!xmlStrcmp (title->name, BAD_CAST "title"))
+ title_tmp = title;
+ }
+ if (title)
+ goto done;
+ else if (title_tmp) {
+ title = title_tmp;
+ goto done;
+ }
+ }
+ else if (!xmlStrcmp (child->name, BAD_CAST "refnamediv")) {
+ for (title = child->children; title; title = title->next) {
+ if (!xmlStrcmp (title->name, BAD_CAST "refname"))
+ break;
+ else if (!xmlStrcmp (title->name, BAD_CAST "refpurpose")) {
+ title = NULL;
+ break;
+ }
+ }
+ if (title)
+ goto done;
+ }
+ else if (!xmlStrncmp (child->name, BAD_CAST "refsect", 7))
+ break;
+ }
+ }
+ else {
+ /* The title for other elements appears in the following:
+ * title[abbrev]
+ * *info/title[abbrev]
+ * blockinfo/title[abbrev]
+ * objectinfo/title[abbrev]
+ * We take them in that order.
+ */
+ xmlNodePtr infos[3] = {NULL, NULL, NULL};
+ int i;
+
+ infoname = g_strdup_printf ("%sinfo", priv->xmlcur->name);
+
+ for (child = priv->xmlcur->children; child; child = child->next) {
+ if (!xmlStrcmp (child->name, BAD_CAST "titleabbrev")) {
+ title = child;
+ goto done;
+ }
+ else if (!xmlStrcmp (child->name, BAD_CAST "title"))
+ title_tmp = child;
+ else if (!xmlStrcmp (child->name, BAD_CAST infoname))
+ infos[0] = child;
+ else if (!xmlStrcmp (child->name, BAD_CAST "blockinfo"))
+ infos[1] = child;
+ else if (!xmlStrcmp (child->name, BAD_CAST "objectinfo"))
+ infos[2] = child;
+ }
+
+ if (title_tmp) {
+ title = title_tmp;
+ goto done;
+ }
+
+ for (i = 0; i < 3; i++) {
+ child = infos[i];
+ if (child) {
+ for (title = child->children; title; title = title->next) {
+ if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev"))
+ goto done;
+ else if (!xmlStrcmp (title->name, BAD_CAST "title"))
+ title_tmp = title;
+ }
+ if (title_tmp) {
+ title = title_tmp;
+ goto done;
+ }
+ }
+ }
+ }
+
+ done:
+ g_free (infoname);
+
+ if (title)
+ return (gchar *) xmlNodeGetContent (title);
+ else
+ return g_strdup (_("Unknown"));
+}
+
+/******************************************************************************/
+
+static void
+transform_chunk_ready (YelpTransform *transform,
+ gchar *chunk_id,
+ YelpDocbookDocument *docbook)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+ gchar *content;
+
+ debug_print (DB_FUNCTION, "entering\n");
+ g_assert (transform == priv->transform);
+
+ if (priv->state == DOCBOOK_STATE_STOP) {
+ docbook_disconnect (docbook);
+ return;
+ }
+
+ content = yelp_transform_take_chunk (transform, chunk_id);
+ yelp_document_give_contents (YELP_DOCUMENT (docbook),
+ chunk_id,
+ content,
+ "application/xhtml+xml");
+
+ yelp_document_signal (YELP_DOCUMENT (docbook),
+ chunk_id,
+ YELP_DOCUMENT_SIGNAL_CONTENTS,
+ NULL);
+}
+
+static void
+transform_finished (YelpTransform *transform,
+ YelpDocbookDocument *docbook)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+ gchar *docuri;
+ GError *error;
+
+ debug_print (DB_FUNCTION, "entering\n");
+ g_assert (transform == priv->transform);
+
+ if (priv->state == DOCBOOK_STATE_STOP) {
+ docbook_disconnect (docbook);
+ return;
+ }
+
+ docbook_disconnect (docbook);
+
+ /* We want to free priv->xmldoc, but we can't free it before transform
+ is finalized. Otherwise, we could crash when YelpTransform frees
+ its libxslt resources.
+ */
+ g_object_weak_ref (transform,
+ transform_finalized,
+ docbook);
+
+ docuri = yelp_uri_get_document_uri (priv->uri);
+ error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
+ _("The requested page was not found in the document ‘%s’."),
+ docuri);
+ g_free (docuri);
+ yelp_document_error_pending ((YelpDocument *) docbook, error);
+ g_error_free (error);
+}
+
+static void
+transform_error (YelpTransform *transform,
+ YelpDocbookDocument *docbook)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+ GError *error;
+
+ debug_print (DB_FUNCTION, "entering\n");
+ g_assert (transform == priv->transform);
+
+ if (priv->state == DOCBOOK_STATE_STOP) {
+ docbook_disconnect (docbook);
+ return;
+ }
+
+ error = yelp_transform_get_error (transform);
+ yelp_document_error_pending ((YelpDocument *) docbook, error);
+ g_error_free (error);
+
+ docbook_disconnect (docbook);
+}
+
+static void
+transform_finalized (YelpDocbookDocument *docbook,
+ gpointer transform)
+{
+ YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ if (priv->xmldoc)
+ xmlFreeDoc (priv->xmldoc);
+ priv->xmldoc = NULL;
+
+}