1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
26 #include <glib/gi18n.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"
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 */
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
,
61 GCancellable
*cancellable
,
62 YelpDocumentCallback callback
,
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
,
71 GFileMonitorEvent event_type
,
72 YelpDocbookDocument
*docbook
);
74 static void docbook_walk (YelpDocbookDocument
*docbook
);
75 static gboolean
docbook_walk_chunkQ (YelpDocbookDocument
*docbook
,
79 static gboolean
docbook_walk_divisionQ (YelpDocbookDocument
*docbook
,
81 static gchar
* docbook_walk_get_title (YelpDocbookDocument
*docbook
,
84 static void transform_chunk_ready (YelpTransform
*transform
,
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
,
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
{
105 gboolean index_running
;
107 gboolean process_running
;
108 gboolean transform_running
;
110 YelpTransform
*transform
;
123 GFileMonitor
**monitors
;
127 /******************************************************************************/
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.
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
);
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
));
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
);
168 yelp_docbook_document_dispose (GObject
*object
)
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
);
185 yelp_docbook_document_finalize (GObject
*object
)
187 YelpDocbookDocumentPrivate
*priv
= GET_PRIV (object
);
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 /******************************************************************************/
204 yelp_docbook_document_new (YelpUri
*uri
)
206 YelpDocbookDocument
*docbook
;
207 YelpDocbookDocumentPrivate
*priv
;
211 g_return_val_if_fail (uri
!= NULL
, NULL
);
213 docbook
= (YelpDocbookDocument
*) g_object_new (YELP_TYPE_DOCBOOK_DOCUMENT
,
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
++) {
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
,
226 g_signal_connect (priv
->monitors
[path_i
], "changed",
227 G_CALLBACK (docbook_monitor_changed
),
229 g_object_unref (file
);
232 return (YelpDocument
*) docbook
;
235 /******************************************************************************/
238 docbook_request_page (YelpDocument
*document
,
239 const gchar
*page_id
,
240 GCancellable
*cancellable
,
241 YelpDocumentCallback callback
,
244 YelpDocbookDocumentPrivate
*priv
= GET_PRIV (document
);
249 debug_print (DB_FUNCTION
, "entering\n");
250 debug_print (DB_ARG
, " page_id=\"%s\"\n", page_id
);
256 YELP_DOCUMENT_CLASS (yelp_docbook_document_parent_class
)->request_page (document
,
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
,
276 case DOCBOOK_STATE_PARSING
:
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ā."),
285 yelp_document_signal (document
, page_id
,
286 YELP_DOCUMENT_SIGNAL_ERROR
,
288 g_error_free (error
);
291 g_assert_not_reached ();
295 g_mutex_unlock (&priv
->mutex
);
299 /******************************************************************************/
302 docbook_process (YelpDocbookDocument
*docbook
)
304 YelpDocbookDocumentPrivate
*priv
= GET_PRIV (docbook
);
305 YelpDocument
*document
= YELP_DOCUMENT (docbook
);
307 gchar
*filepath
= NULL
;
308 xmlDocPtr xmldoc
= NULL
;
310 xmlParserCtxtPtr parserCtxt
= NULL
;
313 gchar
**params
= NULL
;
315 debug_print (DB_FUNCTION
, "entering\n");
317 file
= yelp_uri_get_file (yelp_document_get_uri (document
));
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
);
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."),
332 yelp_document_error_pending (document
, error
);
333 g_error_free (error
);
337 parserCtxt
= xmlNewParserCtxt ();
338 xmldoc
= xmlCtxtReadFile (parserCtxt
,
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."),
348 yelp_document_error_pending (document
, error
);
349 g_error_free (error
);
353 if (xmlXIncludeProcessFlags (xmldoc
,
354 XML_PARSE_DTDLOAD
| XML_PARSE_NOCDATA
|
355 XML_PARSE_NOENT
| XML_PARSE_NONET
)
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."),
362 yelp_document_error_pending (document
, error
);
363 g_error_free (error
);
367 g_mutex_lock (&priv
->mutex
);
368 if (!xmlStrcmp (xmlDocGetRootElement (xmldoc
)->name
, BAD_CAST
"book"))
373 priv
->xmldoc
= xmldoc
;
374 priv
->xmlcur
= xmlDocGetRootElement (xmldoc
);
376 id
= xmlGetProp (priv
->xmlcur
, BAD_CAST
"id");
378 id
= xmlGetNsProp (priv
->xmlcur
, XML_XML_NAMESPACE
, BAD_CAST
"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
);
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");
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
);
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
);
415 priv
->state
= DOCBOOK_STATE_PARSED
;
417 priv
->transform
= yelp_transform_new (STYLESHEET
);
419 g_signal_connect (priv
->transform
, "chunk-ready",
420 (GCallback
) transform_chunk_ready
,
423 g_signal_connect (priv
->transform
, "finished",
424 (GCallback
) transform_finished
,
427 g_signal_connect (priv
->transform
, "error",
428 (GCallback
) transform_error
,
431 params
= yelp_settings_get_all_params (yelp_settings_get_default (), 2, ¶ms_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
,
440 (const gchar
* const *) params
);
442 g_mutex_unlock (&priv
->mutex
);
449 xmlFreeParserCtxt (parserCtxt
);
451 priv
->process_running
= FALSE
;
452 g_object_unref (docbook
);
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
);
468 g_signal_handler_disconnect (priv
->transform
, priv
->error
);
471 yelp_transform_cancel (priv
->transform
);
472 g_object_unref (priv
->transform
);
473 priv
->transform
= NULL
;
474 priv
->transform_running
= FALSE
;
478 docbook_reload (YelpDocbookDocument
*docbook
)
480 YelpDocbookDocumentPrivate
*priv
= GET_PRIV (docbook
);
482 if (priv
->index_running
|| priv
->process_running
|| priv
->transform_running
)
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
,
498 g_mutex_unlock (&priv
->mutex
);
504 docbook_monitor_changed (GFileMonitor
*monitor
,
507 GFileMonitorEvent event_type
,
508 YelpDocbookDocument
*docbook
)
510 YelpDocbookDocumentPrivate
*priv
= GET_PRIV (docbook
);
512 if (g_get_monotonic_time() - priv
->reload_time
< 1000)
515 if (priv
->index_running
|| priv
->process_running
|| priv
->transform_running
) {
516 g_timeout_add_seconds (1, (GSourceFunc
) docbook_reload
, docbook
);
520 docbook_reload (docbook
);
523 /******************************************************************************/
526 docbook_walk (YelpDocbookDocument
*docbook
)
528 static gint autoid
= 0;
531 xmlChar
*title
= NULL
;
532 xmlNodePtr cur
, old_cur
;
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
);
547 priv
->max_depth
= max
;
551 id
= xmlGetProp (priv
->xmlcur
, BAD_CAST
"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");
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
;
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
);
600 yelp_document_signal (YELP_DOCUMENT (docbook
),
602 YELP_DOCUMENT_SIGNAL_INFO
,
605 for (cur
= priv
->xmlcur
->children
; cur
; cur
= cur
->next
) {
606 if (cur
->type
== XML_ELEMENT_NODE
) {
608 docbook_walk (docbook
);
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
;
629 docbook_walk_chunkQ (YelpDocbookDocument
*docbook
,
634 if (cur_depth
<= max_depth
)
635 return docbook_walk_divisionQ (docbook
, cur
);
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") );
672 docbook_walk_get_title (YelpDocbookDocument
*docbook
,
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]
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"))
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"))
700 else if (!xmlStrcmp (title
->name
, BAD_CAST
"title"))
705 else if (title_tmp
) {
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"))
714 else if (!xmlStrcmp (title
->name
, BAD_CAST
"refpurpose")) {
722 else if (!xmlStrncmp (child
->name
, BAD_CAST
"refsect", 7))
727 /* The title for other elements appears in the following:
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
};
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")) {
744 else if (!xmlStrcmp (child
->name
, BAD_CAST
"title"))
746 else if (!xmlStrcmp (child
->name
, BAD_CAST
"info"))
748 else if (!xmlStrcmp (child
->name
, BAD_CAST infoname
))
750 else if (!xmlStrcmp (child
->name
, BAD_CAST
"blockinfo"))
752 else if (!xmlStrcmp (child
->name
, BAD_CAST
"objectinfo"))
761 for (i
= 0; i
< 3; i
++) {
764 for (title
= child
->children
; title
; title
= title
->next
) {
765 if (!xmlStrcmp (title
->name
, BAD_CAST
"titleabbrev"))
767 else if (!xmlStrcmp (title
->name
, BAD_CAST
"title"))
782 xmlChar
*title_s
= xmlNodeGetContent (title
);
783 gchar
*ret
= g_strdup ((const gchar
*) title_s
);
788 return g_strdup (_("Unknown"));
791 /******************************************************************************/
794 transform_chunk_ready (YelpTransform
*transform
,
796 YelpDocbookDocument
*docbook
)
798 YelpDocbookDocumentPrivate
*priv
= GET_PRIV (docbook
);
801 debug_print (DB_FUNCTION
, "entering\n");
802 g_assert (transform
== priv
->transform
);
804 if (priv
->state
== DOCBOOK_STATE_STOP
) {
805 docbook_disconnect (docbook
);
809 content
= yelp_transform_take_chunk (transform
, chunk_id
);
810 yelp_document_give_contents (YELP_DOCUMENT (docbook
),
813 "application/xhtml+xml");
815 yelp_document_signal (YELP_DOCUMENT (docbook
),
817 YELP_DOCUMENT_SIGNAL_CONTENTS
,
822 transform_finished (YelpTransform
*transform
,
823 YelpDocbookDocument
*docbook
)
825 YelpDocbookDocumentPrivate
*priv
= GET_PRIV (docbook
);
826 YelpDocument
*document
= YELP_DOCUMENT (docbook
);
830 debug_print (DB_FUNCTION
, "entering\n");
831 g_assert (transform
== priv
->transform
);
833 if (priv
->state
== DOCBOOK_STATE_STOP
) {
834 docbook_disconnect (docbook
);
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
,
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ā."),
853 yelp_document_error_pending ((YelpDocument
*) docbook
, error
);
854 g_error_free (error
);
858 transform_error (YelpTransform
*transform
,
859 YelpDocbookDocument
*docbook
)
861 YelpDocbookDocumentPrivate
*priv
= GET_PRIV (docbook
);
864 debug_print (DB_FUNCTION
, "entering\n");
865 g_assert (transform
== priv
->transform
);
867 if (priv
->state
== DOCBOOK_STATE_STOP
) {
868 docbook_disconnect (docbook
);
872 error
= yelp_transform_get_error (transform
);
873 yelp_document_error_pending ((YelpDocument
*) docbook
, error
);
874 g_error_free (error
);
876 docbook_disconnect (docbook
);
880 transform_finalized (YelpDocbookDocument
*docbook
,
883 YelpDocbookDocumentPrivate
*priv
= GET_PRIV (docbook
);
885 debug_print (DB_FUNCTION
, "entering\n");
888 xmlFreeDoc (priv
->xmldoc
);
892 /******************************************************************************/
895 docbook_index_done (YelpDocbookDocument
*docbook
)
897 g_object_set (docbook
, "indexed", TRUE
, NULL
);
898 g_object_unref (docbook
);
903 YelpDocbookDocument
*docbook
;
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
);
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"))
931 for (child
= index
->cur
->children
; child
; child
= child
->next
) {
933 docbook_index_node (index
);
939 docbook_index_chunk (DocbookIndexData
*index
)
944 GSList
*chunks
= NULL
;
946 id
= xmlGetProp (index
->cur
, BAD_CAST
"id");
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
;
962 docbook_index_node (index
);
968 YelpDocument
*document
= YELP_DOCUMENT (index
->docbook
);
970 gchar
*full_uri
, *tmp
, *body
;
972 body
= g_string_free (index
->str
, FALSE
);
975 tmp
= g_strconcat ("xref:", id
, NULL
);
976 uri
= yelp_uri_new_relative (yelp_document_get_uri (document
), 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",
986 if (index
->cur
->parent
->parent
== NULL
)
987 yelp_storage_set_root_title (yelp_storage_get_default (),
988 index
->doc_uri
, title
);
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
);
1007 docbook_index_threaded (YelpDocbookDocument
*docbook
)
1009 DocbookIndexData
*index
= NULL
;
1010 xmlParserCtxtPtr parserCtxt
= NULL
;
1012 gchar
*filename
= NULL
;
1014 YelpDocbookDocumentPrivate
*priv
= GET_PRIV (docbook
);
1016 uri
= yelp_document_get_uri (YELP_DOCUMENT (docbook
));
1017 file
= yelp_uri_get_file (uri
);
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
)
1032 if (xmlXIncludeProcessFlags (index
->doc
,
1033 XML_PARSE_DTDLOAD
| XML_PARSE_NOCDATA
|
1034 XML_PARSE_NOENT
| XML_PARSE_NONET
)
1038 index
->cur
= xmlDocGetRootElement (index
->doc
);
1040 if (!xmlStrcmp (index
->cur
->name
, BAD_CAST
"book"))
1041 index
->max_depth
= 2;
1043 index
->max_depth
= 1;
1044 docbook_index_chunk (index
);
1048 g_object_unref (file
);
1049 if (filename
!= NULL
)
1051 if (index
->doc
!= NULL
)
1052 xmlFreeDoc (index
->doc
);
1053 if (index
->doc_uri
!= NULL
)
1054 g_free (index
->doc_uri
);
1057 if (parserCtxt
!= NULL
)
1058 xmlFreeParserCtxt (parserCtxt
);
1060 priv
->index_running
= FALSE
;
1061 g_idle_add ((GSourceFunc
) docbook_index_done
, docbook
);
1065 docbook_index (YelpDocument
*document
)
1067 YelpDocbookDocumentPrivate
*priv
;
1070 g_object_get (document
, "indexed", &done
, NULL
);
1074 priv
= GET_PRIV (document
);
1075 g_object_ref (document
);
1076 priv
->index
= g_thread_new ("docbook-index",
1077 (GThreadFunc
) docbook_index_threaded
,
1079 priv
->index_running
= TRUE
;