[libyelp/yelp-mallard-document] Support for Mallard Facets extension
[yelp.git] / libyelp / yelp-docbook-document.c
blobb6a1d90762e9ad8996f8a8670aef751a0c4b70c5
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2003-2009 Shaun McCance <shaunm@gnome.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Shaun McCance <shaunm@gnome.org>
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gtk/gtk.h>
30 #include <libxml/parser.h>
31 #include <libxml/parserInternals.h>
32 #include <libxml/xinclude.h>
34 #include "yelp-docbook-document.h"
35 #include "yelp-error.h"
36 #include "yelp-settings.h"
37 #include "yelp-transform.h"
38 #include "yelp-debug.h"
40 #define STYLESHEET DATADIR"/yelp/xslt/db2html.xsl"
42 typedef enum {
43 DOCBOOK_STATE_BLANK, /* Brand new, run transform as needed */
44 DOCBOOK_STATE_PARSING, /* Parsing/transforming document, please wait */
45 DOCBOOK_STATE_PARSED, /* All done, if we ain't got it, it ain't here */
46 DOCBOOK_STATE_STOP /* Stop everything now, object to be disposed */
47 } DocbookState;
49 enum {
50 DOCBOOK_COLUMN_ID,
51 DOCBOOK_COLUMN_TITLE
54 static void yelp_docbook_document_class_init (YelpDocbookDocumentClass *klass);
55 static void yelp_docbook_document_init (YelpDocbookDocument *docbook);
56 static void yelp_docbook_document_dispose (GObject *object);
57 static void yelp_docbook_document_finalize (GObject *object);
59 static gboolean docbook_request_page (YelpDocument *document,
60 const gchar *page_id,
61 GCancellable *cancellable,
62 YelpDocumentCallback callback,
63 gpointer user_data);
65 static void docbook_process (YelpDocbookDocument *docbook);
66 static void docbook_disconnect (YelpDocbookDocument *docbook);
68 static void docbook_walk (YelpDocbookDocument *docbook);
69 static gboolean docbook_walk_chunkQ (YelpDocbookDocument *docbook);
70 static gboolean docbook_walk_divisionQ (YelpDocbookDocument *docbook);
71 static gchar * docbook_walk_get_title (YelpDocbookDocument *docbook);
73 static void transform_chunk_ready (YelpTransform *transform,
74 gchar *chunk_id,
75 YelpDocbookDocument *docbook);
76 static void transform_finished (YelpTransform *transform,
77 YelpDocbookDocument *docbook);
78 static void transform_error (YelpTransform *transform,
79 YelpDocbookDocument *docbook);
80 static void transform_finalized (YelpDocbookDocument *docbook,
81 gpointer transform);
83 /* FIXME */
84 #if 0
85 /* static gpointer docbook_get_sections (YelpDocument *document); */
86 #endif
88 G_DEFINE_TYPE (YelpDocbookDocument, yelp_docbook_document, YELP_TYPE_DOCUMENT);
89 #define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DOCBOOK_DOCUMENT, YelpDocbookDocumentPrivate))
91 typedef struct _YelpDocbookDocumentPrivate YelpDocbookDocumentPrivate;
92 struct _YelpDocbookDocumentPrivate {
93 YelpUri *uri;
95 DocbookState state;
97 GMutex *mutex;
98 GThread *thread;
100 gboolean process_running;
101 gboolean transform_running;
103 YelpTransform *transform;
104 guint chunk_ready;
105 guint finished;
106 guint error;
108 /* FIXME: all */
109 GtkTreeModel *sections;
110 GtkTreeIter *sections_iter; /* On the stack, do not free */
112 xmlDocPtr xmldoc;
113 xmlNodePtr xmlcur;
114 gint max_depth;
115 gint cur_depth;
116 gchar *cur_page_id;
117 gchar *cur_prev_id;
118 gchar *root_id;
121 /******************************************************************************/
123 static void
124 yelp_docbook_document_class_init (YelpDocbookDocumentClass *klass)
126 GObjectClass *object_class = G_OBJECT_CLASS (klass);
127 YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass);
129 object_class->dispose = yelp_docbook_document_dispose;
130 object_class->finalize = yelp_docbook_document_finalize;
132 document_class->request_page = docbook_request_page;
133 /*document_class->get_sections = docbook_get_sections;*/
135 g_type_class_add_private (klass, sizeof (YelpDocbookDocumentPrivate));
138 static void
139 yelp_docbook_document_init (YelpDocbookDocument *docbook)
141 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
143 priv->sections = NULL;
145 priv->state = DOCBOOK_STATE_BLANK;
147 priv->mutex = g_mutex_new ();
150 static void
151 yelp_docbook_document_dispose (GObject *object)
153 YelpDocbookDocumentPrivate *priv = GET_PRIV (object);
155 if (priv->uri) {
156 g_object_unref (priv->uri);
157 priv->uri = NULL;
160 if (priv->sections) {
161 g_object_unref (priv->sections);
162 priv->sections = NULL;
165 G_OBJECT_CLASS (yelp_docbook_document_parent_class)->dispose (object);
168 static void
169 yelp_docbook_document_finalize (GObject *object)
171 YelpDocbookDocumentPrivate *priv = GET_PRIV (object);
173 if (priv->xmldoc)
174 xmlFreeDoc (priv->xmldoc);
176 g_free (priv->cur_page_id);
177 g_free (priv->cur_prev_id);
178 g_free (priv->root_id);
180 g_mutex_free (priv->mutex);
182 G_OBJECT_CLASS (yelp_docbook_document_parent_class)->finalize (object);
185 /******************************************************************************/
187 YelpDocument *
188 yelp_docbook_document_new (YelpUri *uri)
190 YelpDocbookDocument *docbook;
191 YelpDocbookDocumentPrivate *priv;
193 g_return_val_if_fail (uri != NULL, NULL);
195 docbook = (YelpDocbookDocument *) g_object_new (YELP_TYPE_DOCBOOK_DOCUMENT, NULL);
196 priv = GET_PRIV (docbook);
198 priv->uri = g_object_ref (uri);
200 yelp_document_set_page_id (YELP_DOCUMENT (docbook), "//about", "//about");
202 priv->sections =
203 GTK_TREE_MODEL (gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_STRING));
205 return (YelpDocument *) docbook;
208 /******************************************************************************/
210 /** YelpDocument **************************************************************/
212 /* static gpointer */
213 /* docbook_get_sections (YelpDocument *document) */
214 /* { */
215 /* YelpDocbook *db = (YelpDocbook *) document; */
217 /* return (gpointer) (db->priv->sections); */
218 /* } */
220 static gboolean
221 docbook_request_page (YelpDocument *document,
222 const gchar *page_id,
223 GCancellable *cancellable,
224 YelpDocumentCallback callback,
225 gpointer user_data)
227 YelpDocbookDocumentPrivate *priv = GET_PRIV (document);
228 gchar *docuri;
229 GError *error;
230 gboolean handled;
232 debug_print (DB_FUNCTION, "entering\n");
233 debug_print (DB_ARG, " page_id=\"%s\"\n", page_id);
235 if (page_id == NULL)
236 page_id = "//index";
238 handled =
239 YELP_DOCUMENT_CLASS (yelp_docbook_document_parent_class)->request_page (document,
240 page_id,
241 cancellable,
242 callback,
243 user_data);
244 if (handled) {
245 return;
248 g_mutex_lock (priv->mutex);
250 switch (priv->state) {
251 case DOCBOOK_STATE_BLANK:
252 priv->state = DOCBOOK_STATE_PARSING;
253 priv->process_running = TRUE;
254 g_object_ref (document);
255 priv->thread = g_thread_create ((GThreadFunc) docbook_process,
256 document, FALSE, NULL);
257 break;
258 case DOCBOOK_STATE_PARSING:
259 break;
260 case DOCBOOK_STATE_PARSED:
261 case DOCBOOK_STATE_STOP:
262 docuri = yelp_uri_get_document_uri (priv->uri);
263 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
264 _("The page ā€˜%sā€™ was not found in the document ā€˜%sā€™."),
265 page_id, docuri);
266 g_free (docuri);
267 yelp_document_signal (document, page_id,
268 YELP_DOCUMENT_SIGNAL_ERROR,
269 error);
270 g_error_free (error);
271 break;
274 g_mutex_unlock (priv->mutex);
277 /******************************************************************************/
279 static void
280 docbook_process (YelpDocbookDocument *docbook)
282 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
283 YelpDocument *document = YELP_DOCUMENT (docbook);
284 GFile *file = NULL;
285 gchar *filepath = NULL;
286 xmlDocPtr xmldoc = NULL;
287 xmlChar *id = NULL;
288 xmlParserCtxtPtr parserCtxt = NULL;
289 GError *error;
290 gint params_i = 0;
291 gchar **params = NULL;
293 debug_print (DB_FUNCTION, "entering\n");
295 file = yelp_uri_get_file (priv->uri);
296 if (file == NULL) {
297 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
298 _("The file does not exist."));
299 yelp_document_error_pending (document, error);
300 g_error_free (error);
301 goto done;
304 filepath = g_file_get_path (file);
305 g_object_unref (file);
306 if (!g_file_test (filepath, G_FILE_TEST_IS_REGULAR)) {
307 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
308 _("The file ā€˜%sā€™ does not exist."),
309 filepath);
310 yelp_document_error_pending (document, error);
311 g_error_free (error);
312 goto done;
315 parserCtxt = xmlNewParserCtxt ();
316 xmldoc = xmlCtxtReadFile (parserCtxt,
317 filepath, NULL,
318 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
319 XML_PARSE_NOENT | XML_PARSE_NONET );
321 if (xmldoc == NULL) {
322 error = g_error_new (YELP_ERROR, YELP_ERROR_PROCESSING,
323 _("The file ā€˜%sā€™ could not be parsed because it is"
324 " not a well-formed XML document."),
325 filepath);
326 yelp_document_error_pending (document, error);
327 g_error_free (error);
328 goto done;
331 if (xmlXIncludeProcessFlags (xmldoc,
332 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
333 XML_PARSE_NOENT | XML_PARSE_NONET )
334 < 0) {
335 error = g_error_new (YELP_ERROR, YELP_ERROR_PROCESSING,
336 _("The file ā€˜%sā€™ could not be parsed because"
337 " one or more of its included files is not"
338 " a well-formed XML document."),
339 filepath);
340 yelp_document_error_pending (document, error);
341 g_error_free (error);
342 goto done;
345 g_mutex_lock (priv->mutex);
346 if (!xmlStrcmp (xmlDocGetRootElement (xmldoc)->name, BAD_CAST "book"))
347 priv->max_depth = 2;
348 else
349 priv->max_depth = 1;
351 priv->xmldoc = xmldoc;
352 priv->xmlcur = xmlDocGetRootElement (xmldoc);
354 id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
355 if (!id)
356 id = xmlGetNsProp (priv->xmlcur, XML_XML_NAMESPACE, BAD_CAST "id");
358 if (id) {
359 priv->root_id = g_strdup (id);
360 yelp_document_set_page_id (document, NULL, (gchar *) id);
361 yelp_document_set_page_id (document, "//index", (gchar *) id);
362 yelp_document_set_prev_id (document, (gchar *) id, "//about");
363 yelp_document_set_next_id (document, "//about", (gchar *) id);
365 else {
366 priv->root_id = g_strdup ("//index");
367 yelp_document_set_page_id (document, NULL, "//index");
368 yelp_document_set_prev_id (document, "//index", "//about");
369 yelp_document_set_next_id (document, "//about", "//index");
370 /* add the id attribute to the root element with value "index"
371 * so when we try to load the document later, it doesn't fail */
372 if (priv->xmlcur->ns)
373 xmlNewNsProp (priv->xmlcur,
374 xmlNewNs (priv->xmlcur, XML_XML_NAMESPACE, BAD_CAST "xml"),
375 BAD_CAST "id", BAD_CAST "//index");
376 else
377 xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST "//index");
379 yelp_document_set_root_id (document, priv->root_id, priv->root_id);
380 yelp_document_set_root_id (document, "//about", priv->root_id);
381 g_mutex_unlock (priv->mutex);
383 g_mutex_lock (priv->mutex);
384 if (priv->state == DOCBOOK_STATE_STOP) {
385 g_mutex_unlock (priv->mutex);
386 goto done;
388 g_mutex_unlock (priv->mutex);
390 docbook_walk (docbook);
392 g_mutex_lock (priv->mutex);
393 if (priv->state == DOCBOOK_STATE_STOP) {
394 g_mutex_unlock (priv->mutex);
395 goto done;
398 priv->transform = yelp_transform_new (STYLESHEET);
399 priv->chunk_ready =
400 g_signal_connect (priv->transform, "chunk-ready",
401 (GCallback) transform_chunk_ready,
402 docbook);
403 priv->finished =
404 g_signal_connect (priv->transform, "finished",
405 (GCallback) transform_finished,
406 docbook);
407 priv->error =
408 g_signal_connect (priv->transform, "error",
409 (GCallback) transform_error,
410 docbook);
412 params = yelp_settings_get_all_params (yelp_settings_get_default (), 2, &params_i);
413 params[params_i++] = g_strdup ("db.chunk.max_depth");
414 params[params_i++] = g_strdup_printf ("%i", priv->max_depth);
415 params[params_i] = NULL;
417 priv->transform_running = TRUE;
418 yelp_transform_start (priv->transform,
419 priv->xmldoc,
420 NULL,
421 (const gchar * const *) params);
422 g_strfreev (params);
423 g_mutex_unlock (priv->mutex);
425 done:
426 g_free (filepath);
427 if (id)
428 xmlFree (id);
429 if (parserCtxt)
430 xmlFreeParserCtxt (parserCtxt);
432 priv->process_running = FALSE;
433 g_object_unref (docbook);
436 static void
437 docbook_disconnect (YelpDocbookDocument *docbook)
439 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
440 if (priv->chunk_ready) {
441 g_signal_handler_disconnect (priv->transform, priv->chunk_ready);
442 priv->chunk_ready = 0;
444 if (priv->finished) {
445 g_signal_handler_disconnect (priv->transform, priv->finished);
446 priv->finished = 0;
448 if (priv->error) {
449 g_signal_handler_disconnect (priv->transform, priv->error);
450 priv->error = 0;
452 yelp_transform_cancel (priv->transform);
453 g_object_unref (priv->transform);
454 priv->transform = NULL;
455 priv->transform_running = FALSE;
458 /******************************************************************************/
460 static void
461 docbook_walk (YelpDocbookDocument *docbook)
463 static gint autoid = 0;
464 gchar autoidstr[20];
465 xmlChar *id = NULL;
466 xmlChar *title = NULL;
467 gchar *old_page_id = NULL;
468 xmlNodePtr cur, old_cur;
469 GtkTreeIter iter;
470 GtkTreeIter *old_iter = NULL;
471 gboolean chunkQ;
472 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
473 YelpDocument *document = YELP_DOCUMENT (docbook);
475 debug_print (DB_FUNCTION, "entering\n");
476 debug_print (DB_DEBUG, " priv->xmlcur->name: %s\n", priv->xmlcur->name);
478 /* Check for the db.chunk.max_depth PI and set max chunk depth */
479 if (priv->cur_depth == 0)
480 for (cur = priv->xmlcur; cur; cur = cur->prev)
481 if (cur->type == XML_PI_NODE)
482 if (!xmlStrcmp (cur->name, (const xmlChar *) "db.chunk.max_depth")) {
483 gint max = atoi ((gchar *) cur->content);
484 if (max)
485 priv->max_depth = max;
486 break;
489 id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
490 if (!id)
491 id = xmlGetNsProp (priv->xmlcur, XML_XML_NAMESPACE, BAD_CAST "id");
493 if (docbook_walk_divisionQ (docbook) && !id) {
494 /* If id attribute is not present, autogenerate a
495 * unique value, and insert it into the in-memory tree */
496 g_snprintf (autoidstr, 20, "//autoid-%d", ++autoid);
497 if (priv->xmlcur->ns) {
498 xmlNewNsProp (priv->xmlcur,
499 xmlNewNs (priv->xmlcur, XML_XML_NAMESPACE, BAD_CAST "xml"),
500 BAD_CAST "id", BAD_CAST autoidstr);
501 id = xmlGetNsProp (priv->xmlcur, XML_XML_NAMESPACE, BAD_CAST "id");
503 else {
504 xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST autoidstr);
505 id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
509 if (docbook_walk_chunkQ (docbook)) {
510 title = BAD_CAST docbook_walk_get_title (docbook);
512 debug_print (DB_DEBUG, " id: \"%s\"\n", id);
513 debug_print (DB_DEBUG, " title: \"%s\"\n", title);
515 yelp_document_set_page_title (document, (gchar *) id, (gchar *) title);
517 gdk_threads_enter ();
518 gtk_tree_store_append (GTK_TREE_STORE (priv->sections),
519 &iter,
520 priv->sections_iter);
521 gtk_tree_store_set (GTK_TREE_STORE (priv->sections),
522 &iter,
523 DOCBOOK_COLUMN_ID, id,
524 DOCBOOK_COLUMN_TITLE, title,
525 -1);
526 gdk_threads_leave ();
528 if (priv->cur_prev_id) {
529 yelp_document_set_prev_id (document, (gchar *) id, priv->cur_prev_id);
530 yelp_document_set_next_id (document, priv->cur_prev_id, (gchar *) id);
531 g_free (priv->cur_prev_id);
533 priv->cur_prev_id = g_strdup ((gchar *) id);
535 if (priv->cur_page_id)
536 yelp_document_set_up_id (document, (gchar *) id, priv->cur_page_id);
537 old_page_id = priv->cur_page_id;
538 priv->cur_page_id = g_strdup ((gchar *) id);
540 old_iter = priv->sections_iter;
541 if (priv->xmlcur->parent->type != XML_DOCUMENT_NODE)
542 priv->sections_iter = &iter;
545 old_cur = priv->xmlcur;
546 priv->cur_depth++;
547 if (id) {
548 yelp_document_set_root_id (document, (gchar *) id, priv->root_id);
549 yelp_document_set_page_id (document, (gchar *) id, priv->cur_page_id);
552 chunkQ = docbook_walk_chunkQ (docbook);
553 if (chunkQ)
554 yelp_document_signal (YELP_DOCUMENT (docbook),
555 priv->cur_page_id,
556 YELP_DOCUMENT_SIGNAL_INFO,
557 NULL);
559 for (cur = priv->xmlcur->children; cur; cur = cur->next) {
560 if (cur->type == XML_ELEMENT_NODE) {
561 priv->xmlcur = cur;
562 docbook_walk (docbook);
565 priv->cur_depth--;
566 priv->xmlcur = old_cur;
568 if (chunkQ) {
569 priv->sections_iter = old_iter;
570 g_free (priv->cur_page_id);
571 priv->cur_page_id = old_page_id;
574 if (priv->cur_depth == 0) {
575 g_free (priv->cur_prev_id);
576 priv->cur_prev_id = NULL;
578 g_free (priv->cur_page_id);
579 priv->cur_page_id = NULL;
582 if (id != NULL)
583 xmlFree (id);
584 if (title != NULL)
585 xmlFree (title);
588 static gboolean
589 docbook_walk_chunkQ (YelpDocbookDocument *docbook)
591 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
592 if (priv->cur_depth <= priv->max_depth)
593 return docbook_walk_divisionQ (docbook);
594 else
595 return FALSE;
598 static gboolean
599 docbook_walk_divisionQ (YelpDocbookDocument *docbook)
601 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
602 xmlNodePtr node = priv->xmlcur;
603 return (!xmlStrcmp (node->name, (const xmlChar *) "appendix") ||
604 !xmlStrcmp (node->name, (const xmlChar *) "article") ||
605 !xmlStrcmp (node->name, (const xmlChar *) "book") ||
606 !xmlStrcmp (node->name, (const xmlChar *) "bibliography") ||
607 !xmlStrcmp (node->name, (const xmlChar *) "chapter") ||
608 !xmlStrcmp (node->name, (const xmlChar *) "colophon") ||
609 !xmlStrcmp (node->name, (const xmlChar *) "glossary") ||
610 !xmlStrcmp (node->name, (const xmlChar *) "index") ||
611 !xmlStrcmp (node->name, (const xmlChar *) "part") ||
612 !xmlStrcmp (node->name, (const xmlChar *) "preface") ||
613 !xmlStrcmp (node->name, (const xmlChar *) "reference") ||
614 !xmlStrcmp (node->name, (const xmlChar *) "refentry") ||
615 !xmlStrcmp (node->name, (const xmlChar *) "refsect1") ||
616 !xmlStrcmp (node->name, (const xmlChar *) "refsect2") ||
617 !xmlStrcmp (node->name, (const xmlChar *) "refsect3") ||
618 !xmlStrcmp (node->name, (const xmlChar *) "refsection") ||
619 !xmlStrcmp (node->name, (const xmlChar *) "sect1") ||
620 !xmlStrcmp (node->name, (const xmlChar *) "sect2") ||
621 !xmlStrcmp (node->name, (const xmlChar *) "sect3") ||
622 !xmlStrcmp (node->name, (const xmlChar *) "sect4") ||
623 !xmlStrcmp (node->name, (const xmlChar *) "sect5") ||
624 !xmlStrcmp (node->name, (const xmlChar *) "section") ||
625 !xmlStrcmp (node->name, (const xmlChar *) "set") ||
626 !xmlStrcmp (node->name, (const xmlChar *) "setindex") ||
627 !xmlStrcmp (node->name, (const xmlChar *) "simplesect") );
630 static gchar *
631 docbook_walk_get_title (YelpDocbookDocument *docbook)
633 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
634 gchar *infoname = NULL;
635 xmlNodePtr child = NULL;
636 xmlNodePtr title = NULL;
637 xmlNodePtr title_tmp = NULL;
639 if (!xmlStrcmp (priv->xmlcur->name, BAD_CAST "refentry")) {
640 /* The title for a refentry element can come from the following:
641 * refmeta/refentrytitle
642 * refentryinfo/title[abbrev]
643 * refnamediv/refname
644 * We take the first one we find.
646 for (child = priv->xmlcur->children; child; child = child->next) {
647 if (!xmlStrcmp (child->name, BAD_CAST "refmeta")) {
648 for (title = child->children; title; title = title->next) {
649 if (!xmlStrcmp (title->name, BAD_CAST "refentrytitle"))
650 break;
652 if (title)
653 goto done;
655 else if (!xmlStrcmp (child->name, BAD_CAST "refentryinfo")) {
656 for (title = child->children; title; title = title->next) {
657 if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev"))
658 break;
659 else if (!xmlStrcmp (title->name, BAD_CAST "title"))
660 title_tmp = title;
662 if (title)
663 goto done;
664 else if (title_tmp) {
665 title = title_tmp;
666 goto done;
669 else if (!xmlStrcmp (child->name, BAD_CAST "refnamediv")) {
670 for (title = child->children; title; title = title->next) {
671 if (!xmlStrcmp (title->name, BAD_CAST "refname"))
672 break;
673 else if (!xmlStrcmp (title->name, BAD_CAST "refpurpose")) {
674 title = NULL;
675 break;
678 if (title)
679 goto done;
681 else if (!xmlStrncmp (child->name, BAD_CAST "refsect", 7))
682 break;
685 else {
686 /* The title for other elements appears in the following:
687 * title[abbrev]
688 * *info/title[abbrev]
689 * blockinfo/title[abbrev]
690 * objectinfo/title[abbrev]
691 * We take them in that order.
693 xmlNodePtr infos[3] = {NULL, NULL, NULL};
694 int i;
696 infoname = g_strdup_printf ("%sinfo", priv->xmlcur->name);
698 for (child = priv->xmlcur->children; child; child = child->next) {
699 if (!xmlStrcmp (child->name, BAD_CAST "titleabbrev")) {
700 title = child;
701 goto done;
703 else if (!xmlStrcmp (child->name, BAD_CAST "title"))
704 title_tmp = child;
705 else if (!xmlStrcmp (child->name, BAD_CAST "info"))
706 infos[0] = child;
707 else if (!xmlStrcmp (child->name, BAD_CAST infoname))
708 infos[0] = child;
709 else if (!xmlStrcmp (child->name, BAD_CAST "blockinfo"))
710 infos[1] = child;
711 else if (!xmlStrcmp (child->name, BAD_CAST "objectinfo"))
712 infos[2] = child;
715 if (title_tmp) {
716 title = title_tmp;
717 goto done;
720 for (i = 0; i < 3; i++) {
721 child = infos[i];
722 if (child) {
723 for (title = child->children; title; title = title->next) {
724 if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev"))
725 goto done;
726 else if (!xmlStrcmp (title->name, BAD_CAST "title"))
727 title_tmp = title;
729 if (title_tmp) {
730 title = title_tmp;
731 goto done;
737 done:
738 g_free (infoname);
740 if (title)
741 return (gchar *) xmlNodeGetContent (title);
742 else
743 return g_strdup (_("Unknown"));
746 /******************************************************************************/
748 static void
749 transform_chunk_ready (YelpTransform *transform,
750 gchar *chunk_id,
751 YelpDocbookDocument *docbook)
753 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
754 gchar *content;
756 debug_print (DB_FUNCTION, "entering\n");
757 g_assert (transform == priv->transform);
759 if (priv->state == DOCBOOK_STATE_STOP) {
760 docbook_disconnect (docbook);
761 return;
764 content = yelp_transform_take_chunk (transform, chunk_id);
765 yelp_document_give_contents (YELP_DOCUMENT (docbook),
766 chunk_id,
767 content,
768 "application/xhtml+xml");
770 yelp_document_signal (YELP_DOCUMENT (docbook),
771 chunk_id,
772 YELP_DOCUMENT_SIGNAL_CONTENTS,
773 NULL);
776 static void
777 transform_finished (YelpTransform *transform,
778 YelpDocbookDocument *docbook)
780 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
781 gchar *docuri;
782 GError *error;
784 debug_print (DB_FUNCTION, "entering\n");
785 g_assert (transform == priv->transform);
787 if (priv->state == DOCBOOK_STATE_STOP) {
788 docbook_disconnect (docbook);
789 return;
792 docbook_disconnect (docbook);
794 /* We want to free priv->xmldoc, but we can't free it before transform
795 is finalized. Otherwise, we could crash when YelpTransform frees
796 its libxslt resources.
798 g_object_weak_ref ((GObject *) transform,
799 (GWeakNotify) transform_finalized,
800 docbook);
802 docuri = yelp_uri_get_document_uri (priv->uri);
803 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
804 _("The requested page was not found in the document ā€˜%sā€™."),
805 docuri);
806 g_free (docuri);
807 yelp_document_error_pending ((YelpDocument *) docbook, error);
808 g_error_free (error);
811 static void
812 transform_error (YelpTransform *transform,
813 YelpDocbookDocument *docbook)
815 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
816 GError *error;
818 debug_print (DB_FUNCTION, "entering\n");
819 g_assert (transform == priv->transform);
821 if (priv->state == DOCBOOK_STATE_STOP) {
822 docbook_disconnect (docbook);
823 return;
826 error = yelp_transform_get_error (transform);
827 yelp_document_error_pending ((YelpDocument *) docbook, error);
828 g_error_free (error);
830 docbook_disconnect (docbook);
833 static void
834 transform_finalized (YelpDocbookDocument *docbook,
835 gpointer transform)
837 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
839 debug_print (DB_FUNCTION, "entering\n");
841 if (priv->xmldoc)
842 xmlFreeDoc (priv->xmldoc);
843 priv->xmldoc = NULL;