1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * Copyright (C) 2010-2020 Shaun McCance  <shaunm@gnome.org>
4  *
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.
9  *
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.
14  *
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/>.
17  *
18  * Author: Shaun McCance  <shaunm@gnome.org>
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
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>
33 
34 #include "yelp-help-list.h"
35 #include "yelp-settings.h"
36 
37 typedef struct _HelpListEntry HelpListEntry;
38 
39 static void           yelp_help_list_dispose         (GObject               *object);
40 static void           yelp_help_list_finalize        (GObject               *object);
41 
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                                                       GDestroyNotify         notify);
48 static void           help_list_think                (YelpHelpList          *list);
49 static void           help_list_handle_page          (YelpHelpList          *list,
50                                                       const gchar           *page_id);
51 static void           help_list_process_docbook      (YelpHelpList          *list,
52                                                       HelpListEntry         *entry);
53 static void           help_list_process_mallard      (YelpHelpList          *list,
54                                                       HelpListEntry         *entry);
55 
56 static const char*const known_vendor_prefixes[] = { "gnome",
57                                                     "fedora",
58                                                     "mozilla",
59                                                     NULL };
60 
61 struct _HelpListEntry
62 {
63     gchar *id;
64     gchar *title;
65     gchar *desc;
66     gchar *icon;
67 
68     gchar *filename;
69     YelpUriDocumentType type;
70 };
71 static void
help_list_entry_free(HelpListEntry * entry)72 help_list_entry_free (HelpListEntry *entry)
73 {
74     g_free (entry->id);
75     g_free (entry->title);
76     g_free (entry->desc);
77     g_free (entry);
78 }
79 static gint
help_list_entry_cmp(HelpListEntry * a,HelpListEntry * b)80 help_list_entry_cmp (HelpListEntry *a, HelpListEntry *b)
81 {
82     gchar *as, *bs;
83     as = a->title ? a->title : strchr (a->id, ':') + 1;
84     bs = b->title ? b->title : strchr (b->id, ':') + 1;
85     return g_utf8_collate (as, bs);
86 }
87 
88 typedef struct _YelpHelpListPrivate  YelpHelpListPrivate;
89 struct _YelpHelpListPrivate {
90     GMutex         mutex;
91     GThread       *thread;
92 
93     gboolean process_running;
94     gboolean process_ran;
95 
96     GHashTable    *entries;
97     GList         *all_entries;
98     GSList        *pending;
99 
100     xmlXPathCompExprPtr  get_docbook_title;
101     xmlXPathCompExprPtr  get_mallard_title;
102     xmlXPathCompExprPtr  get_mallard_desc;
103 };
104 
G_DEFINE_TYPE_WITH_PRIVATE(YelpHelpList,yelp_help_list,YELP_TYPE_DOCUMENT)105 G_DEFINE_TYPE_WITH_PRIVATE (YelpHelpList, yelp_help_list, YELP_TYPE_DOCUMENT)
106 
107 static void
108 yelp_help_list_class_init (YelpHelpListClass *klass)
109 {
110     GObjectClass      *object_class   = G_OBJECT_CLASS (klass);
111     YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass);
112 
113     object_class->dispose = yelp_help_list_dispose;
114     object_class->finalize = yelp_help_list_finalize;
115 
116     document_class->request_page = help_list_request_page;
117 }
118 
119 static void
yelp_help_list_init(YelpHelpList * list)120 yelp_help_list_init (YelpHelpList *list)
121 {
122     YelpHelpListPrivate *priv = yelp_help_list_get_instance_private (list);
123 
124     g_mutex_init (&priv->mutex);
125     priv->entries = g_hash_table_new_full (g_str_hash, g_str_equal,
126                                            g_free,
127                                            (GDestroyNotify) help_list_entry_free);
128 
129     priv->get_docbook_title = xmlXPathCompile (BAD_CAST "normalize-space("
130                                                "( /*/title | /*/db:title"
131                                                "| /*/articleinfo/title"
132                                                "| /*/bookinfo/title"
133                                                "| /*/db:info/db:title"
134                                                ")[1])");
135     priv->get_mallard_title = xmlXPathCompile (BAD_CAST "normalize-space((/mal:page/mal:info/mal:title[@type='text'] |"
136                                                "                 /mal:page/mal:title)[1])");
137     priv->get_mallard_desc = xmlXPathCompile (BAD_CAST "normalize-space(/mal:page/mal:info/mal:desc[1])");
138 
139     yelp_document_set_page_id ((YelpDocument *) list, NULL, "index");
140     yelp_document_set_page_id ((YelpDocument *) list, "index", "index");
141 }
142 
143 static void
yelp_help_list_dispose(GObject * object)144 yelp_help_list_dispose (GObject *object)
145 {
146     G_OBJECT_CLASS (yelp_help_list_parent_class)->dispose (object);
147 }
148 
149 static void
yelp_help_list_finalize(GObject * object)150 yelp_help_list_finalize (GObject *object)
151 {
152     YelpHelpListPrivate *priv = yelp_help_list_get_instance_private (YELP_HELP_LIST (object));
153 
154     g_hash_table_destroy (priv->entries);
155     g_mutex_clear (&priv->mutex);
156 
157     if (priv->get_docbook_title)
158         xmlXPathFreeCompExpr (priv->get_docbook_title);
159     if (priv->get_mallard_title)
160         xmlXPathFreeCompExpr (priv->get_mallard_title);
161     if (priv->get_mallard_desc)
162         xmlXPathFreeCompExpr (priv->get_mallard_desc);
163 
164     G_OBJECT_CLASS (yelp_help_list_parent_class)->finalize (object);
165 }
166 
167 YelpDocument *
yelp_help_list_new(YelpUri * uri)168 yelp_help_list_new (YelpUri *uri)
169 {
170     return g_object_new (YELP_TYPE_HELP_LIST, NULL);
171 }
172 
173 /******************************************************************************/
174 
175 static gboolean
help_list_request_page(YelpDocument * document,const gchar * page_id,GCancellable * cancellable,YelpDocumentCallback callback,gpointer user_data,GDestroyNotify notify)176 help_list_request_page (YelpDocument          *document,
177                         const gchar           *page_id,
178                         GCancellable          *cancellable,
179                         YelpDocumentCallback   callback,
180                         gpointer               user_data,
181                         GDestroyNotify         notify)
182 {
183     gboolean handled;
184     YelpHelpListPrivate *priv = yelp_help_list_get_instance_private (YELP_HELP_LIST (document));
185 
186     if (page_id == NULL)
187         page_id = "index";
188 
189     handled =
190         YELP_DOCUMENT_CLASS (yelp_help_list_parent_class)->request_page (document,
191                                                                          page_id,
192                                                                          cancellable,
193                                                                          callback,
194                                                                          user_data,
195                                                                          notify);
196     if (handled) {
197         return TRUE;
198     }
199 
200     g_mutex_lock (&priv->mutex);
201     if (priv->process_ran) {
202         help_list_handle_page ((YelpHelpList *) document, page_id);
203         return TRUE;
204     }
205 
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)(GCallback) help_list_think,
211                                      document);
212     }
213     priv->pending = g_slist_prepend (priv->pending, g_strdup (page_id));
214     g_mutex_unlock (&priv->mutex);
215     return TRUE;
216 }
217 
218 static void
help_list_think(YelpHelpList * list)219 help_list_think (YelpHelpList *list)
220 {
221     const gchar * const *sdatadirs = g_get_system_data_dirs ();
222     const gchar * const *langs = g_get_language_names ();
223     YelpHelpListPrivate *priv = yelp_help_list_get_instance_private (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;
229 
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];
234 
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;
248         }
249         while ((child = g_file_enumerator_next_file (children, NULL, NULL))) {
250             gchar *docid;
251             HelpListEntry *entry = NULL;
252 
253             if (g_file_info_get_file_type (child) != G_FILE_TYPE_DIRECTORY) {
254                 g_object_unref (child);
255                 continue;
256             }
257 
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;
263             }
264 
265             for (lang_i = 0; langs[lang_i]; lang_i++) {
266                 gchar *filename, *tmp;
267 
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;
279                 }
280                 g_free (filename);
281 
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;
295                 }
296                 g_free (filename);
297             }
298 
299             if (entry != NULL) {
300                 g_hash_table_insert (priv->entries, docid, entry);
301                 priv->all_entries = g_list_prepend (priv->all_entries, entry);
302             }
303             else
304                 g_free (docid);
305             g_object_unref (child);
306         }
307         g_object_unref (children);
308         g_object_unref (helpdir);
309         g_free (helpdirname);
310     }
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;
325             }
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;
332                 }
333 
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;
338                 }
339 
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;
350                 }
351                 g_free (filename);
352 
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;
363                 }
364                 g_free (filename);
365 
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);
372                 }
373             }
374 
375             g_object_unref (children);
376         }
377     }
378     g_free (datadirs);
379 
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;
386 
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);
391 
392         tmp = g_strconcat (entryid, ".desktop", NULL);
393         app = g_desktop_app_info_new (tmp);
394         g_free (tmp);
395 
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;
404             }
405         }
406 
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);
418                 }
419             }
420             g_object_unref (app);
421         }
422     }
423 
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);
432     }
433     g_mutex_unlock (&priv->mutex);
434 
435     g_object_unref (list);
436 }
437 
438 /* This function expects to be called inside a locked mutex */
439 static void
help_list_handle_page(YelpHelpList * list,const gchar * page_id)440 help_list_handle_page (YelpHelpList *list,
441                        const gchar  *page_id)
442 {
443     gchar **colors, *tmp;
444     GList *cur;
445     YelpHelpListPrivate *priv = yelp_help_list_get_instance_private (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 ());
452 
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);
460 
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;");
469 
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);
476 
477     g_string_append (string,
478                      "div.trail { margin: 0 1em 0.2em 1em; padding: 0; text-indent: -1em;");
479 
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);
484 
485     g_string_append (string,
486                      "a.trail { white-space: nowrap; }\n"
487                      "div.hgroup { margin: 0 0 0.5em 0;");
488 
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);
495 
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);
518 
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;");
532 
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);
538 
539     g_string_append (string,
540                      "a:hover { text-decoration: underline; }\n"
541                      "a img { border: none; }\n"
542                      "</style>\n");
543 
544     tmp = g_markup_printf_escaped ("<title>%s</title>",
545                                    _("All Help Documents"));
546     g_string_append (string, tmp);
547     g_free (tmp);
548 
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);
557 
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 : "";
564 
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);
569 
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);
575         }
576         else
577             g_string_append (string, "<div class='inner'>");
578 
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);
585     }
586 
587     g_string_append (string,
588                      "</div>"
589                      "<div class='footer'></div>"
590                      "</body></html>");
591 
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);
599 }
600 
601 
602 static void
help_list_process_docbook(YelpHelpList * list,HelpListEntry * entry)603 help_list_process_docbook (YelpHelpList  *list,
604                            HelpListEntry *entry)
605 {
606     xmlParserCtxtPtr parserCtxt;
607     xmlDocPtr xmldoc;
608     xmlXPathContextPtr xpath;
609     xmlXPathObjectPtr obj = NULL;
610     YelpHelpListPrivate *priv = yelp_help_list_get_instance_private (list);
611 
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;
620 
621     xmlXIncludeProcessFlags (xmldoc,
622                              XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
623                              XML_PARSE_NOENT   | XML_PARSE_NONET   );
624 
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);
633     }
634 
635     if (xmldoc)
636         xmlFreeDoc (xmldoc);
637     if (xpath)
638         xmlXPathFreeContext (xpath);
639 }
640 
641 static void
help_list_process_mallard(YelpHelpList * list,HelpListEntry * entry)642 help_list_process_mallard (YelpHelpList  *list,
643                            HelpListEntry *entry)
644 {
645     xmlParserCtxtPtr parserCtxt;
646     xmlDocPtr xmldoc;
647     xmlXPathContextPtr xpath;
648     xmlXPathObjectPtr obj = NULL;
649     YelpHelpListPrivate *priv = yelp_help_list_get_instance_private (list);
650 
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;
659 
660     xmlXIncludeProcessFlags (xmldoc,
661                              XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
662                              XML_PARSE_NOENT   | XML_PARSE_NONET   );
663 
664     xpath = xmlXPathNewContext (xmldoc);
665     xmlXPathRegisterNs (xpath, BAD_CAST "mal",
666                         BAD_CAST "http://projectmallard.org/1.0/");
667 
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);
673     }
674 
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);
680     }
681 
682     if (xmldoc)
683         xmlFreeDoc (xmldoc);
684     if (xpath)
685         xmlXPathFreeContext (xpath);
686 }
687