Update Chinese (China) translation
[yelp.git] / libyelp / yelp-uri.c
blobf20b167e11d4fc36a141c00d6a9c5350ba6e8736
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, 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 <string.h>
26 #include <stdio.h>
28 #include <glib.h>
29 #include <gio/gio.h>
31 #include "yelp-uri.h"
32 #include "yelp-settings.h"
34 static void yelp_uri_dispose (GObject *object);
35 static void yelp_uri_finalize (GObject *object);
37 static void resolve_start (YelpUri *uri);
38 static void resolve_sync (YelpUri *uri);
39 static void resolve_async (YelpUri *uri);
40 static gboolean resolve_final (YelpUri *uri);
42 static void resolve_file_uri (YelpUri *uri);
43 static void resolve_file_path (YelpUri *uri);
44 static void resolve_data_dirs (YelpUri *uri,
45 const gchar *subdir,
46 const gchar *docid,
47 const gchar *pageid,
48 gboolean langfirst);
49 static void resolve_ghelp_uri (YelpUri *uri);
50 static void resolve_help_uri (YelpUri *uri);
51 static void resolve_help_list_uri (YelpUri *uri);
52 static void resolve_man_uri (YelpUri *uri);
53 static void resolve_info_uri (YelpUri *uri);
54 static void resolve_xref_uri (YelpUri *uri);
55 static void resolve_page_and_frag (YelpUri *uri,
56 const gchar *arg);
57 static void resolve_gfile (YelpUri *uri,
58 const gchar *query,
59 const gchar *hash);
61 static gboolean is_man_path (const gchar *uri,
62 const gchar *encoding);
64 G_DEFINE_TYPE (YelpUri, yelp_uri, G_TYPE_OBJECT)
65 #define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_URI, YelpUriPrivate))
67 typedef struct _YelpUriPrivate YelpUriPrivate;
68 struct _YelpUriPrivate {
69 GThread *resolver;
71 YelpUriDocumentType doctype;
72 YelpUriDocumentType tmptype;
74 gchar *docuri;
75 gchar *fulluri;
76 GFile *gfile;
78 gchar **search_path;
79 gchar *page_id;
80 gchar *frag_id;
82 GHashTable *query;
84 /* Unresolved */
85 YelpUri *res_base;
86 gchar *res_arg;
89 enum {
90 RESOLVED,
91 LAST_SIGNAL
93 static guint uri_signals[LAST_SIGNAL] = {0,};
95 /******************************************************************************/
97 static const gchar *mancats[] = {
98 "0p",
99 "1", "1p", "1g", "1t", "1x", "1ssl", "1m",
100 "2",
101 "3", "3o", "3t", "3p", "3blt", "3nas", "3form", "3menu", "3tiff", "3ssl", "3readline",
102 "3ncurses", "3curses", "3f", "3pm", "3perl", "3qt", "3x", "3X11",
103 "4", "4x",
104 "5", "5snmp", "5x", "5ssl",
105 "6", "6x",
106 "7", "7gcc", "7x", "7ssl",
107 "8", "8l", "9", "0p",
108 NULL
111 static const gchar *infosuffix[] = {
112 ".info",
113 ".info.gz", ".info.bz2", ".info.lzma",
114 ".gz", ".bz2", ".lzma",
115 NULL
118 static const gchar default_info_path[] =
119 "/usr/info:/usr/share/info:/usr/local/info:/usr/local/share/info";
121 /******************************************************************************/
123 static void
124 yelp_uri_class_init (YelpUriClass *klass)
126 GObjectClass *object_class = G_OBJECT_CLASS (klass);
128 object_class->dispose = yelp_uri_dispose;
129 object_class->finalize = yelp_uri_finalize;
131 uri_signals[RESOLVED] =
132 g_signal_new ("resolved",
133 G_OBJECT_CLASS_TYPE (klass),
134 G_SIGNAL_RUN_LAST,
135 0, NULL, NULL,
136 g_cclosure_marshal_VOID__VOID,
137 G_TYPE_NONE, 0);
139 g_type_class_add_private (klass, sizeof (YelpUriPrivate));
142 static void
143 yelp_uri_init (YelpUri *uri)
145 YelpUriPrivate *priv = GET_PRIV (uri);
147 priv->query = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
149 return;
152 static void
153 yelp_uri_dispose (GObject *object)
155 YelpUriPrivate *priv = GET_PRIV (object);
157 if (priv->gfile) {
158 g_object_unref (priv->gfile);
159 priv->gfile = NULL;
162 if (priv->res_base) {
163 g_object_unref (priv->res_base);
164 priv->res_base = NULL;
167 if (priv->query) {
168 g_hash_table_destroy (priv->query);
169 priv->query = NULL;
172 G_OBJECT_CLASS (yelp_uri_parent_class)->dispose (object);
175 static void
176 yelp_uri_finalize (GObject *object)
178 YelpUriPrivate *priv = GET_PRIV (object);
180 g_free (priv->docuri);
181 g_free (priv->fulluri);
182 g_strfreev (priv->search_path);
183 g_free (priv->page_id);
184 g_free (priv->frag_id);
185 g_free (priv->res_arg);
187 G_OBJECT_CLASS (yelp_uri_parent_class)->finalize (object);
190 /******************************************************************************/
192 YelpUri *
193 yelp_uri_new (const gchar *arg)
195 return yelp_uri_new_relative (NULL, arg);
198 YelpUri *
199 yelp_uri_new_relative (YelpUri *base, const gchar *arg)
201 YelpUri *uri;
202 YelpUriPrivate *priv;
204 uri = (YelpUri *) g_object_new (YELP_TYPE_URI, NULL);
206 priv = GET_PRIV (uri);
207 priv->doctype = YELP_URI_DOCUMENT_TYPE_UNRESOLVED;
208 if (base)
209 priv->res_base = g_object_ref (base);
210 priv->res_arg = g_strdup (arg);
212 return uri;
215 YelpUri *
216 yelp_uri_new_search (YelpUri *base,
217 const gchar *text)
219 YelpUri *uri;
220 YelpUriPrivate *priv;
221 gchar *tmp;
223 uri = (YelpUri *) g_object_new (YELP_TYPE_URI, NULL);
225 priv = GET_PRIV (uri);
226 priv->doctype = YELP_URI_DOCUMENT_TYPE_UNRESOLVED;
227 if (base)
228 priv->res_base = g_object_ref (base);
229 tmp = g_uri_escape_string (text, NULL, FALSE);
230 priv->res_arg = g_strconcat("xref:search=", tmp, NULL);
231 g_free (tmp);
233 return uri;
236 /******************************************************************************/
238 void
239 yelp_uri_resolve (YelpUri *uri)
241 YelpUriPrivate *priv = GET_PRIV (uri);
243 if (priv->res_base && !yelp_uri_is_resolved (priv->res_base)) {
244 g_signal_connect_swapped (priv->res_base, "resolved",
245 G_CALLBACK (resolve_start),
246 uri);
247 yelp_uri_resolve (priv->res_base);
249 else {
250 resolve_start (uri);
254 void
255 yelp_uri_resolve_sync (YelpUri *uri)
257 YelpUriPrivate *priv = GET_PRIV (uri);
259 if (priv->doctype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
260 return;
262 if (priv->res_base)
263 yelp_uri_resolve_sync (priv->res_base);
265 g_object_ref (uri);
266 resolve_sync (uri);
267 resolve_final (uri);
270 /* We want code to be able to do something like this:
272 * if (yelp_uri_get_document_type (uri) != YELP_URI_DOCUMENT_TYPE_UNRESOLVED) {
273 * g_signal_connect (uri, "resolve", callback, data);
274 * yelp_uri_resolve (uri);
277 * Resolving happens in a separate thread, though, so if that thread can change
278 * the document type, we have a race condition. So here's the rules we play by:
280 * 1) None of the getters except the document type getter can return real data
281 * while the URI is unresolved. They all do a resolved check first, and
282 * return NULL if the URI is not resolved.
284 * 2) The threaded resolver functions can modify anything but the document
285 * type. They are the only things that are allowed to modify that data.
287 * 3) The resolver thread is not allowed to modify the document type. When
288 * it's done, it queues an async function to set the document type and
289 * emit "resolved" in the main thread.
291 * 4) Once a URI is resolved, it is immutable.
293 static void
294 resolve_start (YelpUri *uri)
296 YelpUriPrivate *priv = GET_PRIV (uri);
298 if (priv->resolver == NULL) {
299 g_object_ref (uri);
300 priv->resolver = g_thread_new ("uri-resolve",
301 (GThreadFunc)(GCallback) resolve_async,
302 uri);
306 static void
307 resolve_sync (YelpUri *uri)
309 YelpUriPrivate *priv = GET_PRIV (uri);
311 if (g_str_has_prefix (priv->res_arg, "ghelp:")
312 || g_str_has_prefix (priv->res_arg, "gnome-help:")) {
313 resolve_ghelp_uri (uri);
315 else if (g_str_has_prefix (priv->res_arg, "help:")) {
316 resolve_help_uri (uri);
318 else if (g_str_has_prefix (priv->res_arg, "help-list:")) {
319 resolve_help_list_uri (uri);
321 else if (g_str_has_prefix (priv->res_arg, "file:")) {
322 resolve_file_uri (uri);
324 else if (g_str_has_prefix (priv->res_arg, "man:")) {
325 resolve_man_uri (uri);
327 else if (g_str_has_prefix (priv->res_arg, "info:")) {
328 resolve_info_uri (uri);
330 else if (g_str_has_prefix (priv->res_arg, "xref:")) {
331 YelpUriPrivate *base_priv;
332 if (priv->res_base == NULL) {
333 priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
334 return;
336 base_priv = GET_PRIV (priv->res_base);
337 switch (base_priv->doctype) {
338 case YELP_URI_DOCUMENT_TYPE_UNRESOLVED:
339 break;
340 case YELP_URI_DOCUMENT_TYPE_DOCBOOK:
341 case YELP_URI_DOCUMENT_TYPE_MALLARD:
342 case YELP_URI_DOCUMENT_TYPE_INFO:
343 resolve_xref_uri (uri);
344 break;
345 case YELP_URI_DOCUMENT_TYPE_MAN: {
346 gchar *tmp = g_strconcat ("man:", priv->res_arg + 5, NULL);
347 g_free (priv->res_arg);
348 priv->res_arg = tmp;
349 resolve_man_uri (uri);
350 break;
352 case YELP_URI_DOCUMENT_TYPE_TEXT:
353 case YELP_URI_DOCUMENT_TYPE_HTML:
354 case YELP_URI_DOCUMENT_TYPE_XHTML:
355 resolve_file_path (uri);
356 break;
357 case YELP_URI_DOCUMENT_TYPE_HELP_LIST:
358 /* FIXME: what do we do? */
359 break;
360 case YELP_URI_DOCUMENT_TYPE_NOT_FOUND:
361 case YELP_URI_DOCUMENT_TYPE_EXTERNAL:
362 case YELP_URI_DOCUMENT_TYPE_ERROR:
363 break;
364 default:
365 g_assert_not_reached ();
366 break;
369 else if (strchr (priv->res_arg, ':')) {
370 priv->tmptype = YELP_URI_DOCUMENT_TYPE_EXTERNAL;
372 else {
373 resolve_file_path (uri);
376 /* We _always_ want to have a non-null fulluri, so check for it
377 * having been set here and, if we can't think of something
378 * better, set it to res_arg. */
379 if (!priv->fulluri) {
380 priv->fulluri = g_strdup (priv->res_arg);
384 static void
385 resolve_async (YelpUri *uri)
387 resolve_sync (uri);
388 g_idle_add ((GSourceFunc) resolve_final, uri);
391 static gboolean
392 resolve_final (YelpUri *uri)
394 YelpUriPrivate *priv = GET_PRIV (uri);
396 priv->resolver = NULL;
398 if (priv->tmptype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
399 priv->doctype = priv->tmptype;
400 else
401 priv->doctype = YELP_URI_DOCUMENT_TYPE_ERROR;
403 if (priv->res_base) {
404 g_object_unref (priv->res_base);
405 priv->res_base = NULL;
408 if (priv->res_arg) {
409 g_free (priv->res_arg);
410 priv->res_arg = NULL;
413 g_signal_emit (uri, uri_signals[RESOLVED], 0);
414 g_object_unref (uri);
415 return FALSE;
418 /******************************************************************************/
420 gboolean
421 yelp_uri_is_resolved (YelpUri *uri)
423 YelpUriPrivate *priv = GET_PRIV (uri);
424 return priv->doctype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED;
427 YelpUriDocumentType
428 yelp_uri_get_document_type (YelpUri *uri)
430 YelpUriPrivate *priv = GET_PRIV (uri);
431 return priv->doctype;
434 gchar *
435 yelp_uri_get_document_uri (YelpUri *uri)
437 YelpUriPrivate *priv = GET_PRIV (uri);
438 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
439 return NULL;
441 /* There's some client code where it makes sense to want a
442 * document uri, whether or not it conforms to a scheme we really
443 * understand. For example, we might want to look up whether the
444 * given page is currently being visited. */
445 if ((!priv->docuri) && priv->fulluri) {
446 return g_strdup (priv->fulluri);
449 return g_strdup (priv->docuri);
452 gchar *
453 yelp_uri_get_canonical_uri (YelpUri *uri)
455 YelpUriPrivate *priv = GET_PRIV (uri);
456 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
457 return NULL;
458 return g_strdup (priv->fulluri);
461 GFile *
462 yelp_uri_get_file (YelpUri *uri)
464 YelpUriPrivate *priv = GET_PRIV (uri);
465 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
466 return NULL;
467 return priv->gfile ? g_object_ref (priv->gfile) : NULL;
470 gchar **
471 yelp_uri_get_search_path (YelpUri *uri)
473 YelpUriPrivate *priv = GET_PRIV (uri);
474 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
475 return NULL;
476 return g_strdupv (priv->search_path);
479 gchar *
480 yelp_uri_get_page_id (YelpUri *uri)
482 YelpUriPrivate *priv = GET_PRIV (uri);
483 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
484 return NULL;
485 return g_strdup (priv->page_id);
488 gchar *
489 yelp_uri_get_frag_id (YelpUri *uri)
491 YelpUriPrivate *priv = GET_PRIV (uri);
492 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
493 return NULL;
494 return g_strdup (priv->frag_id);
497 gchar *
498 yelp_uri_get_query (YelpUri *uri,
499 const gchar *key)
501 YelpUriPrivate *priv = GET_PRIV (uri);
502 const gchar *ret = g_hash_table_lookup (priv->query, key);
503 if (ret)
504 return g_strdup (ret);
505 else
506 return NULL;
509 /******************************************************************************/
511 gchar *
512 yelp_uri_locate_file_uri (YelpUri *uri,
513 const gchar *filename)
515 YelpUriPrivate *priv = GET_PRIV (uri);
516 GFile *gfile;
517 gchar *fullpath;
518 gchar *returi = NULL;
519 gint i;
521 if (g_path_is_absolute (filename)) {
522 if (g_file_test (filename, G_FILE_TEST_EXISTS))
523 return g_filename_to_uri (filename, NULL, NULL);
524 return NULL;
527 for (i = 0; priv->search_path[i] != NULL; i++) {
528 fullpath = g_strconcat (priv->search_path[i],
529 G_DIR_SEPARATOR_S,
530 filename,
531 NULL);
532 if (g_file_test (fullpath, G_FILE_TEST_EXISTS)) {
533 gfile = g_file_new_for_path (fullpath);
534 returi = g_file_get_uri (gfile);
535 g_object_unref (gfile);
537 g_free (fullpath);
538 if (returi)
539 break;
541 return returi;
544 /******************************************************************************/
546 static void
547 resolve_file_uri (YelpUri *uri)
549 YelpUriPrivate *priv = GET_PRIV (uri);
550 gchar *uristr;
551 const gchar *hash = strchr (priv->res_arg, '#');
553 if (hash) {
554 uristr = g_strndup (priv->res_arg, hash - priv->res_arg);
555 hash++;
557 else
558 uristr = priv->res_arg;
560 priv->gfile = g_file_new_for_uri (uristr);
562 resolve_gfile (uri, NULL, hash);
565 static void
566 resolve_file_path (YelpUri *uri)
568 YelpUriPrivate *base_priv = NULL;
569 YelpUriPrivate *priv = GET_PRIV (uri);
570 gchar *path;
571 const gchar *hash;
573 /* Treat xref: URIs like relative file paths */
574 if (g_str_has_prefix (priv->res_arg, "xref:")) {
575 gchar *tmp = g_strdup (priv->res_arg + 5);
576 g_free (priv->res_arg);
577 priv->res_arg = tmp;
580 if (priv->res_base)
581 base_priv = GET_PRIV (priv->res_base);
583 hash = strchr (priv->res_arg, '#');
584 if (hash) {
585 path = g_strndup (priv->res_arg, hash - priv->res_arg);
586 hash++;
588 else
589 path = priv->res_arg;
591 if (priv->res_arg[0] == '/') {
592 priv->gfile = g_file_new_for_path (path);
594 else if (base_priv && base_priv->gfile) {
595 GFileInfo *info;
596 info = g_file_query_info (base_priv->gfile,
597 G_FILE_ATTRIBUTE_STANDARD_TYPE,
598 G_FILE_QUERY_INFO_NONE,
599 NULL, NULL);
600 if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR) {
601 GFile *parent = g_file_get_parent (base_priv->gfile);
602 priv->gfile = g_file_resolve_relative_path (parent, path);
603 g_object_unref (parent);
605 else {
606 priv->gfile = g_file_resolve_relative_path (base_priv->gfile, path);
609 g_object_unref (info);
611 else {
612 gchar *cur;
613 GFile *curfile;
614 cur = g_get_current_dir ();
615 curfile = g_file_new_for_path (cur);
616 priv->gfile = g_file_resolve_relative_path (curfile, path);
617 g_object_unref (curfile);
618 g_free (cur);
621 resolve_gfile (uri, NULL, hash);
624 static void
625 resolve_data_dirs (YelpUri *ret,
626 const gchar *subdir,
627 const gchar *docid,
628 const gchar *pageid,
629 gboolean langfirst)
631 const gchar * const *sdatadirs = g_get_system_data_dirs ();
632 const gchar * const *langs = g_get_language_names ();
633 /* The strings are still owned by GLib; we just own the array. */
634 gchar **datadirs;
635 YelpUriPrivate *priv = GET_PRIV (ret);
636 gchar *filename = NULL;
637 gchar **searchpath = NULL;
638 gint searchi, searchmax;
639 gint datadir_i, lang_i;
641 datadirs = g_new0 (gchar *, g_strv_length ((gchar **) sdatadirs) + 2);
642 datadirs[0] = (gchar *) g_get_user_data_dir ();
643 for (datadir_i = 0; sdatadirs[datadir_i]; datadir_i++)
644 datadirs[datadir_i + 1] = (gchar *) sdatadirs[datadir_i];
646 searchi = 0;
647 searchmax = 10;
648 searchpath = g_new0 (gchar *, 10);
650 for (datadir_i = 0; datadirs[datadir_i]; datadir_i++) {
651 for (lang_i = 0; langs[lang_i]; lang_i++) {
652 gchar *helpdir = g_build_filename (datadirs[datadir_i],
653 subdir,
654 langfirst ? langs[lang_i] : docid,
655 langfirst ? docid : langs[lang_i],
656 NULL);
657 if (!g_file_test (helpdir, G_FILE_TEST_IS_DIR)) {
658 g_free (helpdir);
659 continue;
662 if (searchi + 1 >= searchmax) {
663 searchmax += 5;
664 searchpath = g_renew (gchar *, searchpath, searchmax);
666 searchpath[searchi] = helpdir;
667 searchpath[++searchi] = NULL;
669 if (priv->tmptype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
670 /* We've already found it. We're just adding to the search path now. */
671 continue;
673 filename = g_strdup_printf ("%s/index.page", helpdir);
674 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
675 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
676 g_free (filename);
677 filename = g_strdup (helpdir);
678 continue;
680 g_free (filename);
682 if (langfirst) {
683 filename = g_strdup_printf ("%s/index.docbook", helpdir);
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);
690 else {
691 filename = g_strdup_printf ("%s/%s.xml", helpdir, pageid);
692 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
693 priv->tmptype = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
694 continue;
696 g_free (filename);
699 filename = g_strdup_printf ("%s/%s.html", helpdir, pageid);
700 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
701 priv->tmptype = YELP_URI_DOCUMENT_TYPE_HTML;
702 continue;
704 g_free (filename);
706 filename = g_strdup_printf ("%s/%s.xhtml", helpdir, pageid);
707 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
708 priv->tmptype = YELP_URI_DOCUMENT_TYPE_XHTML;
709 continue;
711 g_free (filename);
712 } /* end for langs */
713 } /* end for datadirs */
715 g_free (datadirs);
716 if (priv->tmptype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED) {
717 g_strfreev (searchpath);
718 priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
720 else {
721 priv->gfile = g_file_new_for_path (filename);
722 priv->search_path = searchpath;
726 static void
727 build_ghelp_fulluri (YelpUri *uri)
729 YelpUriPrivate *priv = GET_PRIV (uri);
731 g_assert (priv->tmptype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED);
732 g_assert (priv->docuri != NULL);
733 priv->fulluri = g_strconcat (priv->docuri,
734 priv->tmptype == YELP_URI_DOCUMENT_TYPE_MALLARD ? "/" : "",
735 priv->page_id ? "?" : "",
736 priv->page_id ? priv->page_id : "",
737 priv->frag_id ? "#" : "",
738 priv->frag_id ? priv->frag_id : "",
739 NULL);
742 static void
743 resolve_ghelp_uri (YelpUri *uri)
745 /* ghelp:/path/to/file
746 * ghelp:document[/file][?page][#frag]
748 YelpUriPrivate *priv = GET_PRIV (uri);
749 gchar *document, *slash, *query, *hash;
750 gchar *colon, *c; /* do not free */
752 colon = strchr (priv->res_arg, ':');
753 if (!colon) {
754 priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
755 return;
758 slash = query = hash = NULL;
759 for (c = colon; *c != '\0'; c++) {
760 if (*c == '#' && hash == NULL)
761 hash = c;
762 else if (*c == '?' && query == NULL && hash == NULL)
763 query = c;
764 else if (*c == '/' && slash == NULL && query == NULL && hash == NULL)
765 slash = c;
768 if (slash || query || hash)
769 document = g_strndup (colon + 1,
770 (slash ? slash : (query ? query : hash)) - colon - 1);
771 else
772 document = g_strdup (colon + 1);
774 if (slash && (query || hash))
775 slash = g_strndup (slash + 1,
776 (query ? query : hash) - slash - 1);
777 else if (slash)
778 slash = g_strdup (slash + 1);
780 if (query && hash)
781 query = g_strndup (query + 1,
782 hash - query - 1);
783 else if (query)
784 query = g_strdup (query + 1);
786 if (hash)
787 hash = g_strdup (hash + 1);
789 if (*(colon + 1) == '/') {
790 gchar *path;
792 path = g_build_filename ("/", slash, NULL);
793 if (g_file_test (path, G_FILE_TEST_EXISTS)) {
794 priv->gfile = g_file_new_for_path (path);
795 resolve_gfile (uri, query, hash);
796 } else {
797 gchar *dirname = g_path_get_dirname (path);
798 gchar *basename = g_path_get_basename (path);
800 priv->gfile = g_file_new_for_path (dirname);
801 g_free (dirname);
802 resolve_gfile (uri, basename, hash);
803 g_free (basename);
805 g_free (path);
806 g_free (slash);
807 g_free (query);
808 g_free (hash);
809 g_free (document);
811 return;
814 resolve_data_dirs (uri, "gnome/help", document, slash ? slash : document, FALSE);
816 if (query && hash) {
817 priv->page_id = query;
818 priv->frag_id = hash;
820 else if (query) {
821 priv->page_id = query;
822 if (priv->tmptype != YELP_URI_DOCUMENT_TYPE_MALLARD)
823 priv->frag_id = g_strdup (query);
825 else if (hash) {
826 priv->page_id = hash;
827 priv->frag_id = g_strdup (hash);
830 if (priv->frag_id && g_str_has_prefix (priv->frag_id, "search=")) {
831 g_free (priv->frag_id);
832 priv->frag_id = NULL;
835 priv->docuri = g_strconcat ("ghelp:", document,
836 slash ? "/" : NULL,
837 slash, NULL);
839 build_ghelp_fulluri (uri);
841 g_free (document);
842 g_free (slash);
843 return;
846 static void
847 resolve_help_uri (YelpUri *uri)
849 /* help:document[/page][?query][#frag]
851 YelpUriPrivate *priv = GET_PRIV (uri);
852 gchar *document, *slash, *query, *hash;
853 gchar *colon, *c; /* do not free */
855 colon = strchr (priv->res_arg, ':');
856 if (!colon) {
857 priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
858 return;
861 slash = query = hash = NULL;
862 for (c = colon; *c != '\0'; c++) {
863 if (*c == '#' && hash == NULL)
864 hash = c;
865 else if (*c == '?' && query == NULL && hash == NULL)
866 query = c;
867 else if (*c == '/' && slash == NULL && query == NULL && hash == NULL)
868 slash = c;
871 if (slash || query || hash)
872 document = g_strndup (colon + 1,
873 (slash ? slash : (query ? query : hash)) - colon - 1);
874 else
875 document = g_strdup (colon + 1);
877 if (slash && (query || hash))
878 slash = g_strndup (slash + 1,
879 (query ? query : hash) - slash - 1);
880 else if (slash)
881 slash = g_strdup (slash + 1);
883 if (query && hash)
884 query = g_strndup (query + 1,
885 hash - query - 1);
886 else if (query)
887 query = g_strdup (query + 1);
889 if (query) {
890 gchar **keyvals = g_strsplit (query, "&", 0);
891 gint i;
893 for (i = 0; keyvals[i]; i++) {
894 gchar *key, *val;
895 val = strchr (keyvals[i], '=');
896 if (val == NULL)
897 continue;
898 key = g_uri_unescape_segment (keyvals[i], val, NULL);
899 val = g_uri_unescape_string (val + 1, NULL);
901 g_hash_table_insert (priv->query, key, val);
904 g_strfreev (keyvals);
907 if (hash)
908 hash = g_strdup (hash + 1);
910 priv->page_id = (slash ? slash : g_strdup ("index"));
911 resolve_data_dirs (uri, "help", document, priv->page_id, TRUE);
913 if (hash)
914 priv->frag_id = hash;
915 if (priv->frag_id && g_str_has_prefix (priv->frag_id, "search=")) {
916 g_free (priv->frag_id);
917 priv->frag_id = NULL;
920 priv->docuri = g_strconcat ("help:", document, NULL);
922 priv->fulluri = g_strconcat (priv->docuri,
923 priv->page_id ? "/" : "",
924 priv->page_id ? priv->page_id : "",
925 query ? "?" : "",
926 query ? query : "",
927 priv->frag_id ? "#" : "",
928 priv->frag_id ? priv->frag_id : "",
929 NULL);
931 g_free (query);
932 g_free (document);
933 return;
936 static void
937 resolve_help_list_uri (YelpUri *uri)
939 YelpUriPrivate *priv = GET_PRIV (uri);
940 priv->docuri = g_strdup ("help-list:");
941 priv->fulluri = g_strdup (priv->res_arg);
942 priv->page_id = g_strdup ("index");
943 priv->tmptype = YELP_URI_DOCUMENT_TYPE_HELP_LIST;
947 Resolve a manual file's path using 'man -w'. section may be NULL,
948 otherwise should be the section of the manual (ie should have dealt
949 with empty strings before calling this!) Returns NULL if the file
950 can't be found.
952 static gchar*
953 find_man_path (gchar* name, gchar* section)
955 const gchar* argv[] = { "man", "-w", NULL, NULL, NULL };
956 gchar **my_argv;
957 gchar *ystdout = NULL;
958 gint status;
959 gchar **lines;
960 GError *error = NULL;
962 /* Syntax for man is "man -w <section> <name>", possibly omitting
963 section */
964 if (section) {
965 argv[2] = section;
966 argv[3] = name;
967 } else {
968 argv[2] = name;
971 /* g_strdupv() should accept a "const gchar **". */
972 my_argv = g_strdupv ((gchar **) argv);
974 if (!g_spawn_sync (NULL, my_argv, NULL,
975 G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
976 NULL, NULL,
977 &ystdout, NULL, &status, &error)) {
978 g_warning ("Couldn't find path for %s(%s). Error: %s",
979 name, section, error->message);
980 g_error_free (error);
983 g_strfreev (my_argv);
985 if (status == 0) {
986 lines = g_strsplit (ystdout, "\n", 2);
987 g_free (ystdout);
988 ystdout = g_strdup (lines[0]);
990 g_strfreev (lines);
991 return ystdout;
992 } else {
993 g_free (ystdout);
994 return NULL;
998 static void
999 build_man_uris (YelpUri *uri, const char *name, const char *section)
1001 YelpUriPrivate *priv = GET_PRIV (uri);
1003 g_assert (priv->tmptype == YELP_URI_DOCUMENT_TYPE_MAN);
1004 priv->docuri = g_strdup ("man:");
1005 priv->fulluri = g_strconcat ("man:", name,
1006 section ? "." : "",
1007 section ? section : "",
1008 NULL);
1009 priv->page_id = g_strconcat (name,
1010 section ? "." : "",
1011 section ? section : "",
1012 NULL);
1015 static void
1016 resolve_man_uri (YelpUri *uri)
1018 YelpUriPrivate *priv = GET_PRIV (uri);
1019 /* man:/path/to/file
1020 * man:name(section)
1021 * man:name.section
1022 * man:name
1025 /* Search via regular expressions for name, name(section) and
1026 * name.section (assuming that name doesn't contain forward
1027 * slashes or other nasties)
1029 * If these don't match, assume that we were given a filename
1030 * (absolute iff it starts with a /).
1032 static GRegex* man_not_path = NULL;
1033 GError *error = NULL;
1034 GMatchInfo *match_info = NULL;
1035 gchar *name, *section, *hash;
1036 gchar *path;
1038 if (!man_not_path) {
1039 /* Match group 1 should contain the name; then one of groups 3
1040 * and 4 will contain the section if there was one. Group 6
1041 * will contain any hash fragment. */
1042 man_not_path = g_regex_new ("man:((?:[^ /.()#]|\\.(?=[^0-9]))+)"
1043 "(\\(([0-9A-Za-z]+)\\)|\\.([0-9A-Za-z]+)|)"
1044 "(#([^/ ()]+))?",
1045 0, 0, &error);
1046 if (!man_not_path) {
1047 g_error ("Error with regex in man uri: %s\n",
1048 error->message);
1052 if (!g_regex_match (man_not_path, priv->res_arg,
1053 0, &match_info)) {
1054 /* The regexp didn't match, so treat as a file name. */
1055 guint i;
1056 static const char *man_suffixes[] = { "gz", "bz2", "lzma", NULL };
1058 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1059 priv->gfile = g_file_new_for_path (priv->res_arg + 4);
1060 name = g_file_get_basename (priv->gfile);
1061 for (i = 0; i < G_N_ELEMENTS (man_suffixes); i++) {
1062 if (is_man_path (name, man_suffixes[i])) {
1063 if (man_suffixes[i])
1064 name[strlen (name) - strlen (man_suffixes[i]) - 1] = '\0';
1065 break;
1068 build_man_uris (uri, name, NULL);
1070 else {
1071 /* The regexp matched, so we've got a name/section pair that
1072 * needs resolving. */
1073 name = g_match_info_fetch (match_info, 1);
1074 section = g_match_info_fetch (match_info, 3);
1075 hash = g_match_info_fetch (match_info, 6);
1076 if (!name) {
1077 g_error ("Error matching strings in man uri '%s'",
1078 priv->res_arg);
1080 if ((!section) || (section[0] == '\0')) {
1081 section = g_match_info_fetch (match_info, 4);
1083 if (section && section[0] == '\0') section = NULL;
1085 path = find_man_path (name, section);
1087 if (!path) {
1088 priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
1089 return;
1091 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1092 priv->gfile = g_file_new_for_path (path);
1093 build_man_uris (uri, name, section);
1095 if (hash && hash[0] != '\0')
1096 resolve_page_and_frag (uri, hash + 1);
1098 g_free (path);
1099 g_match_info_free (match_info);
1103 static void
1104 build_info_uris (YelpUri *uri, const char *name, const char *section)
1106 YelpUriPrivate *priv = GET_PRIV (uri);
1108 g_assert (priv->tmptype == YELP_URI_DOCUMENT_TYPE_INFO);
1109 priv->docuri = g_strconcat ("info:", name, NULL);
1110 priv->fulluri = g_strconcat (priv->docuri,
1111 section ? "#" : "",
1112 section ? section : "",
1113 NULL);
1114 priv->page_id = g_strdup (section);
1115 priv->frag_id = g_strdup (section);
1118 static void
1119 resolve_info_uri (YelpUri *uri)
1121 YelpUriPrivate *priv = GET_PRIV (uri);
1122 /* info:/path/to/file
1123 * info:name#node
1124 * info:name
1125 * info:(name)node
1126 * info:(name)
1128 static gchar **infopath = NULL;
1129 gchar *name = NULL;
1130 gchar *sect = NULL;
1131 gchar *fullpath = NULL;
1132 /* do not free */
1133 gchar *colon;
1134 gint infopath_i, suffix_i;
1136 if (g_str_has_prefix (priv->res_arg, "info:/")) {
1137 const gchar *hash;
1139 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1141 hash = strchr (priv->res_arg + 5, '#');
1142 if (hash) {
1143 gchar *path;
1145 path = g_strndup (priv->res_arg + 5, hash - (priv->res_arg + 5));
1146 priv->gfile = g_file_new_for_path (path);
1147 g_free (path);
1148 sect = g_strdup (hash + 1);
1150 else
1151 priv->gfile = g_file_new_for_path (priv->res_arg + 5);
1153 name = g_file_get_basename (priv->gfile);
1154 for (suffix_i = 0; infosuffix[suffix_i]; suffix_i++) {
1155 if (g_str_has_suffix (name, infosuffix[suffix_i])) {
1156 name[strlen (name) - strlen (infosuffix[suffix_i])] = '\0';
1157 break;
1161 build_info_uris (uri, name, sect);
1162 g_free (name);
1163 g_free (sect);
1164 return;
1167 if (!infopath) {
1168 /* Initialize infopath only once */
1170 /* Use the same logic as the info program. If INFOPATH is not
1171 specified, use the default. If it is specified, just use it
1172 unless it ends with a colon, in which case we add the
1173 default as a suffix.
1175 const gchar *env = g_getenv ("INFOPATH");
1176 gchar *paths;
1177 if (!env || env[0] == '\0')
1178 paths = g_strdup (default_info_path);
1179 else if (env[strlen (env)-1] == ':')
1180 paths = g_strconcat (env, default_info_path, NULL);
1181 else
1182 paths = g_strdup (env);
1184 infopath = g_strsplit (paths, ":", 0);
1186 g_free (paths);
1189 colon = strchr (priv->res_arg, ':');
1190 if (colon)
1191 colon++;
1192 else
1193 colon = (gchar *) priv->res_arg;
1195 if (colon[0] == '(') {
1196 const gchar *rbrace = strchr (colon, ')');
1197 if (rbrace) {
1198 name = g_strndup (colon + 1, rbrace - colon - 1);
1199 sect = g_strdup (rbrace + 1);
1202 else {
1203 const gchar *hash = strchr (colon, '#');
1204 if (hash) {
1205 name = g_strndup (colon, hash - colon);
1206 sect = g_strdup (hash + 1);
1208 else {
1209 name = g_strdup (colon);
1210 sect = NULL;
1214 for (infopath_i = 0; infopath[infopath_i]; infopath_i++) {
1215 if (!g_file_test (infopath[infopath_i], G_FILE_TEST_IS_DIR))
1216 continue;
1217 for (suffix_i = 0; infosuffix[suffix_i]; suffix_i++) {
1218 fullpath = g_strconcat (infopath[infopath_i], "/",
1219 name, infosuffix[suffix_i], NULL);
1220 if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR))
1221 break;
1222 g_free (fullpath);
1223 fullpath = NULL;
1225 if (fullpath != NULL)
1226 break;
1229 if (fullpath) {
1230 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1231 priv->gfile = g_file_new_for_path (fullpath);
1232 build_info_uris (uri, name, sect);
1233 } else {
1234 gchar *res_arg = priv->res_arg;
1235 priv->res_arg = g_strconcat ("man:", name, NULL);
1236 resolve_man_uri (uri);
1237 if (priv->tmptype == YELP_URI_DOCUMENT_TYPE_MAN) {
1238 g_free (priv->res_arg);
1239 priv->res_arg = res_arg;
1241 else {
1242 g_free (res_arg);
1243 priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
1246 g_free (fullpath);
1247 g_free (name);
1248 g_free (sect);
1251 static void
1252 resolve_xref_uri (YelpUri *uri)
1254 YelpUriPrivate *priv = GET_PRIV (uri);
1255 const gchar *arg = priv->res_arg + 5;
1256 YelpUriPrivate *base_priv = GET_PRIV (priv->res_base);
1258 priv->tmptype = base_priv->doctype;
1259 priv->gfile = g_object_ref (base_priv->gfile);
1260 priv->search_path = g_strdupv (base_priv->search_path);
1261 priv->docuri = g_strdup (base_priv->docuri);
1263 if (arg[0] == '#') {
1264 priv->page_id = g_strdup (base_priv->page_id);
1265 priv->frag_id = g_strdup (arg + 1);
1267 else {
1268 gchar *hash = strchr (arg, '#');
1269 if (hash) {
1270 priv->page_id = g_strndup (arg, hash - arg);
1271 priv->frag_id = g_strdup (hash + 1);
1273 else {
1274 priv->page_id = g_strdup (arg);
1275 priv->frag_id = NULL;
1278 if (priv->page_id && priv->page_id[0] == '\0') {
1279 g_free (priv->page_id);
1280 if (g_str_has_prefix (priv->docuri, "help:"))
1281 priv->page_id = g_strdup ("index");
1282 else
1283 priv->page_id = NULL;
1286 if (priv->page_id &&
1287 g_str_has_prefix (priv->docuri, "info:")) {
1289 Special characters get url-encoded when they get clicked on
1290 as links. Info files, at least, don't want that so decode
1291 the url again here.
1293 gchar* tmp = priv->page_id;
1294 priv->page_id = g_uri_unescape_string (tmp, NULL);
1295 g_free (tmp);
1298 if (g_str_has_prefix (priv->docuri, "ghelp:"))
1299 build_ghelp_fulluri (uri);
1300 else if (g_str_has_prefix (priv->docuri, "help:"))
1301 priv->fulluri = g_strconcat (priv->docuri,
1302 priv->page_id ? "/" : "",
1303 priv->page_id ? priv->page_id : "",
1304 priv->frag_id ? "#" : "",
1305 priv->frag_id ? priv->frag_id : "",
1306 NULL);
1307 else if (g_str_has_prefix (priv->docuri, "file:") ||
1308 g_str_has_prefix (priv->docuri, "info:") )
1309 priv->fulluri = g_strconcat (priv->docuri,
1310 (priv->page_id || priv->frag_id) ? "#" : "",
1311 priv->page_id ? priv->page_id : "",
1312 priv->frag_id ? "#" : "",
1313 priv->frag_id,
1314 NULL);
1315 else
1316 /* FIXME: other URI schemes */
1317 priv->fulluri = g_strconcat (priv->docuri,
1318 priv->page_id ? "#" : "",
1319 priv->page_id,
1320 NULL);
1323 static void
1324 resolve_page_and_frag (YelpUri *uri, const gchar *arg)
1326 YelpUriPrivate *priv = GET_PRIV (uri);
1327 gchar *hash;
1329 if (!arg || arg[0] == '\0')
1330 return;
1332 hash = strchr (arg, '#');
1333 if (hash) {
1334 priv->page_id = g_strndup (arg, hash - arg);
1335 priv->frag_id = g_strdup (hash + 1);
1336 } else {
1337 priv->page_id = g_strdup (arg);
1338 priv->frag_id = g_strdup (arg);
1340 return;
1343 static void
1344 resolve_gfile (YelpUri *uri, const gchar *query, const gchar *hash)
1346 YelpUriPrivate *priv = GET_PRIV (uri);
1347 GFileInfo *info;
1348 GError *error = NULL;
1350 info = g_file_query_info (priv->gfile,
1351 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
1352 G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE,
1353 G_FILE_QUERY_INFO_NONE,
1354 NULL, &error);
1355 if (error) {
1356 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
1357 priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
1359 else
1360 priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
1361 g_error_free (error);
1362 return;
1365 if (priv->search_path == NULL) {
1366 if (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) ==
1367 G_FILE_TYPE_DIRECTORY) {
1368 priv->search_path = g_new0 (gchar *, 2);
1369 priv->search_path[0] = g_file_get_path (priv->gfile);
1370 } else {
1371 GFile *parent = g_file_get_parent (priv->gfile);
1372 priv->search_path = g_new0 (gchar *, 2);
1373 priv->search_path[0] = g_file_get_path (parent);
1374 g_object_unref (parent);
1378 if (priv->tmptype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED) {
1379 priv->tmptype = YELP_URI_DOCUMENT_TYPE_EXTERNAL;
1381 if (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) ==
1382 G_FILE_TYPE_DIRECTORY) {
1383 GFile *child = g_file_get_child (priv->gfile, "index.page");
1384 if (g_file_query_exists (child, NULL)) {
1385 char *path;
1387 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
1388 if (priv->page_id == NULL)
1389 priv->page_id = g_strdup (query);
1390 if (priv->frag_id == NULL)
1391 priv->frag_id = g_strdup (hash);
1393 path = g_file_get_path (priv->gfile);
1394 priv->docuri = g_strconcat ("ghelp:", path, NULL);
1395 build_ghelp_fulluri (uri);
1396 g_free (path);
1398 else if (yelp_settings_get_editor_mode (yelp_settings_get_default ())) {
1399 g_object_unref (child);
1400 child = g_file_get_child (priv->gfile, "index.page.stub");
1401 if (g_file_query_exists (child, NULL)) {
1402 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
1403 if (priv->page_id == NULL)
1404 priv->page_id = g_strdup (query);
1405 if (priv->frag_id == NULL)
1406 priv->frag_id = g_strdup (hash);
1409 g_object_unref (child);
1411 else {
1412 gchar *basename;
1413 const gchar *mime_type = g_file_info_get_attribute_string (info,
1414 G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
1415 basename = g_file_get_basename (priv->gfile);
1416 if (g_str_has_suffix (basename, ".page")) {
1417 GFile *old;
1418 char *path;
1420 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
1421 old = priv->gfile;
1422 priv->gfile = g_file_get_parent (old);
1423 if (priv->page_id == NULL) {
1424 /* File names aren't really page IDs, so we stick an illegal character
1425 on the beginning so it can't possibly be confused with a real page
1426 ID. Then we let YelpMallardDocument map file names to pages IDs.
1428 gchar *tmp = g_file_get_basename (old);
1429 priv->page_id = g_strconcat (G_DIR_SEPARATOR_S, tmp, NULL);
1430 g_free (tmp);
1433 if (priv->frag_id == NULL)
1434 priv->frag_id = g_strdup (hash);
1435 path = g_file_get_path (priv->gfile);
1436 priv->docuri = g_strconcat ("ghelp:", path, NULL);
1437 build_ghelp_fulluri (uri);
1438 g_free (path);
1439 g_object_unref (old);
1441 else if (g_str_equal (mime_type, "text/xml") ||
1442 g_str_equal (mime_type, "application/docbook+xml") ||
1443 g_str_equal (mime_type, "application/xml") ||
1444 g_str_has_suffix (basename, ".docbook")) {
1445 char *path;
1447 priv->tmptype = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
1449 if (priv->page_id == NULL)
1450 priv->page_id = g_strdup (query);
1451 if (priv->frag_id == NULL)
1452 priv->frag_id = g_strdup (hash);
1454 path = g_file_get_path (priv->gfile);
1455 priv->docuri = g_strconcat ("ghelp:", path, NULL);
1456 build_ghelp_fulluri (uri);
1457 g_free (path);
1459 else if (g_str_equal (mime_type, "text/html") ||
1460 g_str_equal (mime_type, "application/xhtml+xml")) {
1461 GFile *parent = g_file_get_parent (priv->gfile);
1462 priv->docuri = g_file_get_uri (parent);
1463 g_object_unref (parent);
1464 priv->tmptype = mime_type[0] == 't' ? YELP_URI_DOCUMENT_TYPE_HTML : YELP_URI_DOCUMENT_TYPE_XHTML;
1465 if (priv->page_id == NULL)
1466 priv->page_id = g_strdup (basename);
1467 if (priv->frag_id == NULL)
1468 priv->frag_id = g_strdup (hash);
1469 if (priv->fulluri == NULL) {
1470 gchar *fulluri;
1471 fulluri = g_file_get_uri (priv->gfile);
1472 priv->fulluri = g_strconcat (fulluri,
1473 priv->frag_id ? "#" : NULL,
1474 priv->frag_id,
1475 NULL);
1476 g_free (fulluri);
1479 else if (g_str_equal (mime_type, "application/x-gzip") ||
1480 g_str_equal (mime_type, "application/gzip")) {
1481 if (g_str_has_suffix (basename, ".info.gz")) {
1482 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1483 basename[strlen (basename) - strlen (".info.gz")] = '\0';
1484 build_info_uris (uri, basename, hash);
1486 else if (is_man_path (basename, "gz")) {
1487 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1488 basename[strlen (basename) - strlen ("gz") - 1] = '\0';
1489 build_man_uris (uri, basename, NULL);
1492 else if (g_str_equal (mime_type, "application/x-bzip")) {
1493 if (g_str_has_suffix (basename, ".info.bz2")) {
1494 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1495 basename[strlen (basename) - strlen (".info.bz2")] = '\0';
1496 build_info_uris (uri, basename, hash);
1498 else if (is_man_path (basename, "bz2")) {
1499 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1500 basename[strlen (basename) - strlen ("bz2") - 1] = '\0';
1501 build_man_uris (uri, basename, NULL);
1504 else if (g_str_equal (mime_type, "application/x-lzma")) {
1505 if (g_str_has_suffix (basename, ".info.lzma")) {
1506 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1507 basename[strlen (basename) - strlen (".info.lzma")] = '\0';
1508 build_info_uris (uri, basename, hash);
1510 else if (is_man_path (basename, "lzma")) {
1511 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1512 basename[strlen (basename) - strlen ("lzma") - 1] = '\0';
1513 build_man_uris (uri, basename, NULL);
1516 else if (g_str_equal (mime_type, "application/octet-stream")) {
1517 if (g_str_has_suffix (basename, ".info")) {
1518 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1519 basename[strlen (basename) - strlen (".info")] = '\0';
1520 build_info_uris (uri, basename, hash);
1522 else if (is_man_path (basename, NULL)) {
1523 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1524 build_man_uris (uri, basename, NULL);
1527 else if (g_str_equal (mime_type, "text/plain")) {
1528 if (g_str_has_suffix (basename, ".info")) {
1529 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1530 basename[strlen (basename) - strlen (".info")] = '\0';
1531 build_info_uris (uri, basename, hash);
1533 else if (is_man_path (basename, NULL)) {
1534 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1535 build_man_uris (uri, basename, NULL);
1536 } else
1537 priv->tmptype = YELP_URI_DOCUMENT_TYPE_TEXT;
1538 if (priv->frag_id == NULL)
1539 priv->frag_id = g_strdup (hash);
1541 else if (g_str_equal (mime_type, "text/x-readme")) {
1542 priv->tmptype = YELP_URI_DOCUMENT_TYPE_TEXT;
1544 else {
1545 priv->tmptype = YELP_URI_DOCUMENT_TYPE_EXTERNAL;
1547 g_free (basename);
1551 if (priv->docuri == NULL)
1552 priv->docuri = g_file_get_uri (priv->gfile);
1554 if (priv->fulluri == NULL)
1555 priv->fulluri = g_strconcat (priv->docuri,
1556 (priv->page_id || priv->frag_id) ? "#" : NULL,
1557 priv->page_id ? priv->page_id : "",
1558 priv->frag_id ? "#" : NULL,
1559 priv->frag_id ? priv->frag_id : NULL,
1560 NULL);
1562 g_object_unref (info);
1565 static gboolean
1566 is_man_path (const gchar *path, const gchar *encoding)
1568 gchar **iter = (gchar **) mancats;
1570 if (encoding && *encoding) {
1571 while (iter && *iter) {
1572 gchar *ending = g_strdup_printf ("%s.%s", *iter, encoding);
1573 if (g_str_has_suffix (path, ending)) {
1574 g_free (ending);
1575 return TRUE;
1577 g_free (ending);
1578 iter++;
1580 } else {
1581 while (iter && *iter) {
1582 if (g_str_has_suffix (path, *iter)) {
1583 return TRUE;
1585 iter++;
1588 return FALSE;