yelp-document: Fix return type of document_indexed
[yelp.git] / libyelp / yelp-help-list.c
blobf50202295e8178243669abdfddd87f56cd4bf02f
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2010 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 <gio/gio.h>
26 #include <gio/gdesktopappinfo.h>
27 #include <glib/gi18n.h>
28 #include <gtk/gtk.h>
29 #include <libxml/parser.h>
30 #include <libxml/xinclude.h>
31 #include <libxml/xpath.h>
32 #include <libxml/xpathInternals.h>
34 #include "yelp-help-list.h"
35 #include "yelp-settings.h"
37 typedef struct _HelpListEntry HelpListEntry;
39 static void yelp_help_list_dispose (GObject *object);
40 static void yelp_help_list_finalize (GObject *object);
42 static gboolean help_list_request_page (YelpDocument *document,
43 const gchar *page_id,
44 GCancellable *cancellable,
45 YelpDocumentCallback callback,
46 gpointer user_data);
47 static void help_list_think (YelpHelpList *list);
48 static void help_list_handle_page (YelpHelpList *list,
49 const gchar *page_id);
50 static void help_list_process_docbook (YelpHelpList *list,
51 HelpListEntry *entry);
52 static void help_list_process_mallard (YelpHelpList *list,
53 HelpListEntry *entry);
55 static const char*const known_vendor_prefixes[] = { "gnome",
56 "fedora",
57 "mozilla",
58 NULL };
60 struct _HelpListEntry
62 gchar *id;
63 gchar *title;
64 gchar *desc;
65 gchar *icon;
67 gchar *filename;
68 YelpUriDocumentType type;
70 static void
71 help_list_entry_free (HelpListEntry *entry)
73 g_free (entry->id);
74 g_free (entry->title);
75 g_free (entry->desc);
76 g_free (entry);
78 static gint
79 help_list_entry_cmp (HelpListEntry *a, HelpListEntry *b)
81 gchar *as, *bs;
82 as = a->title ? a->title : strchr (a->id, ':') + 1;
83 bs = b->title ? b->title : strchr (b->id, ':') + 1;
84 return g_utf8_collate (as, bs);
87 G_DEFINE_TYPE (YelpHelpList, yelp_help_list, YELP_TYPE_DOCUMENT)
88 #define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_HELP_LIST, YelpHelpListPrivate))
90 typedef struct _YelpHelpListPrivate YelpHelpListPrivate;
91 struct _YelpHelpListPrivate {
92 GMutex mutex;
93 GThread *thread;
95 gboolean process_running;
96 gboolean process_ran;
98 GHashTable *entries;
99 GList *all_entries;
100 GSList *pending;
102 xmlXPathCompExprPtr get_docbook_title;
103 xmlXPathCompExprPtr get_mallard_title;
104 xmlXPathCompExprPtr get_mallard_desc;
107 static void
108 yelp_help_list_class_init (YelpHelpListClass *klass)
110 GObjectClass *object_class = G_OBJECT_CLASS (klass);
111 YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass);
113 object_class->dispose = yelp_help_list_dispose;
114 object_class->finalize = yelp_help_list_finalize;
116 document_class->request_page = help_list_request_page;
118 g_type_class_add_private (klass, sizeof (YelpHelpListPrivate));
121 static void
122 yelp_help_list_init (YelpHelpList *list)
124 YelpHelpListPrivate *priv = GET_PRIV (list);
126 g_mutex_init (&priv->mutex);
127 priv->entries = g_hash_table_new_full (g_str_hash, g_str_equal,
128 g_free,
129 (GDestroyNotify) help_list_entry_free);
131 priv->get_docbook_title = xmlXPathCompile (BAD_CAST "normalize-space("
132 "( /*/title | /*/db:title"
133 "| /*/articleinfo/title"
134 "| /*/bookinfo/title"
135 "| /*/db:info/db:title"
136 ")[1])");
137 priv->get_mallard_title = xmlXPathCompile (BAD_CAST "normalize-space((/mal:page/mal:info/mal:title[@type='text'] |"
138 " /mal:page/mal:title)[1])");
139 priv->get_mallard_desc = xmlXPathCompile (BAD_CAST "normalize-space(/mal:page/mal:info/mal:desc[1])");
141 yelp_document_set_page_id ((YelpDocument *) list, NULL, "index");
142 yelp_document_set_page_id ((YelpDocument *) list, "index", "index");
145 static void
146 yelp_help_list_dispose (GObject *object)
148 G_OBJECT_CLASS (yelp_help_list_parent_class)->dispose (object);
151 static void
152 yelp_help_list_finalize (GObject *object)
154 YelpHelpListPrivate *priv = GET_PRIV (object);
156 g_hash_table_destroy (priv->entries);
157 g_mutex_clear (&priv->mutex);
159 if (priv->get_docbook_title)
160 xmlXPathFreeCompExpr (priv->get_docbook_title);
161 if (priv->get_mallard_title)
162 xmlXPathFreeCompExpr (priv->get_mallard_title);
163 if (priv->get_mallard_desc)
164 xmlXPathFreeCompExpr (priv->get_mallard_desc);
166 G_OBJECT_CLASS (yelp_help_list_parent_class)->finalize (object);
169 YelpDocument *
170 yelp_help_list_new (YelpUri *uri)
172 return g_object_new (YELP_TYPE_HELP_LIST, NULL);
175 /******************************************************************************/
177 static gboolean
178 help_list_request_page (YelpDocument *document,
179 const gchar *page_id,
180 GCancellable *cancellable,
181 YelpDocumentCallback callback,
182 gpointer user_data)
184 gboolean handled;
185 YelpHelpListPrivate *priv = GET_PRIV (document);
187 if (page_id == NULL)
188 page_id = "index";
190 handled =
191 YELP_DOCUMENT_CLASS (yelp_help_list_parent_class)->request_page (document,
192 page_id,
193 cancellable,
194 callback,
195 user_data);
196 if (handled) {
197 return TRUE;
200 g_mutex_lock (&priv->mutex);
201 if (priv->process_ran) {
202 help_list_handle_page ((YelpHelpList *) document, page_id);
203 return TRUE;
206 if (!priv->process_running) {
207 priv->process_running = TRUE;
208 g_object_ref (document);
209 priv->thread = g_thread_new ("helplist-page",
210 (GThreadFunc) help_list_think,
211 document);
213 priv->pending = g_slist_prepend (priv->pending, g_strdup (page_id));
214 g_mutex_unlock (&priv->mutex);
215 return TRUE;
218 static void
219 help_list_think (YelpHelpList *list)
221 const gchar * const *sdatadirs = g_get_system_data_dirs ();
222 const gchar * const *langs = g_get_language_names ();
223 YelpHelpListPrivate *priv = GET_PRIV (list);
224 /* The strings are still owned by GLib; we just own the array. */
225 gchar **datadirs;
226 gint datadir_i, lang_i;
227 GList *cur;
228 GtkIconTheme *theme;
230 datadirs = g_new0 (gchar *, g_strv_length ((gchar **) sdatadirs) + 2);
231 datadirs[0] = (gchar *) g_get_user_data_dir ();
232 for (datadir_i = 0; sdatadirs[datadir_i]; datadir_i++)
233 datadirs[datadir_i + 1] = (gchar *) sdatadirs[datadir_i];
235 for (datadir_i = 0; datadirs[datadir_i]; datadir_i++) {
236 gchar *helpdirname = g_build_filename (datadirs[datadir_i], "gnome", "help", NULL);
237 GFile *helpdir = g_file_new_for_path (helpdirname);
238 GFileEnumerator *children = g_file_enumerate_children (helpdir,
239 G_FILE_ATTRIBUTE_STANDARD_TYPE","
240 G_FILE_ATTRIBUTE_STANDARD_NAME,
241 G_FILE_QUERY_INFO_NONE,
242 NULL, NULL);
243 GFileInfo *child;
244 if (children == NULL) {
245 g_object_unref (helpdir);
246 g_free (helpdirname);
247 continue;
249 while ((child = g_file_enumerator_next_file (children, NULL, NULL))) {
250 gchar *docid;
251 HelpListEntry *entry = NULL;
253 if (g_file_info_get_file_type (child) != G_FILE_TYPE_DIRECTORY) {
254 g_object_unref (child);
255 continue;
258 docid = g_strconcat ("ghelp:", g_file_info_get_name (child), NULL);
259 if (g_hash_table_lookup (priv->entries, docid)) {
260 g_free (docid);
261 g_object_unref (child);
262 continue;
265 for (lang_i = 0; langs[lang_i]; lang_i++) {
266 gchar *filename, *tmp;
268 filename = g_build_filename (helpdirname,
269 g_file_info_get_name (child),
270 langs[lang_i],
271 "index.page",
272 NULL);
273 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
274 entry = g_new0 (HelpListEntry, 1);
275 entry->id = g_strdup (docid);
276 entry->filename = filename;
277 entry->type = YELP_URI_DOCUMENT_TYPE_MALLARD;
278 break;
280 g_free (filename);
282 tmp = g_strdup_printf ("%s.xml", g_file_info_get_name (child));
283 filename = g_build_filename (helpdirname,
284 g_file_info_get_name (child),
285 langs[lang_i],
286 tmp,
287 NULL);
288 g_free (tmp);
289 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
290 entry = g_new0 (HelpListEntry, 1);
291 entry->id = g_strdup (docid);
292 entry->filename = filename;
293 entry->type = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
294 break;
296 g_free (filename);
299 if (entry != NULL) {
300 g_hash_table_insert (priv->entries, docid, entry);
301 priv->all_entries = g_list_prepend (priv->all_entries, entry);
303 else
304 g_free (docid);
305 g_object_unref (child);
307 g_object_unref (children);
308 g_object_unref (helpdir);
309 g_free (helpdirname);
311 for (datadir_i = 0; datadirs[datadir_i]; datadir_i++) {
312 for (lang_i = 0; langs[lang_i]; lang_i++) {
313 gchar *langdirname = g_build_filename (datadirs[datadir_i], "help", langs[lang_i], NULL);
314 GFile *langdir = g_file_new_for_path (langdirname);
315 GFileEnumerator *children = g_file_enumerate_children (langdir,
316 G_FILE_ATTRIBUTE_STANDARD_TYPE","
317 G_FILE_ATTRIBUTE_STANDARD_NAME,
318 G_FILE_QUERY_INFO_NONE,
319 NULL, NULL);
320 GFileInfo *child;
321 if (children == NULL) {
322 g_object_unref (langdir);
323 g_free (langdirname);
324 continue;
326 while ((child = g_file_enumerator_next_file (children, NULL, NULL))) {
327 gchar *docid, *filename;
328 HelpListEntry *entry = NULL;
329 if (g_file_info_get_file_type (child) != G_FILE_TYPE_DIRECTORY) {
330 g_object_unref (child);
331 continue;
334 docid = g_strconcat ("help:", g_file_info_get_name (child), NULL);
335 if (g_hash_table_lookup (priv->entries, docid) != NULL) {
336 g_free (docid);
337 continue;
340 filename = g_build_filename (langdirname,
341 g_file_info_get_name (child),
342 "index.page",
343 NULL);
344 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
345 entry = g_new0 (HelpListEntry, 1);
346 entry->id = docid;
347 entry->filename = filename;
348 entry->type = YELP_URI_DOCUMENT_TYPE_MALLARD;
349 goto found;
351 g_free (filename);
353 filename = g_build_filename (langdirname,
354 g_file_info_get_name (child),
355 "index.docbook",
356 NULL);
357 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
358 entry = g_new0 (HelpListEntry, 1);
359 entry->id = docid;
360 entry->filename = filename;
361 entry->type = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
362 goto found;
364 g_free (filename);
366 g_free (docid);
367 found:
368 g_object_unref (child);
369 if (entry != NULL) {
370 g_hash_table_insert (priv->entries, docid, entry);
371 priv->all_entries = g_list_prepend (priv->all_entries, entry);
375 g_object_unref (children);
378 g_free (datadirs);
380 theme = gtk_icon_theme_get_default ();
381 for (cur = priv->all_entries; cur != NULL; cur = cur->next) {
382 GDesktopAppInfo *app;
383 gchar *tmp;
384 HelpListEntry *entry = (HelpListEntry *) cur->data;
385 const gchar *entryid = strchr (entry->id, ':') + 1;
387 if (entry->type == YELP_URI_DOCUMENT_TYPE_MALLARD)
388 help_list_process_mallard (list, entry);
389 else if (entry->type == YELP_URI_DOCUMENT_TYPE_DOCBOOK)
390 help_list_process_docbook (list, entry);
392 tmp = g_strconcat (entryid, ".desktop", NULL);
393 app = g_desktop_app_info_new (tmp);
394 g_free (tmp);
396 if (app == NULL) {
397 char **prefix;
398 for (prefix = (char **) known_vendor_prefixes; *prefix; prefix++) {
399 tmp = g_strconcat (*prefix, "-", entryid, ".desktop", NULL);
400 app = g_desktop_app_info_new (tmp);
401 g_free (tmp);
402 if (app)
403 break;
407 if (app != NULL) {
408 GIcon *icon = g_app_info_get_icon ((GAppInfo *) app);
409 if (icon != NULL) {
410 GtkIconInfo *info = gtk_icon_theme_lookup_by_gicon (theme,
411 icon, 22,
412 GTK_ICON_LOOKUP_NO_SVG);
413 if (info != NULL) {
414 const gchar *iconfile = gtk_icon_info_get_filename (info);
415 if (iconfile)
416 entry->icon = g_filename_to_uri (iconfile, NULL, NULL);
417 g_object_unref (info);
420 g_object_unref (app);
424 g_mutex_lock (&priv->mutex);
425 priv->process_running = FALSE;
426 priv->process_ran = TRUE;
427 while (priv->pending) {
428 gchar *page_id = (gchar *) priv->pending->data;
429 help_list_handle_page (list, page_id);
430 g_free (page_id);
431 priv->pending = g_slist_delete_link (priv->pending, priv->pending);
433 g_mutex_unlock (&priv->mutex);
435 g_object_unref (list);
438 /* This function expects to be called inside a locked mutex */
439 static void
440 help_list_handle_page (YelpHelpList *list,
441 const gchar *page_id)
443 gchar **colors, *tmp;
444 GList *cur;
445 YelpHelpListPrivate *priv = GET_PRIV (list);
446 GtkTextDirection direction = gtk_widget_get_default_direction ();
447 GString *string = g_string_new
448 ("<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><style type='text/css'>\n"
449 "html { height: 100%; }\n"
450 "body { margin: 0; padding: 0; max-width: 100%;");
451 colors = yelp_settings_get_colors (yelp_settings_get_default ());
453 tmp = g_markup_printf_escaped (" background-color: %s; color: %s;"
454 " direction: %s; }\n",
455 colors[YELP_SETTINGS_COLOR_BASE],
456 colors[YELP_SETTINGS_COLOR_TEXT],
457 (direction == GTK_TEXT_DIR_RTL) ? "rtl" : "ltr");
458 g_string_append (string, tmp);
459 g_free (tmp);
461 g_string_append (string,
462 "div.body { margin: 0 12px 0 12px; padding: 0;"
463 " max-width: 60em; min-height: 20em; }\n"
464 "div.header { max-width: 100%; width: 100%;"
465 " padding: 0; margin: 0 0 1em 0; }\n"
466 "div.footer { max-width: 60em; }\n"
467 "div.sect { margin-top: 1.72em; }\n"
468 "div.trails { margin: 0; padding: 0.2em 12px 0 12px;");
470 tmp = g_markup_printf_escaped (" background-color: %s;"
471 " border-bottom: solid 1px %s; }\n",
472 colors[YELP_SETTINGS_COLOR_GRAY_BASE],
473 colors[YELP_SETTINGS_COLOR_GRAY_BORDER]);
474 g_string_append (string, tmp);
475 g_free (tmp);
477 g_string_append (string,
478 "div.trail { margin: 0 1em 0.2em 1em; padding: 0; text-indent: -1em;");
480 tmp = g_markup_printf_escaped (" color: %s; }\n",
481 colors[YELP_SETTINGS_COLOR_TEXT_LIGHT]);
482 g_string_append (string, tmp);
483 g_free (tmp);
485 g_string_append (string,
486 "a.trail { white-space: nowrap; }\n"
487 "div.hgroup { margin: 0 0 0.5em 0;");
489 tmp = g_markup_printf_escaped (" color: %s;"
490 " border-bottom: solid 1px %s; }\n",
491 colors[YELP_SETTINGS_COLOR_TEXT_LIGHT],
492 colors[YELP_SETTINGS_COLOR_GRAY_BORDER]);
493 g_string_append (string, tmp);
494 g_free (tmp);
496 tmp = g_markup_printf_escaped ("div.title { margin: 0 0 0.2em 0; font-weight: bold; color: %s; }\n"
497 "div.desc { margin: 0 0 0.2em 0; }\n"
498 "div.linkdiv div.inner { padding-%s: 30px; min-height: 24px;"
499 " background-position: top %s; background-repeat: no-repeat;"
500 " -webkit-background-size: 22px 22px; }\n"
501 "div.linkdiv div.title {font-size: 1em; color: inherit; }\n"
502 "div.linkdiv div.desc { color: %s; }\n"
503 "div.linkdiv { margin: 0; padding: 0.5em; }\n"
504 "a:hover div.linkdiv {"
505 " text-decoration: none;"
506 " outline: solid 1px %s;"
507 " background: -webkit-gradient(linear, left top, left 80,"
508 " from(%s), to(%s)); }\n",
509 colors[YELP_SETTINGS_COLOR_TEXT_LIGHT],
510 ((direction == GTK_TEXT_DIR_RTL) ? "right" : "left"),
511 ((direction == GTK_TEXT_DIR_RTL) ? "right" : "left"),
512 colors[YELP_SETTINGS_COLOR_TEXT_LIGHT],
513 colors[YELP_SETTINGS_COLOR_BLUE_BASE],
514 colors[YELP_SETTINGS_COLOR_BLUE_BASE],
515 colors[YELP_SETTINGS_COLOR_BASE]);
516 g_string_append (string, tmp);
517 g_free (tmp);
519 g_string_append (string,
520 "h1, h2, h3, h4, h5, h6, h7 { margin: 0; padding: 0; font-weight: bold; }\n"
521 "h1 { font-size: 1.44em; }\n"
522 "h2 { font-size: 1.2em; }"
523 "h3.title, h4.title, h5.title, h6.title, h7.title { font-size: 1.2em; }"
524 "h3, h4, h5, h6, h7 { font-size: 1em; }"
525 "p { line-height: 1.72em; }"
526 "div, pre, p { margin: 1em 0 0 0; padding: 0; }"
527 "div:first-child, pre:first-child, p:first-child { margin-top: 0; }"
528 "div.inner, div.contents, pre.contents { margin-top: 0; }"
529 "p img { vertical-align: middle; }"
530 "a {"
531 " text-decoration: none;");
533 tmp = g_markup_printf_escaped (" color: %s; } a:visited { color: %s; }",
534 colors[YELP_SETTINGS_COLOR_LINK],
535 colors[YELP_SETTINGS_COLOR_LINK_VISITED]);
536 g_string_append (string, tmp);
537 g_free (tmp);
539 g_string_append (string,
540 "a:hover { text-decoration: underline; }\n"
541 "a img { border: none; }\n"
542 "</style>\n");
544 tmp = g_markup_printf_escaped ("<title>%s</title>",
545 _("All Help Documents"));
546 g_string_append (string, tmp);
547 g_free (tmp);
549 g_string_append (string,
550 "</head><body>"
551 "<div class='header'></div>"
552 "<div class='body'><div class='hgroup'>");
553 tmp = g_markup_printf_escaped ("<h1>%s</h1></div>\n",
554 _("All Help Documents"));
555 g_string_append (string, tmp);
556 g_free (tmp);
558 priv->all_entries = g_list_sort (priv->all_entries,
559 (GCompareFunc) help_list_entry_cmp);
560 for (cur = priv->all_entries; cur != NULL; cur = cur->next) {
561 HelpListEntry *entry = (HelpListEntry *) cur->data;
562 gchar *title = entry->title ? entry->title : (strchr (entry->id, ':') + 1);
563 const gchar *desc = entry->desc ? entry->desc : "";
565 tmp = g_markup_printf_escaped ("<a href='%s'><div class='linkdiv'>",
566 entry->id);
567 g_string_append (string, tmp);
568 g_free (tmp);
570 if (entry->icon) {
571 tmp = g_markup_printf_escaped ("<div class='inner' style='background-image: url(%s);'>",
572 entry->icon);
573 g_string_append (string, tmp);
574 g_free (tmp);
576 else
577 g_string_append (string, "<div class='inner'>");
579 tmp = g_markup_printf_escaped ("<div class='title'>%s</div>"
580 "<div class='desc'>%s</div>"
581 "</div></div></a>",
582 title, desc);
583 g_string_append (string, tmp);
584 g_free (tmp);
587 g_string_append (string,
588 "</div>"
589 "<div class='footer'></div>"
590 "</body></html>");
592 yelp_document_give_contents (YELP_DOCUMENT (list), page_id,
593 string->str,
594 "application/xhtml+xml");
595 g_strfreev (colors);
596 g_string_free (string, FALSE);
597 yelp_document_signal (YELP_DOCUMENT (list), page_id,
598 YELP_DOCUMENT_SIGNAL_CONTENTS, NULL);
602 static void
603 help_list_process_docbook (YelpHelpList *list,
604 HelpListEntry *entry)
606 xmlParserCtxtPtr parserCtxt;
607 xmlDocPtr xmldoc;
608 xmlXPathContextPtr xpath;
609 xmlXPathObjectPtr obj = NULL;
610 YelpHelpListPrivate *priv = GET_PRIV (list);
612 parserCtxt = xmlNewParserCtxt ();
613 xmldoc = xmlCtxtReadFile (parserCtxt,
614 (const char *) entry->filename, NULL,
615 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
616 XML_PARSE_NOENT | XML_PARSE_NONET );
617 xmlFreeParserCtxt (parserCtxt);
618 if (xmldoc == NULL)
619 return;
621 xmlXIncludeProcessFlags (xmldoc,
622 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
623 XML_PARSE_NOENT | XML_PARSE_NONET );
625 xpath = xmlXPathNewContext (xmldoc);
626 xmlXPathRegisterNs (xpath, BAD_CAST "db",
627 BAD_CAST "http://docbook.org/ns/docbook");
628 obj = xmlXPathCompiledEval (priv->get_docbook_title, xpath);
629 if (obj) {
630 if (obj->stringval)
631 entry->title = g_strdup ((const gchar *) obj->stringval);
632 xmlXPathFreeObject (obj);
635 if (xmldoc)
636 xmlFreeDoc (xmldoc);
637 if (xpath)
638 xmlXPathFreeContext (xpath);
641 static void
642 help_list_process_mallard (YelpHelpList *list,
643 HelpListEntry *entry)
645 xmlParserCtxtPtr parserCtxt;
646 xmlDocPtr xmldoc;
647 xmlXPathContextPtr xpath;
648 xmlXPathObjectPtr obj = NULL;
649 YelpHelpListPrivate *priv = GET_PRIV (list);
651 parserCtxt = xmlNewParserCtxt ();
652 xmldoc = xmlCtxtReadFile (parserCtxt,
653 (const char *) entry->filename, NULL,
654 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
655 XML_PARSE_NOENT | XML_PARSE_NONET );
656 xmlFreeParserCtxt (parserCtxt);
657 if (xmldoc == NULL)
658 return;
660 xmlXIncludeProcessFlags (xmldoc,
661 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
662 XML_PARSE_NOENT | XML_PARSE_NONET );
664 xpath = xmlXPathNewContext (xmldoc);
665 xmlXPathRegisterNs (xpath, BAD_CAST "mal",
666 BAD_CAST "http://projectmallard.org/1.0/");
668 obj = xmlXPathCompiledEval (priv->get_mallard_title, xpath);
669 if (obj) {
670 if (obj->stringval)
671 entry->title = g_strdup ((const gchar *) obj->stringval);
672 xmlXPathFreeObject (obj);
675 obj = xmlXPathCompiledEval (priv->get_mallard_desc, xpath);
676 if (obj) {
677 if (obj->stringval)
678 entry->desc = g_strdup ((const gchar *) obj->stringval);
679 xmlXPathFreeObject (obj);
682 if (xmldoc)
683 xmlFreeDoc (xmldoc);
684 if (xpath)
685 xmlXPathFreeContext (xpath);