yelp-document: Fix return type of document_indexed
[yelp.git] / libyelp / yelp-docbook-document.c
blob4ac09a97d3b2db20a56a96b49b588303ccc2a2c4
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, see <http://www.gnu.org/licenses/>.
18 * Author: Shaun McCance <shaunm@gnome.org>
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <glib.h>
26 #include <glib/gi18n.h>
27 #include <gtk/gtk.h>
28 #include <libxml/parser.h>
29 #include <libxml/parserInternals.h>
30 #include <libxml/xinclude.h>
32 #include "yelp-docbook-document.h"
33 #include "yelp-error.h"
34 #include "yelp-settings.h"
35 #include "yelp-storage.h"
36 #include "yelp-transform.h"
37 #include "yelp-debug.h"
39 #define STYLESHEET DATADIR"/yelp/xslt/db2html.xsl"
40 #define DEFAULT_CATALOG "file:///etc/xml/catalog"
41 #define YELP_CATALOG "file://"DATADIR"/yelp/dtd/catalog"
43 typedef enum {
44 DOCBOOK_STATE_BLANK, /* Brand new, run transform as needed */
45 DOCBOOK_STATE_PARSING, /* Parsing/transforming document, please wait */
46 DOCBOOK_STATE_PARSED, /* All done, if we ain't got it, it ain't here */
47 DOCBOOK_STATE_STOP /* Stop everything now, object to be disposed */
48 } DocbookState;
50 enum {
51 DOCBOOK_COLUMN_ID,
52 DOCBOOK_COLUMN_TITLE
55 static void yelp_docbook_document_dispose (GObject *object);
56 static void yelp_docbook_document_finalize (GObject *object);
58 static void docbook_index (YelpDocument *document);
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);
67 static gboolean docbook_reload (YelpDocbookDocument *docbook);
68 static void docbook_monitor_changed (GFileMonitor *monitor,
69 GFile *file,
70 GFile *other_file,
71 GFileMonitorEvent event_type,
72 YelpDocbookDocument *docbook);
74 static void docbook_walk (YelpDocbookDocument *docbook);
75 static gboolean docbook_walk_chunkQ (YelpDocbookDocument *docbook,
76 xmlNodePtr cur,
77 gint depth,
78 gint max_depth);
79 static gboolean docbook_walk_divisionQ (YelpDocbookDocument *docbook,
80 xmlNodePtr cur);
81 static gchar * docbook_walk_get_title (YelpDocbookDocument *docbook,
82 xmlNodePtr cur);
84 static void transform_chunk_ready (YelpTransform *transform,
85 gchar *chunk_id,
86 YelpDocbookDocument *docbook);
87 static void transform_finished (YelpTransform *transform,
88 YelpDocbookDocument *docbook);
89 static void transform_error (YelpTransform *transform,
90 YelpDocbookDocument *docbook);
91 static void transform_finalized (YelpDocbookDocument *docbook,
92 gpointer transform);
94 G_DEFINE_TYPE (YelpDocbookDocument, yelp_docbook_document, YELP_TYPE_DOCUMENT)
95 #define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DOCBOOK_DOCUMENT, YelpDocbookDocumentPrivate))
97 typedef struct _YelpDocbookDocumentPrivate YelpDocbookDocumentPrivate;
98 struct _YelpDocbookDocumentPrivate {
99 DocbookState state;
101 GMutex mutex;
102 GThread *thread;
104 GThread *index;
105 gboolean index_running;
107 gboolean process_running;
108 gboolean transform_running;
110 YelpTransform *transform;
111 guint chunk_ready;
112 guint finished;
113 guint error;
115 xmlDocPtr xmldoc;
116 xmlNodePtr xmlcur;
117 gint max_depth;
118 gint cur_depth;
119 gchar *cur_page_id;
120 gchar *cur_prev_id;
121 gchar *root_id;
123 GFileMonitor **monitors;
124 gint64 reload_time;
127 /******************************************************************************/
129 static void
130 yelp_docbook_document_class_init (YelpDocbookDocumentClass *klass)
132 GObjectClass *object_class = G_OBJECT_CLASS (klass);
133 YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass);
134 const gchar *catalog = g_getenv ("XML_CATALOG_FILES");
136 /* We ship a faux DocBook catalog. It just contains the common entity
137 * definitions. Documents can use the named entities they expect to
138 * be able to use, but we don't have to depend on docbook-dtds.
140 if (catalog == NULL)
141 catalog = DEFAULT_CATALOG;
142 if (!strstr(catalog, YELP_CATALOG)) {
143 gchar *newcat = g_strconcat (YELP_CATALOG, " ", catalog, NULL);
144 g_setenv ("XML_CATALOG_FILES", newcat, TRUE);
145 g_free (newcat);
148 object_class->dispose = yelp_docbook_document_dispose;
149 object_class->finalize = yelp_docbook_document_finalize;
151 document_class->index = docbook_index;
152 document_class->request_page = docbook_request_page;
154 g_type_class_add_private (klass, sizeof (YelpDocbookDocumentPrivate));
157 static void
158 yelp_docbook_document_init (YelpDocbookDocument *docbook)
160 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
162 priv->state = DOCBOOK_STATE_BLANK;
164 g_mutex_init (&priv->mutex);
167 static void
168 yelp_docbook_document_dispose (GObject *object)
170 gint i;
171 YelpDocbookDocumentPrivate *priv = GET_PRIV (object);
173 if (priv->monitors != NULL) {
174 for (i = 0; priv->monitors[i]; i++) {
175 g_object_unref (priv->monitors[i]);
177 g_free (priv->monitors);
178 priv->monitors = NULL;
181 G_OBJECT_CLASS (yelp_docbook_document_parent_class)->dispose (object);
184 static void
185 yelp_docbook_document_finalize (GObject *object)
187 YelpDocbookDocumentPrivate *priv = GET_PRIV (object);
189 if (priv->xmldoc)
190 xmlFreeDoc (priv->xmldoc);
192 g_free (priv->cur_page_id);
193 g_free (priv->cur_prev_id);
194 g_free (priv->root_id);
196 g_mutex_clear (&priv->mutex);
198 G_OBJECT_CLASS (yelp_docbook_document_parent_class)->finalize (object);
201 /******************************************************************************/
203 YelpDocument *
204 yelp_docbook_document_new (YelpUri *uri)
206 YelpDocbookDocument *docbook;
207 YelpDocbookDocumentPrivate *priv;
208 gchar **path;
209 gint path_i;
211 g_return_val_if_fail (uri != NULL, NULL);
213 docbook = (YelpDocbookDocument *) g_object_new (YELP_TYPE_DOCBOOK_DOCUMENT,
214 "document-uri", uri,
215 NULL);
216 priv = GET_PRIV (docbook);
218 path = yelp_uri_get_search_path (uri);
219 priv->monitors = g_new0 (GFileMonitor*, g_strv_length (path) + 1);
220 for (path_i = 0; path[path_i]; path_i++) {
221 GFile *file;
222 file = g_file_new_for_path (path[path_i]);
223 priv->monitors[path_i] = g_file_monitor (file,
224 G_FILE_MONITOR_SEND_MOVED,
225 NULL, NULL);
226 g_signal_connect (priv->monitors[path_i], "changed",
227 G_CALLBACK (docbook_monitor_changed),
228 docbook);
229 g_object_unref (file);
231 g_strfreev (path);
232 return (YelpDocument *) docbook;
235 /******************************************************************************/
237 static gboolean
238 docbook_request_page (YelpDocument *document,
239 const gchar *page_id,
240 GCancellable *cancellable,
241 YelpDocumentCallback callback,
242 gpointer user_data)
244 YelpDocbookDocumentPrivate *priv = GET_PRIV (document);
245 gchar *docuri;
246 GError *error;
247 gboolean handled;
249 debug_print (DB_FUNCTION, "entering\n");
250 debug_print (DB_ARG, " page_id=\"%s\"\n", page_id);
252 if (page_id == NULL)
253 page_id = "//index";
255 handled =
256 YELP_DOCUMENT_CLASS (yelp_docbook_document_parent_class)->request_page (document,
257 page_id,
258 cancellable,
259 callback,
260 user_data);
261 if (handled) {
262 return handled;
265 g_mutex_lock (&priv->mutex);
267 switch (priv->state) {
268 case DOCBOOK_STATE_BLANK:
269 priv->state = DOCBOOK_STATE_PARSING;
270 priv->process_running = TRUE;
271 g_object_ref (document);
272 priv->thread = g_thread_new ("docbook-page",
273 (GThreadFunc) docbook_process,
274 document);
275 break;
276 case DOCBOOK_STATE_PARSING:
277 break;
278 case DOCBOOK_STATE_PARSED:
279 case DOCBOOK_STATE_STOP:
280 docuri = yelp_uri_get_document_uri (yelp_document_get_uri (document));
281 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
282 _("The page ā€˜%sā€™ was not found in the document ā€˜%sā€™."),
283 page_id, docuri);
284 g_free (docuri);
285 yelp_document_signal (document, page_id,
286 YELP_DOCUMENT_SIGNAL_ERROR,
287 error);
288 g_error_free (error);
289 break;
290 default:
291 g_assert_not_reached ();
292 break;
295 g_mutex_unlock (&priv->mutex);
296 return FALSE;
299 /******************************************************************************/
301 static void
302 docbook_process (YelpDocbookDocument *docbook)
304 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
305 YelpDocument *document = YELP_DOCUMENT (docbook);
306 GFile *file = NULL;
307 gchar *filepath = NULL;
308 xmlDocPtr xmldoc = NULL;
309 xmlChar *id = NULL;
310 xmlParserCtxtPtr parserCtxt = NULL;
311 GError *error;
312 gint params_i = 0;
313 gchar **params = NULL;
315 debug_print (DB_FUNCTION, "entering\n");
317 file = yelp_uri_get_file (yelp_document_get_uri (document));
318 if (file == NULL) {
319 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
320 _("The file does not exist."));
321 yelp_document_error_pending (document, error);
322 g_error_free (error);
323 goto done;
326 filepath = g_file_get_path (file);
327 g_object_unref (file);
328 if (!g_file_test (filepath, G_FILE_TEST_IS_REGULAR)) {
329 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
330 _("The file ā€˜%sā€™ does not exist."),
331 filepath);
332 yelp_document_error_pending (document, error);
333 g_error_free (error);
334 goto done;
337 parserCtxt = xmlNewParserCtxt ();
338 xmldoc = xmlCtxtReadFile (parserCtxt,
339 filepath, NULL,
340 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
341 XML_PARSE_NOENT | XML_PARSE_NONET );
343 if (xmldoc == NULL) {
344 error = g_error_new (YELP_ERROR, YELP_ERROR_PROCESSING,
345 _("The file ā€˜%sā€™ could not be parsed because it is"
346 " not a well-formed XML document."),
347 filepath);
348 yelp_document_error_pending (document, error);
349 g_error_free (error);
350 goto done;
353 if (xmlXIncludeProcessFlags (xmldoc,
354 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
355 XML_PARSE_NOENT | XML_PARSE_NONET )
356 < 0) {
357 error = g_error_new (YELP_ERROR, YELP_ERROR_PROCESSING,
358 _("The file ā€˜%sā€™ could not be parsed because"
359 " one or more of its included files is not"
360 " a well-formed XML document."),
361 filepath);
362 yelp_document_error_pending (document, error);
363 g_error_free (error);
364 goto done;
367 g_mutex_lock (&priv->mutex);
368 if (!xmlStrcmp (xmlDocGetRootElement (xmldoc)->name, BAD_CAST "book"))
369 priv->max_depth = 2;
370 else
371 priv->max_depth = 1;
373 priv->xmldoc = xmldoc;
374 priv->xmlcur = xmlDocGetRootElement (xmldoc);
376 id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
377 if (!id)
378 id = xmlGetNsProp (priv->xmlcur, XML_XML_NAMESPACE, BAD_CAST "id");
380 if (id) {
381 priv->root_id = g_strdup ((const gchar *) id);
382 yelp_document_set_page_id (document, NULL, (gchar *) id);
383 yelp_document_set_page_id (document, "//index", (gchar *) id);
385 else {
386 priv->root_id = g_strdup ("//index");
387 yelp_document_set_page_id (document, NULL, "//index");
388 /* add the id attribute to the root element with value "index"
389 * so when we try to load the document later, it doesn't fail */
390 if (priv->xmlcur->ns)
391 xmlNewNsProp (priv->xmlcur,
392 xmlNewNs (priv->xmlcur, XML_XML_NAMESPACE, BAD_CAST "xml"),
393 BAD_CAST "id", BAD_CAST "//index");
394 else
395 xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST "//index");
397 yelp_document_set_root_id (document, priv->root_id, priv->root_id);
398 g_mutex_unlock (&priv->mutex);
400 g_mutex_lock (&priv->mutex);
401 if (priv->state == DOCBOOK_STATE_STOP) {
402 g_mutex_unlock (&priv->mutex);
403 goto done;
405 g_mutex_unlock (&priv->mutex);
407 docbook_walk (docbook);
409 g_mutex_lock (&priv->mutex);
410 if (priv->state == DOCBOOK_STATE_STOP) {
411 g_mutex_unlock (&priv->mutex);
412 goto done;
415 priv->state = DOCBOOK_STATE_PARSED;
417 priv->transform = yelp_transform_new (STYLESHEET);
418 priv->chunk_ready =
419 g_signal_connect (priv->transform, "chunk-ready",
420 (GCallback) transform_chunk_ready,
421 docbook);
422 priv->finished =
423 g_signal_connect (priv->transform, "finished",
424 (GCallback) transform_finished,
425 docbook);
426 priv->error =
427 g_signal_connect (priv->transform, "error",
428 (GCallback) transform_error,
429 docbook);
431 params = yelp_settings_get_all_params (yelp_settings_get_default (), 2, &params_i);
432 params[params_i++] = g_strdup ("db.chunk.max_depth");
433 params[params_i++] = g_strdup_printf ("%i", priv->max_depth);
434 params[params_i] = NULL;
436 priv->transform_running = TRUE;
437 yelp_transform_start (priv->transform,
438 priv->xmldoc,
439 NULL,
440 (const gchar * const *) params);
441 g_strfreev (params);
442 g_mutex_unlock (&priv->mutex);
444 done:
445 g_free (filepath);
446 if (id)
447 xmlFree (id);
448 if (parserCtxt)
449 xmlFreeParserCtxt (parserCtxt);
451 priv->process_running = FALSE;
452 g_object_unref (docbook);
455 static void
456 docbook_disconnect (YelpDocbookDocument *docbook)
458 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
459 if (priv->chunk_ready) {
460 g_signal_handler_disconnect (priv->transform, priv->chunk_ready);
461 priv->chunk_ready = 0;
463 if (priv->finished) {
464 g_signal_handler_disconnect (priv->transform, priv->finished);
465 priv->finished = 0;
467 if (priv->error) {
468 g_signal_handler_disconnect (priv->transform, priv->error);
469 priv->error = 0;
471 yelp_transform_cancel (priv->transform);
472 g_object_unref (priv->transform);
473 priv->transform = NULL;
474 priv->transform_running = FALSE;
477 static gboolean
478 docbook_reload (YelpDocbookDocument *docbook)
480 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
482 if (priv->index_running || priv->process_running || priv->transform_running)
483 return TRUE;
485 g_mutex_lock (&priv->mutex);
487 priv->reload_time = g_get_monotonic_time();
489 yelp_document_clear_contents (YELP_DOCUMENT (docbook));
491 priv->state = DOCBOOK_STATE_PARSING;
492 priv->process_running = TRUE;
493 g_object_ref (docbook);
494 priv->thread = g_thread_new ("docbook-reload",
495 (GThreadFunc) docbook_process,
496 docbook);
498 g_mutex_unlock (&priv->mutex);
500 return FALSE;
503 static void
504 docbook_monitor_changed (GFileMonitor *monitor,
505 GFile *file,
506 GFile *other_file,
507 GFileMonitorEvent event_type,
508 YelpDocbookDocument *docbook)
510 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
512 if (g_get_monotonic_time() - priv->reload_time < 1000)
513 return;
515 if (priv->index_running || priv->process_running || priv->transform_running) {
516 g_timeout_add_seconds (1, (GSourceFunc) docbook_reload, docbook);
517 return;
520 docbook_reload (docbook);
523 /******************************************************************************/
525 static void
526 docbook_walk (YelpDocbookDocument *docbook)
528 static gint autoid = 0;
529 gchar autoidstr[20];
530 xmlChar *id = NULL;
531 xmlChar *title = NULL;
532 xmlNodePtr cur, old_cur;
533 gboolean chunkQ;
534 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
535 YelpDocument *document = YELP_DOCUMENT (docbook);
537 debug_print (DB_FUNCTION, "entering\n");
538 debug_print (DB_DEBUG, " priv->xmlcur->name: %s\n", priv->xmlcur->name);
540 /* Check for the db.chunk.max_depth PI and set max chunk depth */
541 if (priv->cur_depth == 0)
542 for (cur = priv->xmlcur; cur; cur = cur->prev)
543 if (cur->type == XML_PI_NODE)
544 if (!xmlStrcmp (cur->name, (const xmlChar *) "db.chunk.max_depth")) {
545 gint max = atoi ((gchar *) cur->content);
546 if (max)
547 priv->max_depth = max;
548 break;
551 id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
552 if (!id)
553 id = xmlGetNsProp (priv->xmlcur, XML_XML_NAMESPACE, BAD_CAST "id");
555 if (docbook_walk_divisionQ (docbook, priv->xmlcur) && !id) {
556 /* If id attribute is not present, autogenerate a
557 * unique value, and insert it into the in-memory tree */
558 g_snprintf (autoidstr, 20, "//autoid-%d", ++autoid);
559 if (priv->xmlcur->ns) {
560 xmlNewNsProp (priv->xmlcur,
561 xmlNewNs (priv->xmlcur, XML_XML_NAMESPACE, BAD_CAST "xml"),
562 BAD_CAST "id", BAD_CAST autoidstr);
563 id = xmlGetNsProp (priv->xmlcur, XML_XML_NAMESPACE, BAD_CAST "id");
565 else {
566 xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST autoidstr);
567 id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
571 if (docbook_walk_chunkQ (docbook, priv->xmlcur, priv->cur_depth, priv->max_depth)) {
572 title = BAD_CAST docbook_walk_get_title (docbook, priv->xmlcur);
574 debug_print (DB_DEBUG, " id: \"%s\"\n", id);
575 debug_print (DB_DEBUG, " title: \"%s\"\n", title);
577 yelp_document_set_page_title (document, (gchar *) id, (gchar *) title);
579 if (priv->cur_prev_id) {
580 yelp_document_set_prev_id (document, (gchar *) id, priv->cur_prev_id);
581 yelp_document_set_next_id (document, priv->cur_prev_id, (gchar *) id);
582 g_free (priv->cur_prev_id);
584 priv->cur_prev_id = g_strdup ((gchar *) id);
586 if (priv->cur_page_id)
587 yelp_document_set_up_id (document, (gchar *) id, priv->cur_page_id);
588 priv->cur_page_id = g_strdup ((gchar *) id);
591 old_cur = priv->xmlcur;
592 priv->cur_depth++;
593 if (id) {
594 yelp_document_set_root_id (document, (gchar *) id, priv->root_id);
595 yelp_document_set_page_id (document, (gchar *) id, priv->cur_page_id);
598 chunkQ = docbook_walk_chunkQ (docbook, priv->xmlcur, priv->cur_depth, priv->max_depth);
599 if (chunkQ)
600 yelp_document_signal (YELP_DOCUMENT (docbook),
601 priv->cur_page_id,
602 YELP_DOCUMENT_SIGNAL_INFO,
603 NULL);
605 for (cur = priv->xmlcur->children; cur; cur = cur->next) {
606 if (cur->type == XML_ELEMENT_NODE) {
607 priv->xmlcur = cur;
608 docbook_walk (docbook);
611 priv->cur_depth--;
612 priv->xmlcur = old_cur;
614 if (priv->cur_depth == 0) {
615 g_free (priv->cur_prev_id);
616 priv->cur_prev_id = NULL;
618 g_free (priv->cur_page_id);
619 priv->cur_page_id = NULL;
622 if (id != NULL)
623 xmlFree (id);
624 if (title != NULL)
625 xmlFree (title);
628 static gboolean
629 docbook_walk_chunkQ (YelpDocbookDocument *docbook,
630 xmlNodePtr cur,
631 gint cur_depth,
632 gint max_depth)
634 if (cur_depth <= max_depth)
635 return docbook_walk_divisionQ (docbook, cur);
636 else
637 return FALSE;
640 static gboolean
641 docbook_walk_divisionQ (YelpDocbookDocument *docbook, xmlNodePtr node)
643 return (!xmlStrcmp (node->name, (const xmlChar *) "appendix") ||
644 !xmlStrcmp (node->name, (const xmlChar *) "article") ||
645 !xmlStrcmp (node->name, (const xmlChar *) "book") ||
646 !xmlStrcmp (node->name, (const xmlChar *) "bibliography") ||
647 !xmlStrcmp (node->name, (const xmlChar *) "bibliodiv") ||
648 !xmlStrcmp (node->name, (const xmlChar *) "chapter") ||
649 !xmlStrcmp (node->name, (const xmlChar *) "colophon") ||
650 !xmlStrcmp (node->name, (const xmlChar *) "dedication") ||
651 !xmlStrcmp (node->name, (const xmlChar *) "glossary") ||
652 !xmlStrcmp (node->name, (const xmlChar *) "glossdiv") ||
653 !xmlStrcmp (node->name, (const xmlChar *) "lot") ||
654 !xmlStrcmp (node->name, (const xmlChar *) "index") ||
655 !xmlStrcmp (node->name, (const xmlChar *) "part") ||
656 !xmlStrcmp (node->name, (const xmlChar *) "preface") ||
657 !xmlStrcmp (node->name, (const xmlChar *) "reference") ||
658 !xmlStrcmp (node->name, (const xmlChar *) "refentry") ||
659 !xmlStrcmp (node->name, (const xmlChar *) "sect1") ||
660 !xmlStrcmp (node->name, (const xmlChar *) "sect2") ||
661 !xmlStrcmp (node->name, (const xmlChar *) "sect3") ||
662 !xmlStrcmp (node->name, (const xmlChar *) "sect4") ||
663 !xmlStrcmp (node->name, (const xmlChar *) "sect5") ||
664 !xmlStrcmp (node->name, (const xmlChar *) "section") ||
665 !xmlStrcmp (node->name, (const xmlChar *) "set") ||
666 !xmlStrcmp (node->name, (const xmlChar *) "setindex") ||
667 !xmlStrcmp (node->name, (const xmlChar *) "simplesect") ||
668 !xmlStrcmp (node->name, (const xmlChar *) "toc") );
671 static gchar *
672 docbook_walk_get_title (YelpDocbookDocument *docbook,
673 xmlNodePtr cur)
675 gchar *infoname = NULL;
676 xmlNodePtr child = NULL;
677 xmlNodePtr title = NULL;
678 xmlNodePtr title_tmp = NULL;
680 if (!xmlStrcmp (cur->name, BAD_CAST "refentry")) {
681 /* The title for a refentry element can come from the following:
682 * refmeta/refentrytitle
683 * refentryinfo/title[abbrev]
684 * refnamediv/refname
685 * We take the first one we find.
687 for (child = cur->children; child; child = child->next) {
688 if (!xmlStrcmp (child->name, BAD_CAST "refmeta")) {
689 for (title = child->children; title; title = title->next) {
690 if (!xmlStrcmp (title->name, BAD_CAST "refentrytitle"))
691 break;
693 if (title)
694 goto done;
696 else if (!xmlStrcmp (child->name, BAD_CAST "refentryinfo")) {
697 for (title = child->children; title; title = title->next) {
698 if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev"))
699 break;
700 else if (!xmlStrcmp (title->name, BAD_CAST "title"))
701 title_tmp = title;
703 if (title)
704 goto done;
705 else if (title_tmp) {
706 title = title_tmp;
707 goto done;
710 else if (!xmlStrcmp (child->name, BAD_CAST "refnamediv")) {
711 for (title = child->children; title; title = title->next) {
712 if (!xmlStrcmp (title->name, BAD_CAST "refname"))
713 break;
714 else if (!xmlStrcmp (title->name, BAD_CAST "refpurpose")) {
715 title = NULL;
716 break;
719 if (title)
720 goto done;
722 else if (!xmlStrncmp (child->name, BAD_CAST "refsect", 7))
723 break;
726 else {
727 /* The title for other elements appears in the following:
728 * title[abbrev]
729 * *info/title[abbrev]
730 * blockinfo/title[abbrev]
731 * objectinfo/title[abbrev]
732 * We take them in that order.
734 xmlNodePtr infos[3] = {NULL, NULL, NULL};
735 int i;
737 infoname = g_strdup_printf ("%sinfo", cur->name);
739 for (child = cur->children; child; child = child->next) {
740 if (!xmlStrcmp (child->name, BAD_CAST "titleabbrev")) {
741 title = child;
742 goto done;
744 else if (!xmlStrcmp (child->name, BAD_CAST "title"))
745 title_tmp = child;
746 else if (!xmlStrcmp (child->name, BAD_CAST "info"))
747 infos[0] = child;
748 else if (!xmlStrcmp (child->name, BAD_CAST infoname))
749 infos[0] = child;
750 else if (!xmlStrcmp (child->name, BAD_CAST "blockinfo"))
751 infos[1] = child;
752 else if (!xmlStrcmp (child->name, BAD_CAST "objectinfo"))
753 infos[2] = child;
756 if (title_tmp) {
757 title = title_tmp;
758 goto done;
761 for (i = 0; i < 3; i++) {
762 child = infos[i];
763 if (child) {
764 for (title = child->children; title; title = title->next) {
765 if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev"))
766 goto done;
767 else if (!xmlStrcmp (title->name, BAD_CAST "title"))
768 title_tmp = title;
770 if (title_tmp) {
771 title = title_tmp;
772 goto done;
778 done:
779 g_free (infoname);
781 if (title) {
782 xmlChar *title_s = xmlNodeGetContent (title);
783 gchar *ret = g_strdup ((const gchar *) title_s);
784 xmlFree (title_s);
785 return ret;
787 else
788 return g_strdup (_("Unknown"));
791 /******************************************************************************/
793 static void
794 transform_chunk_ready (YelpTransform *transform,
795 gchar *chunk_id,
796 YelpDocbookDocument *docbook)
798 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
799 gchar *content;
801 debug_print (DB_FUNCTION, "entering\n");
802 g_assert (transform == priv->transform);
804 if (priv->state == DOCBOOK_STATE_STOP) {
805 docbook_disconnect (docbook);
806 return;
809 content = yelp_transform_take_chunk (transform, chunk_id);
810 yelp_document_give_contents (YELP_DOCUMENT (docbook),
811 chunk_id,
812 content,
813 "application/xhtml+xml");
815 yelp_document_signal (YELP_DOCUMENT (docbook),
816 chunk_id,
817 YELP_DOCUMENT_SIGNAL_CONTENTS,
818 NULL);
821 static void
822 transform_finished (YelpTransform *transform,
823 YelpDocbookDocument *docbook)
825 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
826 YelpDocument *document = YELP_DOCUMENT (docbook);
827 gchar *docuri;
828 GError *error;
830 debug_print (DB_FUNCTION, "entering\n");
831 g_assert (transform == priv->transform);
833 if (priv->state == DOCBOOK_STATE_STOP) {
834 docbook_disconnect (docbook);
835 return;
838 docbook_disconnect (docbook);
840 /* We want to free priv->xmldoc, but we can't free it before transform
841 is finalized. Otherwise, we could crash when YelpTransform frees
842 its libxslt resources.
844 g_object_weak_ref ((GObject *) transform,
845 (GWeakNotify) transform_finalized,
846 docbook);
848 docuri = yelp_uri_get_document_uri (yelp_document_get_uri (document));
849 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
850 _("The requested page was not found in the document ā€˜%sā€™."),
851 docuri);
852 g_free (docuri);
853 yelp_document_error_pending ((YelpDocument *) docbook, error);
854 g_error_free (error);
857 static void
858 transform_error (YelpTransform *transform,
859 YelpDocbookDocument *docbook)
861 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
862 GError *error;
864 debug_print (DB_FUNCTION, "entering\n");
865 g_assert (transform == priv->transform);
867 if (priv->state == DOCBOOK_STATE_STOP) {
868 docbook_disconnect (docbook);
869 return;
872 error = yelp_transform_get_error (transform);
873 yelp_document_error_pending ((YelpDocument *) docbook, error);
874 g_error_free (error);
876 docbook_disconnect (docbook);
879 static void
880 transform_finalized (YelpDocbookDocument *docbook,
881 gpointer transform)
883 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
885 debug_print (DB_FUNCTION, "entering\n");
887 if (priv->xmldoc)
888 xmlFreeDoc (priv->xmldoc);
889 priv->xmldoc = NULL;
892 /******************************************************************************/
894 static gboolean
895 docbook_index_done (YelpDocbookDocument *docbook)
897 g_object_set (docbook, "indexed", TRUE, NULL);
898 g_object_unref (docbook);
899 return FALSE;
902 typedef struct {
903 YelpDocbookDocument *docbook;
904 xmlDocPtr doc;
905 xmlNodePtr cur;
906 gchar *doc_uri;
907 GString *str;
908 gint depth;
909 gint max_depth;
910 } DocbookIndexData;
912 static void
913 docbook_index_node (DocbookIndexData *index)
915 xmlNodePtr oldcur, child;
917 if ((g_str_equal (index->cur->parent->name, "menuchoice") ||
918 g_str_equal (index->cur->parent->name, "keycombo")) &&
919 index->cur->prev != NULL) {
920 g_string_append_c (index->str, ' ');
922 if (index->cur->type == XML_TEXT_NODE) {
923 g_string_append (index->str, (const gchar *) index->cur->content);
924 return;
926 if (index->cur->type != XML_ELEMENT_NODE ||
927 g_str_has_suffix ((const gchar *) index->cur->name, "info") ||
928 g_str_equal (index->cur->name, "remark"))
929 return;
930 oldcur = index->cur;
931 for (child = index->cur->children; child; child = child->next) {
932 index->cur = child;
933 docbook_index_node (index);
934 index->cur = oldcur;
938 static void
939 docbook_index_chunk (DocbookIndexData *index)
941 xmlChar *id;
942 xmlNodePtr child;
943 gchar *title = NULL;
944 GSList *chunks = NULL;
946 id = xmlGetProp (index->cur, BAD_CAST "id");
947 if (id != NULL) {
948 title = docbook_walk_get_title (index->docbook, index->cur);
949 if (index->cur->parent->parent == NULL)
950 yelp_storage_set_root_title (yelp_storage_get_default (),
951 index->doc_uri, title);
952 index->str = g_string_new ("");
955 for (child = index->cur->children; child; child = child->next) {
956 if (docbook_walk_chunkQ (index->docbook, child, index->depth, index->max_depth)) {
957 chunks = g_slist_append (chunks, child);
959 else if (id != NULL) {
960 xmlNodePtr oldcur = index->cur;
961 index->cur = child;
962 docbook_index_node (index);
963 index->cur = oldcur;
967 if (id != NULL) {
968 YelpDocument *document = YELP_DOCUMENT (index->docbook);
969 YelpUri *uri;
970 gchar *full_uri, *tmp, *body;
972 body = g_string_free (index->str, FALSE);
973 index->str = NULL;
975 tmp = g_strconcat ("xref:", id, NULL);
976 uri = yelp_uri_new_relative (yelp_document_get_uri (document), tmp);
977 g_free (tmp);
978 yelp_uri_resolve_sync (uri);
979 full_uri = yelp_uri_get_canonical_uri (uri);
980 g_object_unref (uri);
982 yelp_storage_update (yelp_storage_get_default (),
983 index->doc_uri, full_uri,
984 title, "", "yelp-page-symbolic",
985 body);
986 if (index->cur->parent->parent == NULL)
987 yelp_storage_set_root_title (yelp_storage_get_default (),
988 index->doc_uri, title);
989 g_free (full_uri);
990 g_free (body);
991 g_free (title);
992 xmlFree (id);
995 index->depth++;
996 while (chunks != NULL) {
997 xmlNodePtr oldcur = index->cur;
998 index->cur = (xmlNodePtr) chunks->data;
999 docbook_index_chunk(index);
1000 index->cur = oldcur;
1001 chunks = g_slist_delete_link (chunks, chunks);
1003 index->depth--;
1006 static void
1007 docbook_index_threaded (YelpDocbookDocument *docbook)
1009 DocbookIndexData *index = NULL;
1010 xmlParserCtxtPtr parserCtxt = NULL;
1011 GFile *file = NULL;
1012 gchar *filename = NULL;
1013 YelpUri *uri;
1014 YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
1016 uri = yelp_document_get_uri (YELP_DOCUMENT (docbook));
1017 file = yelp_uri_get_file (uri);
1018 if (file == NULL)
1019 goto done;
1020 filename = g_file_get_path (file);
1022 index = g_new0 (DocbookIndexData, 1);
1023 index->docbook = docbook;
1024 index->doc_uri = yelp_uri_get_document_uri (uri);
1026 parserCtxt = xmlNewParserCtxt ();
1027 index->doc = xmlCtxtReadFile (parserCtxt, filename, NULL,
1028 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
1029 XML_PARSE_NOENT | XML_PARSE_NONET );
1030 if (index->doc == NULL)
1031 goto done;
1032 if (xmlXIncludeProcessFlags (index->doc,
1033 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
1034 XML_PARSE_NOENT | XML_PARSE_NONET )
1035 < 0)
1036 goto done;
1038 index->cur = xmlDocGetRootElement (index->doc);
1039 index->depth = 0;
1040 if (!xmlStrcmp (index->cur->name, BAD_CAST "book"))
1041 index->max_depth = 2;
1042 else
1043 index->max_depth = 1;
1044 docbook_index_chunk (index);
1046 done:
1047 if (file != NULL)
1048 g_object_unref (file);
1049 if (filename != NULL)
1050 g_free (filename);
1051 if (index->doc != NULL)
1052 xmlFreeDoc (index->doc);
1053 if (index->doc_uri != NULL)
1054 g_free (index->doc_uri);
1055 if (index != NULL)
1056 g_free (index);
1057 if (parserCtxt != NULL)
1058 xmlFreeParserCtxt (parserCtxt);
1060 priv->index_running = FALSE;
1061 g_idle_add ((GSourceFunc) docbook_index_done, docbook);
1064 static void
1065 docbook_index (YelpDocument *document)
1067 YelpDocbookDocumentPrivate *priv;
1068 gboolean done;
1070 g_object_get (document, "indexed", &done, NULL);
1071 if (done)
1072 return;
1074 priv = GET_PRIV (document);
1075 g_object_ref (document);
1076 priv->index = g_thread_new ("docbook-index",
1077 (GThreadFunc) docbook_index_threaded,
1078 document);
1079 priv->index_running = TRUE;