Fix handling of xref links with anchors in resolve_file_path()
[yelp.git] / libyelp / yelp-uri.c
blobbe23df2c4b06d0e2c6dfed581980b5abbe9d0f02
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2009 Shaun McCance <shaunm@gnome.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Shaun McCance <shaunm@gnome.org>
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <string.h>
28 #include <stdio.h>
30 #include <glib.h>
31 #include <gio/gio.h>
33 #include "yelp-uri.h"
34 #include "yelp-debug.h"
35 #include "yelp-settings.h"
37 static void yelp_uri_class_init (YelpUriClass *klass);
38 static void yelp_uri_init (YelpUri *uri);
39 static void yelp_uri_dispose (GObject *object);
40 static void yelp_uri_finalize (GObject *object);
42 static void resolve_start (YelpUri *uri);
43 static void resolve_sync (YelpUri *uri);
44 static void resolve_async (YelpUri *uri);
45 static gboolean resolve_final (YelpUri *uri);
47 static void resolve_file_uri (YelpUri *uri);
48 static void resolve_file_path (YelpUri *uri);
49 static void resolve_data_dirs (YelpUri *uri,
50 const gchar *subdir,
51 const gchar *docid,
52 const gchar *pageid,
53 gboolean langfirst);
54 static void resolve_ghelp_uri (YelpUri *uri);
55 static void resolve_help_uri (YelpUri *uri);
56 static void resolve_help_list_uri (YelpUri *uri);
57 static void resolve_man_uri (YelpUri *uri);
58 static void resolve_info_uri (YelpUri *uri);
59 static void resolve_xref_uri (YelpUri *uri);
60 static void resolve_page_and_frag (YelpUri *uri,
61 const gchar *arg);
62 static void resolve_gfile (YelpUri *uri,
63 const gchar *hash);
65 static gboolean is_man_path (const gchar *uri,
66 const gchar *encoding);
68 G_DEFINE_TYPE (YelpUri, yelp_uri, G_TYPE_OBJECT);
69 #define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_URI, YelpUriPrivate))
71 typedef struct _YelpUriPrivate YelpUriPrivate;
72 struct _YelpUriPrivate {
73 GThread *resolver;
75 YelpUriDocumentType doctype;
76 YelpUriDocumentType tmptype;
78 gchar *docuri;
79 gchar *fulluri;
80 GFile *gfile;
82 gchar **search_path;
83 gchar *page_id;
84 gchar *frag_id;
86 GHashTable *query;
88 /* Unresolved */
89 YelpUri *res_base;
90 gchar *res_arg;
93 enum {
94 RESOLVED,
95 LAST_SIGNAL
97 static guint uri_signals[LAST_SIGNAL] = {0,};
99 /******************************************************************************/
101 static const gchar *mancats[] = {
102 "0p",
103 "1", "1p", "1g", "1t", "1x", "1ssl", "1m",
104 "2",
105 "3", "3o", "3t", "3p", "3blt", "3nas", "3form", "3menu", "3tiff", "3ssl", "3readline",
106 "3ncurses", "3curses", "3f", "3pm", "3perl", "3qt", "3x", "3X11",
107 "4", "4x",
108 "5", "5snmp", "5x", "5ssl",
109 "6", "6x",
110 "7", "7gcc", "7x", "7ssl",
111 "8", "8l", "9", "0p",
112 NULL
115 static const gchar *infosuffix[] = {
116 ".info",
117 ".info.gz", ".info.bz2", ".info.lzma",
118 ".gz", ".bz2", ".lzma",
119 NULL
122 static const gchar default_info_path[] =
123 "/usr/info:/usr/share/info:/usr/local/info:/usr/local/share/info";
125 /******************************************************************************/
127 static void
128 yelp_uri_class_init (YelpUriClass *klass)
130 GObjectClass *object_class = G_OBJECT_CLASS (klass);
132 object_class->dispose = yelp_uri_dispose;
133 object_class->finalize = yelp_uri_finalize;
135 uri_signals[RESOLVED] =
136 g_signal_new ("resolved",
137 G_OBJECT_CLASS_TYPE (klass),
138 G_SIGNAL_RUN_LAST,
139 0, NULL, NULL,
140 g_cclosure_marshal_VOID__VOID,
141 G_TYPE_NONE, 0);
143 g_type_class_add_private (klass, sizeof (YelpUriPrivate));
146 static void
147 yelp_uri_init (YelpUri *uri)
149 YelpUriPrivate *priv = GET_PRIV (uri);
151 priv->query = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
153 return;
156 static void
157 yelp_uri_dispose (GObject *object)
159 YelpUriPrivate *priv = GET_PRIV (object);
161 if (priv->gfile) {
162 g_object_unref (priv->gfile);
163 priv->gfile = NULL;
166 if (priv->res_base) {
167 g_object_unref (priv->res_base);
168 priv->res_base = NULL;
171 if (priv->query) {
172 g_hash_table_destroy (priv->query);
173 priv->query = NULL;
176 G_OBJECT_CLASS (yelp_uri_parent_class)->dispose (object);
179 static void
180 yelp_uri_finalize (GObject *object)
182 YelpUriPrivate *priv = GET_PRIV (object);
184 g_free (priv->docuri);
185 g_free (priv->fulluri);
186 g_strfreev (priv->search_path);
187 g_free (priv->page_id);
188 g_free (priv->frag_id);
189 g_free (priv->res_arg);
191 G_OBJECT_CLASS (yelp_uri_parent_class)->finalize (object);
194 /******************************************************************************/
196 YelpUri *
197 yelp_uri_new (const gchar *arg)
199 return yelp_uri_new_relative (NULL, arg);
202 YelpUri *
203 yelp_uri_new_relative (YelpUri *base, const gchar *arg)
205 YelpUri *uri;
206 YelpUriPrivate *priv;
208 uri = (YelpUri *) g_object_new (YELP_TYPE_URI, NULL);
210 priv = GET_PRIV (uri);
211 priv->doctype = YELP_URI_DOCUMENT_TYPE_UNRESOLVED;
212 if (base)
213 priv->res_base = g_object_ref (base);
214 priv->res_arg = g_strdup (arg);
216 return uri;
219 YelpUri *
220 yelp_uri_new_search (YelpUri *base,
221 const gchar *text)
223 YelpUri *uri;
224 YelpUriPrivate *priv;
225 gchar *tmp;
227 uri = (YelpUri *) g_object_new (YELP_TYPE_URI, NULL);
229 priv = GET_PRIV (uri);
230 priv->doctype = YELP_URI_DOCUMENT_TYPE_UNRESOLVED;
231 if (base)
232 priv->res_base = g_object_ref (base);
233 tmp = g_uri_escape_string (text, NULL, FALSE);
234 priv->res_arg = g_strconcat("xref:search=", tmp, NULL);
235 g_free (tmp);
237 return uri;
240 /******************************************************************************/
242 void
243 yelp_uri_resolve (YelpUri *uri)
245 YelpUriPrivate *priv = GET_PRIV (uri);
247 if (priv->res_base && !yelp_uri_is_resolved (priv->res_base)) {
248 g_signal_connect_swapped (priv->res_base, "resolved",
249 G_CALLBACK (resolve_start),
250 uri);
251 yelp_uri_resolve (priv->res_base);
253 else {
254 resolve_start (uri);
258 void
259 yelp_uri_resolve_sync (YelpUri *uri)
261 YelpUriPrivate *priv = GET_PRIV (uri);
263 if (priv->doctype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
264 return;
266 if (priv->res_base)
267 yelp_uri_resolve_sync (priv->res_base);
269 g_object_ref (uri);
270 resolve_sync (uri);
271 resolve_final (uri);
274 /* We want code to be able to do something like this:
276 * if (yelp_uri_get_document_type (uri) != YELP_URI_DOCUMENT_TYPE_UNRESOLVED) {
277 * g_signal_connect (uri, "resolve", callback, data);
278 * yelp_uri_resolve (uri);
281 * Resolving happens in a separate thread, though, so if that thread can change
282 * the document type, we have a race condition. So here's the rules we play by:
284 * 1) None of the getters except the document type getter can return real data
285 * while the URI is unresolved. They all do a resolved check first, and
286 * return NULL if the URI is not resolved.
288 * 2) The threaded resolver functions can modify anything but the document
289 * type. They are the only things that are allowed to modify that data.
291 * 3) The resolver thread is not allowed to modify the document type. When
292 * it's done, it queues an async function to set the document type and
293 * emit "resolved" in the main thread.
295 * 4) Once a URI is resolved, it is immutable.
297 static void
298 resolve_start (YelpUri *uri)
300 YelpUriPrivate *priv = GET_PRIV (uri);
302 if (priv->resolver == NULL) {
303 g_object_ref (uri);
304 priv->resolver = g_thread_create ((GThreadFunc) resolve_async,
305 uri, FALSE, NULL);
309 static void
310 resolve_sync (YelpUri *uri)
312 gchar *tmp;
313 YelpUriPrivate *priv = GET_PRIV (uri);
315 if (g_str_has_prefix (priv->res_arg, "ghelp:")
316 || g_str_has_prefix (priv->res_arg, "gnome-help:")) {
317 resolve_ghelp_uri (uri);
319 else if (g_str_has_prefix (priv->res_arg, "help:")) {
320 resolve_help_uri (uri);
322 else if (g_str_has_prefix (priv->res_arg, "help-list:")) {
323 resolve_help_list_uri (uri);
325 else if (g_str_has_prefix (priv->res_arg, "file:")) {
326 resolve_file_uri (uri);
328 else if (g_str_has_prefix (priv->res_arg, "man:")) {
329 resolve_man_uri (uri);
331 else if (g_str_has_prefix (priv->res_arg, "info:")) {
332 resolve_info_uri (uri);
334 else if (g_str_has_prefix (priv->res_arg, "xref:")) {
335 YelpUriPrivate *base_priv;
336 if (priv->res_base == NULL) {
337 priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
338 return;
340 base_priv = GET_PRIV (priv->res_base);
341 switch (base_priv->doctype) {
342 case YELP_URI_DOCUMENT_TYPE_UNRESOLVED:
343 break;
344 case YELP_URI_DOCUMENT_TYPE_DOCBOOK:
345 case YELP_URI_DOCUMENT_TYPE_MALLARD:
346 case YELP_URI_DOCUMENT_TYPE_INFO:
347 resolve_xref_uri (uri);
348 break;
349 case YELP_URI_DOCUMENT_TYPE_MAN: {
350 gchar *tmp = g_strconcat ("man:", priv->res_arg + 5, NULL);
351 g_free (priv->res_arg);
352 priv->res_arg = tmp;
353 resolve_man_uri (uri);
354 break;
356 case YELP_URI_DOCUMENT_TYPE_TEXT:
357 case YELP_URI_DOCUMENT_TYPE_HTML:
358 case YELP_URI_DOCUMENT_TYPE_XHTML:
359 resolve_file_path (uri);
360 break;
361 case YELP_URI_DOCUMENT_TYPE_HELP_LIST:
362 /* FIXME: what do we do? */
363 break;
364 case YELP_URI_DOCUMENT_TYPE_NOT_FOUND:
365 case YELP_URI_DOCUMENT_TYPE_EXTERNAL:
366 case YELP_URI_DOCUMENT_TYPE_ERROR:
367 break;
370 else if (strchr (priv->res_arg, ':')) {
371 priv->tmptype = YELP_URI_DOCUMENT_TYPE_EXTERNAL;
373 else {
374 resolve_file_path (uri);
377 /* We _always_ want to have a non-null fulluri, so check for it
378 * having been set here and, if we can't think of something
379 * better, set it to res_arg. */
380 if (!priv->fulluri) {
381 priv->fulluri = g_strdup (priv->res_arg);
385 static void
386 resolve_async (YelpUri *uri)
388 resolve_sync (uri);
389 g_idle_add ((GSourceFunc) resolve_final, uri);
392 static gboolean
393 resolve_final (YelpUri *uri)
395 YelpUriPrivate *priv = GET_PRIV (uri);
397 priv->resolver = NULL;
399 if (priv->tmptype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
400 priv->doctype = priv->tmptype;
401 else
402 priv->doctype = YELP_URI_DOCUMENT_TYPE_ERROR;
404 if (priv->res_base) {
405 g_object_unref (priv->res_base);
406 priv->res_base = NULL;
409 if (priv->res_arg) {
410 g_free (priv->res_arg);
411 priv->res_arg = NULL;
414 g_signal_emit (uri, uri_signals[RESOLVED], 0);
415 g_object_unref (uri);
416 return FALSE;
419 /******************************************************************************/
421 gboolean
422 yelp_uri_is_resolved (YelpUri *uri)
424 YelpUriPrivate *priv = GET_PRIV (uri);
425 return priv->doctype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED;
428 YelpUriDocumentType
429 yelp_uri_get_document_type (YelpUri *uri)
431 YelpUriPrivate *priv = GET_PRIV (uri);
432 return priv->doctype;
435 gchar *
436 yelp_uri_get_document_uri (YelpUri *uri)
438 YelpUriPrivate *priv = GET_PRIV (uri);
439 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
440 return NULL;
442 /* There's some client code where it makes sense to want a
443 * document uri, whether or not it conforms to a scheme we really
444 * understand. For example, we might want to look up whether the
445 * given page is currently being visited. */
446 if ((!priv->docuri) && priv->fulluri) {
447 return g_strdup (priv->fulluri);
450 return g_strdup (priv->docuri);
453 gchar *
454 yelp_uri_get_canonical_uri (YelpUri *uri)
456 YelpUriPrivate *priv = GET_PRIV (uri);
457 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
458 return NULL;
459 return g_strdup (priv->fulluri);
462 GFile *
463 yelp_uri_get_file (YelpUri *uri)
465 YelpUriPrivate *priv = GET_PRIV (uri);
466 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
467 return NULL;
468 return priv->gfile ? g_object_ref (priv->gfile) : NULL;
471 gchar **
472 yelp_uri_get_search_path (YelpUri *uri)
474 YelpUriPrivate *priv = GET_PRIV (uri);
475 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
476 return NULL;
477 return g_strdupv (priv->search_path);
480 gchar *
481 yelp_uri_get_page_id (YelpUri *uri)
483 YelpUriPrivate *priv = GET_PRIV (uri);
484 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
485 return NULL;
486 return g_strdup (priv->page_id);
489 gchar *
490 yelp_uri_get_frag_id (YelpUri *uri)
492 YelpUriPrivate *priv = GET_PRIV (uri);
493 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
494 return NULL;
495 return g_strdup (priv->frag_id);
498 gchar *
499 yelp_uri_get_query (YelpUri *uri,
500 const gchar *key)
502 YelpUriPrivate *priv = GET_PRIV (uri);
503 const gchar *ret = g_hash_table_lookup (priv->query, key);
504 if (ret)
505 return g_strdup (ret);
506 else
507 return NULL;
510 /******************************************************************************/
512 gchar *
513 yelp_uri_locate_file_uri (YelpUri *uri,
514 const gchar *filename)
516 YelpUriPrivate *priv = GET_PRIV (uri);
517 GFile *gfile;
518 gchar *fullpath;
519 gchar *returi = NULL;
520 gint i;
521 for (i = 0; priv->search_path[i] != NULL; i++) {
522 fullpath = g_strconcat (priv->search_path[i],
523 G_DIR_SEPARATOR_S,
524 filename,
525 NULL);
526 if (g_file_test (fullpath, G_FILE_TEST_EXISTS)) {
527 gfile = g_file_new_for_path (fullpath);
528 returi = g_file_get_uri (gfile);
529 g_object_unref (gfile);
531 g_free (fullpath);
532 if (returi)
533 break;
535 return returi;
538 /******************************************************************************/
540 static void
541 resolve_file_uri (YelpUri *uri)
543 YelpUriPrivate *priv = GET_PRIV (uri);
544 gchar *uristr;
545 const gchar *hash = strchr (priv->res_arg, '#');
547 if (hash) {
548 uristr = g_strndup (priv->res_arg, hash - priv->res_arg);
549 hash++;
551 else
552 uristr = priv->res_arg;
554 priv->gfile = g_file_new_for_uri (uristr);
556 resolve_gfile (uri, hash);
559 static void
560 resolve_file_path (YelpUri *uri)
562 YelpUriPrivate *base_priv = NULL;
563 YelpUriPrivate *priv = GET_PRIV (uri);
564 gchar *path;
565 const gchar *hash;
567 /* Treat xref: URIs like relative file paths */
568 if (g_str_has_prefix (priv->res_arg, "xref:")) {
569 gchar *tmp = g_strdup (priv->res_arg + 5);
570 g_free (priv->res_arg);
571 priv->res_arg = tmp;
574 if (priv->res_base)
575 base_priv = GET_PRIV (priv->res_base);
577 hash = strchr (priv->res_arg, '#');
578 if (hash) {
579 path = g_strndup (priv->res_arg, hash - priv->res_arg);
580 hash++;
582 else
583 path = priv->res_arg;
585 if (priv->res_arg[0] == '/') {
586 priv->gfile = g_file_new_for_path (path);
588 else if (base_priv && base_priv->gfile) {
589 GFileInfo *info;
590 info = g_file_query_info (base_priv->gfile,
591 G_FILE_ATTRIBUTE_STANDARD_TYPE,
592 G_FILE_QUERY_INFO_NONE,
593 NULL, NULL);
594 if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR) {
595 GFile *parent = g_file_get_parent (base_priv->gfile);
596 priv->gfile = g_file_resolve_relative_path (parent, path);
597 g_object_unref (parent);
599 else {
600 priv->gfile = g_file_resolve_relative_path (base_priv->gfile, path);
603 else {
604 gchar *cur;
605 GFile *curfile;
606 cur = g_get_current_dir ();
607 curfile = g_file_new_for_path (cur);
608 priv->gfile = g_file_resolve_relative_path (curfile, path);
609 g_object_unref (curfile);
610 g_free (cur);
613 resolve_gfile (uri, hash);
616 static void
617 resolve_data_dirs (YelpUri *ret,
618 const gchar *subdir,
619 const gchar *docid,
620 const gchar *pageid,
621 gboolean langfirst)
623 const gchar * const *sdatadirs = g_get_system_data_dirs ();
624 const gchar * const *langs = g_get_language_names ();
625 /* The strings are still owned by GLib; we just own the array. */
626 gchar **datadirs;
627 YelpUriPrivate *priv = GET_PRIV (ret);
628 gchar *filename = NULL;
629 gchar **searchpath = NULL;
630 gint searchi, searchmax;
631 gint datadir_i, lang_i;
633 datadirs = g_new0 (gchar *, g_strv_length ((gchar **) sdatadirs) + 2);
634 datadirs[0] = (gchar *) g_get_user_data_dir ();
635 for (datadir_i = 0; sdatadirs[datadir_i]; datadir_i++)
636 datadirs[datadir_i + 1] = (gchar *) sdatadirs[datadir_i];
638 searchi = 0;
639 searchmax = 10;
640 searchpath = g_new0 (gchar *, 10);
642 for (datadir_i = 0; datadirs[datadir_i]; datadir_i++) {
643 for (lang_i = 0; langs[lang_i]; lang_i++) {
644 gchar *helpdir = g_build_filename (datadirs[datadir_i],
645 subdir,
646 langfirst ? langs[lang_i] : docid,
647 langfirst ? docid : langs[lang_i],
648 NULL);
649 if (!g_file_test (helpdir, G_FILE_TEST_IS_DIR)) {
650 g_free (helpdir);
651 continue;
654 if (searchi + 1 >= searchmax) {
655 searchmax += 5;
656 searchpath = g_renew (gchar *, searchpath, searchmax);
658 searchpath[searchi] = helpdir;
659 searchpath[++searchi] = NULL;
661 if (priv->tmptype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
662 /* We've already found it. We're just adding to the search path now. */
663 continue;
665 filename = g_strdup_printf ("%s/index.page", helpdir);
666 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
667 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
668 g_free (filename);
669 filename = g_strdup (helpdir);
670 continue;
672 g_free (filename);
674 if (langfirst) {
675 filename = g_strdup_printf ("%s/index.docbook", helpdir);
676 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
677 priv->tmptype = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
678 continue;
680 g_free (filename);
682 else {
683 filename = g_strdup_printf ("%s/%s.xml", helpdir, pageid);
684 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
685 priv->tmptype = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
686 continue;
688 g_free (filename);
691 filename = g_strdup_printf ("%s/%s.html", helpdir, pageid);
692 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
693 priv->tmptype = YELP_URI_DOCUMENT_TYPE_HTML;
694 continue;
696 g_free (filename);
698 filename = g_strdup_printf ("%s/%s.xhtml", helpdir, pageid);
699 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
700 priv->tmptype = YELP_URI_DOCUMENT_TYPE_XHTML;
701 continue;
703 g_free (filename);
704 } /* end for langs */
705 } /* end for datadirs */
707 g_free (datadirs);
708 if (priv->tmptype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED) {
709 g_strfreev (searchpath);
710 priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
712 else {
713 priv->gfile = g_file_new_for_path (filename);
714 priv->search_path = searchpath;
718 static void
719 resolve_ghelp_uri (YelpUri *uri)
721 /* ghelp:/path/to/file
722 * ghelp:document[/file][?page][#frag]
724 YelpUriPrivate *priv = GET_PRIV (uri);
725 gchar *document, *slash, *query, *hash;
726 gchar *colon, *c; /* do not free */
728 colon = strchr (priv->res_arg, ':');
729 if (!colon) {
730 priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
731 return;
734 slash = query = hash = NULL;
735 for (c = colon; *c != '\0'; c++) {
736 if (*c == '#' && hash == NULL)
737 hash = c;
738 else if (*c == '?' && query == NULL && hash == NULL)
739 query = c;
740 else if (*c == '/' && slash == NULL && query == NULL && hash == NULL)
741 slash = c;
744 if (slash || query || hash)
745 document = g_strndup (colon + 1,
746 (slash ? slash : (query ? query : hash)) - colon - 1);
747 else
748 document = g_strdup (colon + 1);
750 if (slash && (query || hash))
751 slash = g_strndup (slash + 1,
752 (query ? query : hash) - slash - 1);
753 else if (slash)
754 slash = g_strdup (slash + 1);
756 if (query && hash)
757 query = g_strndup (query + 1,
758 hash - query - 1);
759 else if (query)
760 query = g_strdup (query + 1);
762 if (hash)
763 hash = g_strdup (hash + 1);
765 if (*(colon + 1) == '/') {
766 gchar *newuri;
767 newuri = g_strdup_printf ("file:/%s", slash);
768 g_free (priv->res_arg);
769 priv->res_arg = newuri;
770 resolve_file_uri (uri);
772 else {
773 resolve_data_dirs (uri, "gnome/help", document, slash ? slash : document, FALSE);
776 if (query && hash) {
777 priv->page_id = query;
778 priv->frag_id = hash;
780 else if (query) {
781 priv->page_id = query;
782 if (priv->tmptype != YELP_URI_DOCUMENT_TYPE_MALLARD)
783 priv->frag_id = g_strdup (query);
785 else if (hash) {
786 priv->page_id = hash;
787 priv->frag_id = g_strdup (hash);
790 if (priv->frag_id && g_str_has_prefix (priv->frag_id, "search=")) {
791 g_free (priv->frag_id);
792 priv->frag_id = NULL;
795 priv->docuri = g_strconcat ("ghelp:", document,
796 slash ? "/" : NULL,
797 slash, NULL);
799 priv->fulluri = g_strconcat (priv->docuri,
800 priv->page_id ? "?" : "",
801 priv->page_id ? priv->page_id : "",
802 priv->frag_id ? "#" : "",
803 priv->frag_id ? priv->frag_id : "",
804 NULL);
806 g_free (document);
807 g_free (slash);
808 return;
811 static void
812 resolve_help_uri (YelpUri *uri)
814 /* help:document[/page][?query][#frag]
816 YelpUriPrivate *priv = GET_PRIV (uri);
817 gchar *document, *slash, *query, *hash;
818 gchar *colon, *c; /* do not free */
820 colon = strchr (priv->res_arg, ':');
821 if (!colon) {
822 priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
823 return;
826 slash = query = hash = NULL;
827 for (c = colon; *c != '\0'; c++) {
828 if (*c == '#' && hash == NULL)
829 hash = c;
830 else if (*c == '?' && query == NULL && hash == NULL)
831 query = c;
832 else if (*c == '/' && slash == NULL && query == NULL && hash == NULL)
833 slash = c;
836 if (slash || query || hash)
837 document = g_strndup (colon + 1,
838 (slash ? slash : (query ? query : hash)) - colon - 1);
839 else
840 document = g_strdup (colon + 1);
842 if (slash && (query || hash))
843 slash = g_strndup (slash + 1,
844 (query ? query : hash) - slash - 1);
845 else if (slash)
846 slash = g_strdup (slash + 1);
848 if (query && hash)
849 query = g_strndup (query + 1,
850 hash - query - 1);
851 else if (query)
852 query = g_strdup (query + 1);
854 if (query) {
855 gchar **keyvals = g_strsplit (query, "&", 0);
856 gint i;
858 for (i = 0; keyvals[i]; i++) {
859 gchar *key, *val;
860 val = strchr (keyvals[i], '=');
861 if (val == NULL)
862 continue;
863 key = g_uri_unescape_segment (keyvals[i], val, NULL);
864 val = g_uri_unescape_string (val + 1, NULL);
866 g_hash_table_insert (priv->query, key, val);
869 g_strfreev (keyvals);
872 if (hash)
873 hash = g_strdup (hash + 1);
875 priv->page_id = (slash ? slash : g_strdup ("index"));
876 resolve_data_dirs (uri, "help", document, priv->page_id, TRUE);
878 if (hash)
879 priv->frag_id = hash;
880 if (priv->frag_id && g_str_has_prefix (priv->frag_id, "search=")) {
881 g_free (priv->frag_id);
882 priv->frag_id = NULL;
885 priv->docuri = g_strconcat ("help:", document, NULL);
887 priv->fulluri = g_strconcat (priv->docuri,
888 priv->page_id ? "/" : "",
889 priv->page_id ? priv->page_id : "",
890 query ? "?" : "",
891 query ? query : "",
892 priv->frag_id ? "#" : "",
893 priv->frag_id ? priv->frag_id : "",
894 NULL);
896 g_free (query);
897 g_free (document);
898 return;
901 static void
902 resolve_help_list_uri (YelpUri *uri)
904 YelpUriPrivate *priv = GET_PRIV (uri);
905 priv->docuri = g_strdup ("help-list:");
906 priv->fulluri = g_strdup (priv->res_arg);
907 priv->page_id = g_strdup ("index");
908 priv->tmptype = YELP_URI_DOCUMENT_TYPE_HELP_LIST;
912 Resolve a manual file's path using 'man -w'. section may be NULL,
913 otherwise should be the section of the manual (ie should have dealt
914 with empty strings before calling this!) Returns NULL if the file
915 can't be found.
917 static gchar*
918 find_man_path (gchar* name, gchar* section)
920 gchar* argv[] = { "man", "-w", NULL, NULL, NULL };
921 gchar *ystdout = NULL;
922 gint status;
923 gchar **lines;
924 GError *error = NULL;
926 /* Syntax for man is "man -w <section> <name>", possibly omitting
927 section */
928 if (section) {
929 argv[2] = section;
930 argv[3] = name;
931 } else {
932 argv[2] = name;
935 if (!g_spawn_sync (NULL, argv, NULL,
936 G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
937 NULL, NULL,
938 &ystdout, NULL, &status, &error)) {
939 g_warning ("Couldn't find path for %s(%s). Error: %s",
940 name, section, error->message);
941 g_error_free (error);
944 if (status == 0) {
945 lines = g_strsplit (ystdout, "\n", 2);
946 g_free (ystdout);
947 ystdout = g_strdup (lines[0]);
949 g_strfreev (lines);
950 return ystdout;
951 } else {
952 g_free (ystdout);
953 return NULL;
957 static void
958 resolve_man_uri (YelpUri *uri)
960 YelpUriPrivate *priv = GET_PRIV (uri);
961 /* man:/path/to/file
962 * man:name(section)
963 * man:name.section
964 * man:name
967 /* Search via regular expressions for name, name(section) and
968 * name.section (assuming that name doesn't contain forward
969 * slashes or other nasties)
971 * If these don't match, assume that we were given a filename
972 * (absolute iff it starts with a /).
974 static GRegex* man_not_path = NULL;
975 GError *error = NULL;
976 GMatchInfo *match_info = NULL;
977 gchar *name, *section, *hash;
978 gchar *path;
980 if (!man_not_path) {
981 /* Match group 1 should contain the name; then one of groups 3
982 * and 4 will contain the section if there was one. Group 6
983 * will contain any hash fragment. */
984 man_not_path = g_regex_new ("man:((?:[^ /.()#]|\\.(?=[^0-9]))+)"
985 "(\\(([0-9A-Za-z]+)\\)|\\.([0-9A-Za-z]+)|)"
986 "(#([^/ ()]+))?",
987 0, 0, &error);
988 if (!man_not_path) {
989 g_error ("Error with regex in man uri: %s\n",
990 error->message);
994 if (!g_regex_match (man_not_path, priv->res_arg,
995 0, &match_info)) {
996 /* The regexp didn't match, so treat as a file name. */
997 gchar *newuri;
998 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
999 newuri = g_strdup_printf ("file:%s", priv->res_arg + 4);
1000 g_free (priv->res_arg);
1001 priv->res_arg = newuri;
1002 resolve_file_uri (uri);
1004 else {
1005 /* The regexp matched, so we've got a name/section pair that
1006 * needs resolving. */
1007 name = g_match_info_fetch (match_info, 1);
1008 section = g_match_info_fetch (match_info, 3);
1009 hash = g_match_info_fetch (match_info, 6);
1010 if (!name) {
1011 g_error ("Error matching strings in man uri '%s'",
1012 priv->res_arg);
1014 if ((!section) || (section[0] == '\0')) {
1015 section = g_match_info_fetch (match_info, 4);
1017 if (section && section[0] == '\0') section = NULL;
1019 path = find_man_path (name, section);
1021 if (!path) {
1022 priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
1023 return;
1025 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1026 priv->gfile = g_file_new_for_path (path);
1027 priv->docuri = g_strdup ("man:");
1028 priv->fulluri = g_strconcat ("man:", name, ".", section, NULL);
1029 priv->page_id = g_strconcat (name, ".", section, NULL);
1030 resolve_gfile (uri, NULL);
1032 if (hash && hash[0] != '\0')
1033 resolve_page_and_frag (uri, hash + 1);
1035 g_free (path);
1036 g_match_info_free (match_info);
1040 static void
1041 resolve_info_uri (YelpUri *uri)
1043 YelpUriPrivate *priv = GET_PRIV (uri);
1044 /* info:/path/to/file
1045 * info:name#node
1046 * info:name
1047 * info:(name)node
1048 * info:(name)
1050 static gchar **infopath = NULL;
1051 gchar *name = NULL;
1052 gchar *sect = NULL;
1053 gchar *fullpath = NULL;
1054 /* do not free */
1055 gchar *colon;
1056 gint infopath_i, suffix_i;
1058 if (g_str_has_prefix (priv->res_arg, "info:/")) {
1059 gchar *newuri;
1060 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1061 newuri = g_strdup_printf ("file:%s", priv->res_arg + 5);
1062 g_free (priv->res_arg);
1063 priv->res_arg = newuri;
1064 resolve_file_uri (uri);
1065 return;
1068 if (!infopath) {
1069 /* Initialize infopath only once */
1071 /* Use the same logic as the info program. If INFOPATH is not
1072 specified, use the default. If it is specified, just use it
1073 unless it ends with a colon, in which case we add the
1074 default as a suffix.
1076 const gchar *env = g_getenv ("INFOPATH");
1077 gchar *paths;
1078 if (!env || env[0] == '\0')
1079 paths = g_strdup (default_info_path);
1080 else if (env[strlen (env)-1] == ':')
1081 paths = g_strconcat (env, default_info_path, NULL);
1082 else
1083 paths = g_strdup (env);
1085 infopath = g_strsplit (paths, ":", 0);
1087 g_free (paths);
1090 colon = strchr (priv->res_arg, ':');
1091 if (colon)
1092 colon++;
1093 else
1094 colon = (gchar *) priv->res_arg;
1096 if (colon[0] == '(') {
1097 const gchar *rbrace = strchr (colon, ')');
1098 if (rbrace) {
1099 name = g_strndup (colon + 1, rbrace - colon - 1);
1100 sect = g_strdup (rbrace + 1);
1103 else {
1104 const gchar *hash = strchr (colon, '#');
1105 if (hash) {
1106 name = g_strndup (colon, hash - colon);
1107 sect = g_strdup (hash + 1);
1109 else {
1110 name = g_strdup (colon);
1111 sect = NULL;
1115 for (infopath_i = 0; infopath[infopath_i]; infopath_i++) {
1116 if (!g_file_test (infopath[infopath_i], G_FILE_TEST_IS_DIR))
1117 continue;
1118 for (suffix_i = 0; infosuffix[suffix_i]; suffix_i++) {
1119 fullpath = g_strconcat (infopath[infopath_i], "/",
1120 name, infosuffix[suffix_i], NULL);
1121 if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR))
1122 break;
1123 g_free (fullpath);
1124 fullpath = NULL;
1126 if (fullpath != NULL)
1127 break;
1130 if (fullpath) {
1131 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1132 priv->gfile = g_file_new_for_path (fullpath);
1133 priv->docuri = g_strconcat ("info:", name, NULL);
1134 if (sect) {
1135 priv->fulluri = g_strconcat ("info:", name, "#", sect, NULL);
1136 priv->page_id = g_strdup (sect);
1137 priv->frag_id = sect;
1138 sect = NULL; /* steal memory */
1140 else
1141 priv->fulluri = g_strdup (priv->docuri);
1142 resolve_gfile (uri, NULL);
1143 } else {
1144 gchar *res_arg = priv->res_arg;
1145 priv->res_arg = g_strconcat ("man:", name, NULL);
1146 resolve_man_uri (uri);
1147 if (priv->tmptype == YELP_URI_DOCUMENT_TYPE_MAN) {
1148 g_free (priv->res_arg);
1149 priv->res_arg = res_arg;
1151 else {
1152 g_free (res_arg);
1153 priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
1156 g_free (fullpath);
1157 g_free (name);
1158 g_free (sect);
1161 static void
1162 resolve_xref_uri (YelpUri *uri)
1164 YelpUriPrivate *priv = GET_PRIV (uri);
1165 const gchar *arg = priv->res_arg + 5;
1166 YelpUriPrivate *base_priv = GET_PRIV (priv->res_base);
1168 priv->tmptype = base_priv->doctype;
1169 priv->gfile = g_object_ref (base_priv->gfile);
1170 priv->search_path = g_strdupv (base_priv->search_path);
1171 priv->docuri = g_strdup (base_priv->docuri);
1173 if (arg[0] == '#') {
1174 priv->page_id = g_strdup (base_priv->page_id);
1175 priv->frag_id = g_strdup (arg + 1);
1177 else {
1178 gchar *hash = strchr (arg, '#');
1179 if (hash) {
1180 priv->page_id = g_strndup (arg, hash - arg);
1181 priv->frag_id = g_strdup (hash + 1);
1183 else {
1184 priv->page_id = g_strdup (arg);
1185 priv->frag_id = NULL;
1188 if (priv->page_id && priv->page_id[0] == '\0') {
1189 g_free (priv->page_id);
1190 priv->page_id = NULL;
1193 if (priv->page_id &&
1194 g_str_has_prefix (priv->docuri, "info:")) {
1196 Special characters get url-encoded when they get clicked on
1197 as links. Info files, at least, don't want that so decode
1198 the url again here.
1200 gchar* tmp = priv->page_id;
1201 priv->page_id = g_uri_unescape_string (tmp, NULL);
1202 g_free (tmp);
1205 if (g_str_has_prefix (priv->docuri, "ghelp:"))
1206 priv->fulluri = g_strconcat (priv->docuri,
1207 priv->page_id ? "?" : "",
1208 priv->page_id ? priv->page_id : "",
1209 priv->frag_id ? "#" : "",
1210 priv->frag_id ? priv->frag_id : "",
1211 NULL);
1212 else if (g_str_has_prefix (priv->docuri, "help:"))
1213 priv->fulluri = g_strconcat (priv->docuri,
1214 priv->page_id ? "/" : "",
1215 priv->page_id ? priv->page_id : "",
1216 priv->frag_id ? "#" : "",
1217 priv->frag_id ? priv->frag_id : "",
1218 NULL);
1219 else if (g_str_has_prefix (priv->docuri, "file:") ||
1220 g_str_has_prefix (priv->docuri, "info:") )
1221 priv->fulluri = g_strconcat (priv->docuri,
1222 (priv->page_id || priv->frag_id) ? "#" : "",
1223 priv->page_id ? priv->page_id : "",
1224 priv->frag_id ? "#" : "",
1225 priv->frag_id,
1226 NULL);
1227 else
1228 /* FIXME: other URI schemes */
1229 priv->fulluri = g_strconcat (priv->docuri,
1230 priv->page_id ? "#" : "",
1231 priv->page_id,
1232 NULL);
1235 static void
1236 resolve_page_and_frag (YelpUri *uri, const gchar *arg)
1238 YelpUriPrivate *priv = GET_PRIV (uri);
1239 gchar *hash;
1241 if (!arg || arg[0] == '\0')
1242 return;
1244 hash = strchr (arg, '#');
1245 if (hash) {
1246 priv->page_id = g_strndup (arg, hash - arg);
1247 priv->frag_id = g_strdup (hash + 1);
1248 } else {
1249 priv->page_id = g_strdup (arg);
1250 priv->frag_id = g_strdup (arg);
1252 return;
1255 static void
1256 resolve_gfile (YelpUri *uri, const gchar *hash)
1258 YelpUriPrivate *priv = GET_PRIV (uri);
1259 GFileInfo *info;
1260 GError *error = NULL;
1262 info = g_file_query_info (priv->gfile,
1263 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
1264 G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE,
1265 G_FILE_QUERY_INFO_NONE,
1266 NULL, &error);
1267 if (error) {
1268 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
1269 priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
1271 else
1272 priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
1273 g_error_free (error);
1274 return;
1277 if (priv->search_path == NULL) {
1278 if (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) ==
1279 G_FILE_TYPE_DIRECTORY) {
1280 priv->search_path = g_new0 (gchar *, 2);
1281 priv->search_path[0] = g_file_get_path (priv->gfile);
1282 } else {
1283 GFile *parent = g_file_get_parent (priv->gfile);
1284 priv->search_path = g_new0 (gchar *, 2);
1285 priv->search_path[0] = g_file_get_path (parent);
1286 g_object_unref (parent);
1290 if (priv->tmptype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED) {
1291 gchar **splithash = NULL;
1292 if (hash)
1293 splithash = g_strsplit (hash, "#", 2);
1294 priv->tmptype = YELP_URI_DOCUMENT_TYPE_EXTERNAL;
1296 if (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) ==
1297 G_FILE_TYPE_DIRECTORY) {
1298 GFile *child = g_file_get_child (priv->gfile, "index.page");
1299 if (g_file_query_exists (child, NULL)) {
1300 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
1301 if (splithash) {
1302 if (priv->page_id == NULL)
1303 priv->page_id = g_strdup (splithash[0]);
1304 if (priv->frag_id == NULL && splithash[0])
1305 priv->frag_id = g_strdup (splithash[1]);
1308 else if (yelp_settings_get_editor_mode (yelp_settings_get_default ())) {
1309 g_object_unref (child);
1310 child = g_file_get_child (priv->gfile, "index.page.stub");
1311 if (g_file_query_exists (child, NULL)) {
1312 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
1313 if (splithash) {
1314 if (priv->page_id == NULL)
1315 priv->page_id = g_strdup (splithash[0]);
1316 if (priv->frag_id == NULL && splithash[0])
1317 priv->frag_id = g_strdup (splithash[1]);
1321 g_object_unref (child);
1323 else {
1324 gchar *basename;
1325 const gchar *mime_type = g_file_info_get_attribute_string (info,
1326 G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
1327 basename = g_file_get_basename (priv->gfile);
1328 if (g_str_has_suffix (basename, ".page")) {
1329 GFile *old;
1330 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
1331 old = priv->gfile;
1332 priv->gfile = g_file_get_parent (old);
1333 if (priv->page_id == NULL) {
1334 /* File names aren't really page IDs, so we stick an illegal character
1335 on the beginning so it can't possibly be confused with a real page
1336 ID. Then we let YelpMallardDocument map file names to pages IDs.
1338 gchar *tmp = g_file_get_basename (old);
1339 priv->page_id = g_strconcat (G_DIR_SEPARATOR_S, tmp, NULL);
1340 g_free (tmp);
1342 if (priv->frag_id == NULL)
1343 priv->frag_id = g_strdup (hash);
1344 g_object_unref (old);
1346 else if (g_str_equal (mime_type, "text/xml") ||
1347 g_str_equal (mime_type, "application/docbook+xml") ||
1348 g_str_equal (mime_type, "application/xml")) {
1349 priv->tmptype = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
1350 if (splithash) {
1351 if (priv->page_id == NULL)
1352 priv->page_id = g_strdup (splithash[0]);
1353 if (priv->frag_id == NULL && splithash[0])
1354 priv->frag_id = g_strdup (splithash[1]);
1357 else if (g_str_equal (mime_type, "text/html") ||
1358 g_str_equal (mime_type, "application/xhtml+xml")) {
1359 GFile *parent = g_file_get_parent (priv->gfile);
1360 priv->docuri = g_file_get_uri (parent);
1361 g_object_unref (parent);
1362 priv->tmptype = mime_type[0] == 't' ? YELP_URI_DOCUMENT_TYPE_HTML : YELP_URI_DOCUMENT_TYPE_XHTML;
1363 if (priv->page_id == NULL)
1364 priv->page_id = g_file_get_basename (priv->gfile);
1365 if (priv->frag_id == NULL)
1366 priv->frag_id = g_strdup (hash);
1367 if (priv->fulluri == NULL) {
1368 gchar *fulluri;
1369 fulluri = g_file_get_uri (priv->gfile);
1370 priv->fulluri = g_strconcat (fulluri,
1371 priv->frag_id ? "#" : NULL,
1372 priv->frag_id,
1373 NULL);
1374 g_free (fulluri);
1377 else if (g_str_equal (mime_type, "application/x-gzip")) {
1378 if (g_str_has_suffix (basename, ".info.gz"))
1379 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1380 else if (is_man_path (basename, "gz"))
1381 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1383 else if (g_str_equal (mime_type, "application/x-bzip")) {
1384 if (g_str_has_suffix (basename, ".info.bz2"))
1385 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1386 else if (is_man_path (basename, "bz2"))
1387 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1389 else if (g_str_equal (mime_type, "application/x-lzma")) {
1390 if (g_str_has_suffix (basename, ".info.lzma"))
1391 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1392 else if (is_man_path (basename, "lzma"))
1393 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1395 else if (g_str_equal (mime_type, "application/octet-stream")) {
1396 if (g_str_has_suffix (basename, ".info"))
1397 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1398 else if (is_man_path (basename, NULL))
1399 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1401 else if (g_str_equal (mime_type, "text/plain")) {
1402 if (g_str_has_suffix (basename, ".info"))
1403 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1404 else if (is_man_path (basename, NULL))
1405 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1406 else
1407 priv->tmptype = YELP_URI_DOCUMENT_TYPE_TEXT;
1408 if (priv->frag_id == NULL)
1409 priv->frag_id = g_strdup (hash);
1411 else if (g_str_equal (mime_type, "text/x-readme")) {
1412 priv->tmptype = YELP_URI_DOCUMENT_TYPE_TEXT;
1414 else {
1415 priv->tmptype = YELP_URI_DOCUMENT_TYPE_EXTERNAL;
1418 if (splithash)
1419 g_strfreev (splithash);
1422 if (priv->docuri == NULL)
1423 priv->docuri = g_file_get_uri (priv->gfile);
1425 if (priv->fulluri == NULL)
1426 priv->fulluri = g_strconcat (priv->docuri,
1427 (priv->page_id || priv->frag_id) ? "#" : NULL,
1428 priv->page_id ? priv->page_id : "",
1429 priv->frag_id ? "#" : NULL,
1430 priv->frag_id ? priv->frag_id : NULL,
1431 NULL);
1433 g_object_unref (info);
1436 static gboolean
1437 is_man_path (const gchar *path, const gchar *encoding)
1439 gchar **iter = (gchar **) mancats;
1441 if (encoding && *encoding) {
1442 while (iter && *iter) {
1443 gchar *ending = g_strdup_printf ("%s.%s", *iter, encoding);
1444 if (g_str_has_suffix (path, ending)) {
1445 g_free (ending);
1446 return TRUE;
1448 g_free (ending);
1449 iter++;
1451 } else {
1452 while (iter && *iter) {
1453 if (g_str_has_suffix (path, *iter)) {
1454 return TRUE;
1456 iter++;
1459 return FALSE;