1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2009-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 <string.h>
26 #include <stdio.h>
27
28 #include <glib.h>
29 #include <gio/gio.h>
30
31 #include "yelp-uri.h"
32 #include "yelp-settings.h"
33
34 static void yelp_uri_dispose (GObject *object);
35 static void yelp_uri_finalize (GObject *object);
36
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);
41
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);
60
61 static gboolean is_man_path (const gchar *uri,
62 const gchar *encoding);
63
64 typedef struct _YelpUriPrivate YelpUriPrivate;
65 struct _YelpUriPrivate {
66 GThread *resolver;
67
68 YelpUriDocumentType doctype;
69 YelpUriDocumentType tmptype;
70
71 gchar *docuri;
72 gchar *fulluri;
73 GFile *gfile;
74
75 gchar **search_path;
76 gchar *page_id;
77 gchar *frag_id;
78
79 GHashTable *query;
80
81 /* Unresolved */
82 YelpUri *res_base;
83 gchar *res_arg;
84 };
85
86 enum {
87 RESOLVED,
88 LAST_SIGNAL
89 };
90 static guint uri_signals[LAST_SIGNAL] = {0,};
91
92 G_DEFINE_TYPE_WITH_PRIVATE (YelpUri, yelp_uri, G_TYPE_OBJECT)
93
94 /******************************************************************************/
95
96 static const gchar *mancats[] = {
97 "0p",
98 "1", "1p", "1g", "1t", "1x", "1ssl", "1m",
99 "2",
100 "3", "3o", "3t", "3p", "3blt", "3nas", "3form", "3menu", "3tiff", "3ssl", "3readline",
101 "3ncurses", "3curses", "3f", "3pm", "3perl", "3qt", "3x", "3X11",
102 "4", "4x",
103 "5", "5snmp", "5x", "5ssl",
104 "6", "6x",
105 "7", "7gcc", "7x", "7ssl",
106 "8", "8l", "9", "0p",
107 NULL
108 };
109
110 static const gchar *infosuffix[] = {
111 ".info",
112 ".info.gz", ".info.bz2", ".info.lzma",
113 ".gz", ".bz2", ".lzma",
114 NULL
115 };
116
117 static const gchar default_info_path[] =
118 "/usr/info:/usr/share/info:/usr/local/info:/usr/local/share/info";
119
120 /******************************************************************************/
121
122 static void
yelp_uri_class_init(YelpUriClass * klass)123 yelp_uri_class_init (YelpUriClass *klass)
124 {
125 GObjectClass *object_class = G_OBJECT_CLASS (klass);
126
127 object_class->dispose = yelp_uri_dispose;
128 object_class->finalize = yelp_uri_finalize;
129
130 uri_signals[RESOLVED] =
131 g_signal_new ("resolved",
132 G_OBJECT_CLASS_TYPE (klass),
133 G_SIGNAL_RUN_LAST,
134 0, NULL, NULL,
135 g_cclosure_marshal_VOID__VOID,
136 G_TYPE_NONE, 0);
137 }
138
139 static void
yelp_uri_init(YelpUri * uri)140 yelp_uri_init (YelpUri *uri)
141 {
142 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
143
144 priv->query = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
145
146 return;
147 }
148
149 static void
yelp_uri_dispose(GObject * object)150 yelp_uri_dispose (GObject *object)
151 {
152 YelpUriPrivate *priv = yelp_uri_get_instance_private (YELP_URI (object));
153
154 if (priv->gfile) {
155 g_object_unref (priv->gfile);
156 priv->gfile = NULL;
157 }
158
159 if (priv->res_base) {
160 g_object_unref (priv->res_base);
161 priv->res_base = NULL;
162 }
163
164 if (priv->query) {
165 g_hash_table_destroy (priv->query);
166 priv->query = NULL;
167 }
168
169 G_OBJECT_CLASS (yelp_uri_parent_class)->dispose (object);
170 }
171
172 static void
yelp_uri_finalize(GObject * object)173 yelp_uri_finalize (GObject *object)
174 {
175 YelpUriPrivate *priv = yelp_uri_get_instance_private (YELP_URI (object));
176
177 g_free (priv->docuri);
178 g_free (priv->fulluri);
179 g_strfreev (priv->search_path);
180 g_free (priv->page_id);
181 g_free (priv->frag_id);
182 g_free (priv->res_arg);
183
184 G_OBJECT_CLASS (yelp_uri_parent_class)->finalize (object);
185 }
186
187 /******************************************************************************/
188
189 YelpUri *
yelp_uri_new(const gchar * arg)190 yelp_uri_new (const gchar *arg)
191 {
192 return yelp_uri_new_relative (NULL, arg);
193 }
194
195 YelpUri *
yelp_uri_new_relative(YelpUri * base,const gchar * arg)196 yelp_uri_new_relative (YelpUri *base, const gchar *arg)
197 {
198 YelpUri *uri;
199 YelpUriPrivate *priv;
200
201 uri = (YelpUri *) g_object_new (YELP_TYPE_URI, NULL);
202
203 priv = yelp_uri_get_instance_private (uri);
204 priv->doctype = YELP_URI_DOCUMENT_TYPE_UNRESOLVED;
205 if (base)
206 priv->res_base = g_object_ref (base);
207 priv->res_arg = g_strdup (arg);
208
209 return uri;
210 }
211
212 YelpUri *
yelp_uri_new_search(YelpUri * base,const gchar * text)213 yelp_uri_new_search (YelpUri *base,
214 const gchar *text)
215 {
216 YelpUri *uri;
217 YelpUriPrivate *priv;
218 gchar *tmp;
219
220 uri = (YelpUri *) g_object_new (YELP_TYPE_URI, NULL);
221
222 priv = yelp_uri_get_instance_private (uri);
223 priv->doctype = YELP_URI_DOCUMENT_TYPE_UNRESOLVED;
224 if (base)
225 priv->res_base = g_object_ref (base);
226 tmp = g_uri_escape_string (text, NULL, FALSE);
227 priv->res_arg = g_strconcat("xref:search=", tmp, NULL);
228 g_free (tmp);
229
230 return uri;
231 }
232
233 /******************************************************************************/
234
235 void
yelp_uri_resolve(YelpUri * uri)236 yelp_uri_resolve (YelpUri *uri)
237 {
238 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
239
240 if (priv->res_base && !yelp_uri_is_resolved (priv->res_base)) {
241 g_signal_connect_swapped (priv->res_base, "resolved",
242 G_CALLBACK (resolve_start),
243 uri);
244 yelp_uri_resolve (priv->res_base);
245 }
246 else {
247 resolve_start (uri);
248 }
249 }
250
251 void
yelp_uri_resolve_sync(YelpUri * uri)252 yelp_uri_resolve_sync (YelpUri *uri)
253 {
254 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
255
256 if (priv->doctype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
257 return;
258
259 if (priv->res_base)
260 yelp_uri_resolve_sync (priv->res_base);
261
262 g_object_ref (uri);
263 resolve_sync (uri);
264 resolve_final (uri);
265 }
266
267 /* We want code to be able to do something like this:
268 *
269 * if (yelp_uri_get_document_type (uri) != YELP_URI_DOCUMENT_TYPE_UNRESOLVED) {
270 * g_signal_connect (uri, "resolve", callback, data);
271 * yelp_uri_resolve (uri);
272 * }
273 *
274 * Resolving happens in a separate thread, though, so if that thread can change
275 * the document type, we have a race condition. So here's the rules we play by:
276 *
277 * 1) None of the getters except the document type getter can return real data
278 * while the URI is unresolved. They all do a resolved check first, and
279 * return NULL if the URI is not resolved.
280 *
281 * 2) The threaded resolver functions can modify anything but the document
282 * type. They are the only things that are allowed to modify that data.
283 *
284 * 3) The resolver thread is not allowed to modify the document type. When
285 * it's done, it queues an async function to set the document type and
286 * emit "resolved" in the main thread.
287 *
288 * 4) Once a URI is resolved, it is immutable.
289 */
290 static void
resolve_start(YelpUri * uri)291 resolve_start (YelpUri *uri)
292 {
293 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
294
295 if (priv->resolver == NULL) {
296 g_object_ref (uri);
297 priv->resolver = g_thread_new ("uri-resolve",
298 (GThreadFunc)(GCallback) resolve_async,
299 uri);
300 }
301 }
302
303 static void
resolve_sync(YelpUri * uri)304 resolve_sync (YelpUri *uri)
305 {
306 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
307
308 if (g_str_has_prefix (priv->res_arg, "ghelp:")
309 || g_str_has_prefix (priv->res_arg, "gnome-help:")) {
310 resolve_ghelp_uri (uri);
311 }
312 else if (g_str_has_prefix (priv->res_arg, "help:")) {
313 resolve_help_uri (uri);
314 }
315 else if (g_str_has_prefix (priv->res_arg, "help-list:")) {
316 resolve_help_list_uri (uri);
317 }
318 else if (g_str_has_prefix (priv->res_arg, "file:")) {
319 resolve_file_uri (uri);
320 }
321 else if (g_str_has_prefix (priv->res_arg, "man:")) {
322 resolve_man_uri (uri);
323 }
324 else if (g_str_has_prefix (priv->res_arg, "info:")) {
325 resolve_info_uri (uri);
326 }
327 else if (g_str_has_prefix (priv->res_arg, "xref:")) {
328 YelpUriPrivate *base_priv;
329 if (priv->res_base == NULL) {
330 priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
331 return;
332 }
333 base_priv = yelp_uri_get_instance_private (priv->res_base);
334 switch (base_priv->doctype) {
335 case YELP_URI_DOCUMENT_TYPE_UNRESOLVED:
336 break;
337 case YELP_URI_DOCUMENT_TYPE_DOCBOOK:
338 case YELP_URI_DOCUMENT_TYPE_MALLARD:
339 case YELP_URI_DOCUMENT_TYPE_INFO:
340 resolve_xref_uri (uri);
341 break;
342 case YELP_URI_DOCUMENT_TYPE_MAN: {
343 gchar *tmp = g_strconcat ("man:", priv->res_arg + 5, NULL);
344 g_free (priv->res_arg);
345 priv->res_arg = tmp;
346 resolve_man_uri (uri);
347 break;
348 }
349 case YELP_URI_DOCUMENT_TYPE_TEXT:
350 case YELP_URI_DOCUMENT_TYPE_HTML:
351 case YELP_URI_DOCUMENT_TYPE_XHTML:
352 resolve_file_path (uri);
353 break;
354 case YELP_URI_DOCUMENT_TYPE_HELP_LIST:
355 /* FIXME: what do we do? */
356 break;
357 case YELP_URI_DOCUMENT_TYPE_NOT_FOUND:
358 case YELP_URI_DOCUMENT_TYPE_EXTERNAL:
359 case YELP_URI_DOCUMENT_TYPE_ERROR:
360 break;
361 default:
362 g_assert_not_reached ();
363 break;
364 }
365 }
366 else if (strchr (priv->res_arg, ':')) {
367 priv->tmptype = YELP_URI_DOCUMENT_TYPE_EXTERNAL;
368 }
369 else {
370 resolve_file_path (uri);
371 }
372
373 /* We _always_ want to have a non-null fulluri, so check for it
374 * having been set here and, if we can't think of something
375 * better, set it to res_arg. */
376 if (!priv->fulluri) {
377 priv->fulluri = g_strdup (priv->res_arg);
378 }
379 }
380
381 static void
resolve_async(YelpUri * uri)382 resolve_async (YelpUri *uri)
383 {
384 resolve_sync (uri);
385 g_idle_add ((GSourceFunc) resolve_final, uri);
386 }
387
388 static gboolean
resolve_final(YelpUri * uri)389 resolve_final (YelpUri *uri)
390 {
391 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
392
393 priv->resolver = NULL;
394
395 if (priv->tmptype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
396 priv->doctype = priv->tmptype;
397 else
398 priv->doctype = YELP_URI_DOCUMENT_TYPE_ERROR;
399
400 if (priv->res_base) {
401 g_object_unref (priv->res_base);
402 priv->res_base = NULL;
403 }
404
405 if (priv->res_arg) {
406 g_free (priv->res_arg);
407 priv->res_arg = NULL;
408 }
409
410 g_signal_emit (uri, uri_signals[RESOLVED], 0);
411 g_object_unref (uri);
412 return FALSE;
413 }
414
415 /******************************************************************************/
416
417 gboolean
yelp_uri_is_resolved(YelpUri * uri)418 yelp_uri_is_resolved (YelpUri *uri)
419 {
420 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
421 return priv->doctype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED;
422 }
423
424 YelpUriDocumentType
yelp_uri_get_document_type(YelpUri * uri)425 yelp_uri_get_document_type (YelpUri *uri)
426 {
427 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
428 return priv->doctype;
429 }
430
431 gchar *
yelp_uri_get_document_uri(YelpUri * uri)432 yelp_uri_get_document_uri (YelpUri *uri)
433 {
434 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
435 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
436 return NULL;
437
438 /* There's some client code where it makes sense to want a
439 * document uri, whether or not it conforms to a scheme we really
440 * understand. For example, we might want to look up whether the
441 * given page is currently being visited. */
442 if ((!priv->docuri) && priv->fulluri) {
443 return g_strdup (priv->fulluri);
444 }
445
446 return g_strdup (priv->docuri);
447 }
448
449 gchar *
yelp_uri_get_canonical_uri(YelpUri * uri)450 yelp_uri_get_canonical_uri (YelpUri *uri)
451 {
452 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
453 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
454 return NULL;
455 return g_strdup (priv->fulluri);
456 }
457
458 GFile *
yelp_uri_get_file(YelpUri * uri)459 yelp_uri_get_file (YelpUri *uri)
460 {
461 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
462 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
463 return NULL;
464 return priv->gfile ? g_object_ref (priv->gfile) : NULL;
465 }
466
467 gchar **
yelp_uri_get_search_path(YelpUri * uri)468 yelp_uri_get_search_path (YelpUri *uri)
469 {
470 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
471 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
472 return NULL;
473 return g_strdupv (priv->search_path);
474 }
475
476 gchar *
yelp_uri_get_page_id(YelpUri * uri)477 yelp_uri_get_page_id (YelpUri *uri)
478 {
479 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
480 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
481 return NULL;
482 return g_strdup (priv->page_id);
483 }
484
485 gchar *
yelp_uri_get_frag_id(YelpUri * uri)486 yelp_uri_get_frag_id (YelpUri *uri)
487 {
488 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
489 if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
490 return NULL;
491 return g_strdup (priv->frag_id);
492 }
493
494 gchar *
yelp_uri_get_query(YelpUri * uri,const gchar * key)495 yelp_uri_get_query (YelpUri *uri,
496 const gchar *key)
497 {
498 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
499 const gchar *ret = g_hash_table_lookup (priv->query, key);
500 if (ret)
501 return g_strdup (ret);
502 else
503 return NULL;
504 }
505
506 /******************************************************************************/
507
508 gchar *
yelp_uri_locate_file_uri(YelpUri * uri,const gchar * filename)509 yelp_uri_locate_file_uri (YelpUri *uri,
510 const gchar *filename)
511 {
512 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
513 GFile *gfile;
514 gchar *fullpath;
515 gchar *returi = NULL;
516 gint i;
517
518 if (g_path_is_absolute (filename)) {
519 if (g_file_test (filename, G_FILE_TEST_EXISTS))
520 return g_filename_to_uri (filename, NULL, NULL);
521 return NULL;
522 }
523
524 for (i = 0; priv->search_path[i] != NULL; i++) {
525 fullpath = g_strconcat (priv->search_path[i],
526 G_DIR_SEPARATOR_S,
527 filename,
528 NULL);
529 if (g_file_test (fullpath, G_FILE_TEST_EXISTS)) {
530 gfile = g_file_new_for_path (fullpath);
531 returi = g_file_get_uri (gfile);
532 g_object_unref (gfile);
533 }
534 g_free (fullpath);
535 if (returi)
536 break;
537 }
538 return returi;
539 }
540
541 /******************************************************************************/
542
543 static void
resolve_file_uri(YelpUri * uri)544 resolve_file_uri (YelpUri *uri)
545 {
546 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
547 gchar *uristr;
548 const gchar *hash = strchr (priv->res_arg, '#');
549
550 if (hash) {
551 uristr = g_strndup (priv->res_arg, hash - priv->res_arg);
552 hash++;
553 }
554 else
555 uristr = priv->res_arg;
556
557 priv->gfile = g_file_new_for_uri (uristr);
558
559 resolve_gfile (uri, NULL, hash);
560 }
561
562 static void
resolve_file_path(YelpUri * uri)563 resolve_file_path (YelpUri *uri)
564 {
565 YelpUriPrivate *base_priv = NULL;
566 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
567 gchar *path;
568 const gchar *hash;
569
570 /* Treat xref: URIs like relative file paths */
571 if (g_str_has_prefix (priv->res_arg, "xref:")) {
572 gchar *tmp = g_strdup (priv->res_arg + 5);
573 g_free (priv->res_arg);
574 priv->res_arg = tmp;
575 }
576
577 if (priv->res_base)
578 base_priv = yelp_uri_get_instance_private (priv->res_base);
579
580 hash = strchr (priv->res_arg, '#');
581 if (hash) {
582 path = g_strndup (priv->res_arg, hash - priv->res_arg);
583 hash++;
584 }
585 else
586 path = priv->res_arg;
587
588 if (priv->res_arg[0] == '/') {
589 priv->gfile = g_file_new_for_path (path);
590 }
591 else if (base_priv && base_priv->gfile) {
592 GFileInfo *info;
593 info = g_file_query_info (base_priv->gfile,
594 G_FILE_ATTRIBUTE_STANDARD_TYPE,
595 G_FILE_QUERY_INFO_NONE,
596 NULL, NULL);
597 if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR) {
598 GFile *parent = g_file_get_parent (base_priv->gfile);
599 priv->gfile = g_file_resolve_relative_path (parent, path);
600 g_object_unref (parent);
601 }
602 else {
603 priv->gfile = g_file_resolve_relative_path (base_priv->gfile, path);
604 }
605
606 g_object_unref (info);
607 }
608 else {
609 gchar *cur;
610 GFile *curfile;
611 cur = g_get_current_dir ();
612 curfile = g_file_new_for_path (cur);
613 priv->gfile = g_file_resolve_relative_path (curfile, path);
614 g_object_unref (curfile);
615 g_free (cur);
616 }
617
618 resolve_gfile (uri, NULL, hash);
619 }
620
621 static void
resolve_data_dirs(YelpUri * ret,const gchar * subdir,const gchar * docid,const gchar * pageid,gboolean langfirst)622 resolve_data_dirs (YelpUri *ret,
623 const gchar *subdir,
624 const gchar *docid,
625 const gchar *pageid,
626 gboolean langfirst)
627 {
628 const gchar * const *sdatadirs = g_get_system_data_dirs ();
629 const gchar * const *langs = g_get_language_names ();
630 /* The strings are still owned by GLib; we just own the array. */
631 gchar **datadirs;
632 YelpUriPrivate *priv = yelp_uri_get_instance_private (ret);
633 gchar *filename = NULL;
634 gchar **searchpath = NULL;
635 gint searchi, searchmax;
636 gint datadir_i, lang_i;
637
638 datadirs = g_new0 (gchar *, g_strv_length ((gchar **) sdatadirs) + 2);
639 datadirs[0] = (gchar *) g_get_user_data_dir ();
640 for (datadir_i = 0; sdatadirs[datadir_i]; datadir_i++)
641 datadirs[datadir_i + 1] = (gchar *) sdatadirs[datadir_i];
642
643 searchi = 0;
644 searchmax = 10;
645 searchpath = g_new0 (gchar *, 10);
646
647 for (datadir_i = 0; datadirs[datadir_i]; datadir_i++) {
648 for (lang_i = 0; langs[lang_i]; lang_i++) {
649 gchar *helpdir = g_build_filename (datadirs[datadir_i],
650 subdir,
651 langfirst ? langs[lang_i] : docid,
652 langfirst ? docid : langs[lang_i],
653 NULL);
654 if (!g_file_test (helpdir, G_FILE_TEST_IS_DIR)) {
655 g_free (helpdir);
656 continue;
657 }
658
659 if (searchi + 1 >= searchmax) {
660 searchmax += 5;
661 searchpath = g_renew (gchar *, searchpath, searchmax);
662 }
663 searchpath[searchi] = helpdir;
664 searchpath[++searchi] = NULL;
665
666 if (priv->tmptype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
667 /* We've already found it. We're just adding to the search path now. */
668 continue;
669
670 filename = g_strdup_printf ("%s/index.page", helpdir);
671 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
672 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
673 g_free (filename);
674 filename = g_strdup (helpdir);
675 continue;
676 }
677 g_free (filename);
678
679 if (langfirst) {
680 filename = g_strdup_printf ("%s/index.docbook", helpdir);
681 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
682 priv->tmptype = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
683 continue;
684 }
685 g_free (filename);
686 }
687 else {
688 filename = g_strdup_printf ("%s/%s.xml", helpdir, pageid);
689 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
690 priv->tmptype = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
691 continue;
692 }
693 g_free (filename);
694 }
695
696 filename = g_strdup_printf ("%s/%s.html", helpdir, pageid);
697 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
698 priv->tmptype = YELP_URI_DOCUMENT_TYPE_HTML;
699 continue;
700 }
701 g_free (filename);
702
703 filename = g_strdup_printf ("%s/%s.xhtml", helpdir, pageid);
704 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
705 priv->tmptype = YELP_URI_DOCUMENT_TYPE_XHTML;
706 continue;
707 }
708 g_free (filename);
709 } /* end for langs */
710 } /* end for datadirs */
711
712 g_free (datadirs);
713 if (priv->tmptype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED) {
714 g_strfreev (searchpath);
715 priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
716 }
717 else {
718 priv->gfile = g_file_new_for_path (filename);
719 priv->search_path = searchpath;
720 g_free (filename);
721 }
722 }
723
724 static void
build_ghelp_fulluri(YelpUri * uri)725 build_ghelp_fulluri (YelpUri *uri)
726 {
727 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
728
729 g_assert (priv->tmptype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED);
730 g_assert (priv->docuri != NULL);
731 priv->fulluri = g_strconcat (priv->docuri,
732 priv->tmptype == YELP_URI_DOCUMENT_TYPE_MALLARD ? "/" : "",
733 priv->page_id ? "?" : "",
734 priv->page_id ? priv->page_id : "",
735 priv->frag_id ? "#" : "",
736 priv->frag_id ? priv->frag_id : "",
737 NULL);
738 }
739
740 static void
resolve_ghelp_uri(YelpUri * uri)741 resolve_ghelp_uri (YelpUri *uri)
742 {
743 /* ghelp:/path/to/file
744 * ghelp:document[/file][?page][#frag]
745 */
746 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
747 gchar *document, *slash, *query, *hash;
748 gchar *colon, *c; /* do not free */
749
750 colon = strchr (priv->res_arg, ':');
751 if (!colon) {
752 priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
753 return;
754 }
755
756 slash = query = hash = NULL;
757 for (c = colon; *c != '\0'; c++) {
758 if (*c == '#' && hash == NULL)
759 hash = c;
760 else if (*c == '?' && query == NULL && hash == NULL)
761 query = c;
762 else if (*c == '/' && slash == NULL && query == NULL && hash == NULL)
763 slash = c;
764 }
765
766 if (slash || query || hash)
767 document = g_strndup (colon + 1,
768 (slash ? slash : (query ? query : hash)) - colon - 1);
769 else
770 document = g_strdup (colon + 1);
771
772 if (slash && (query || hash))
773 slash = g_strndup (slash + 1,
774 (query ? query : hash) - slash - 1);
775 else if (slash)
776 slash = g_strdup (slash + 1);
777
778 if (query && hash)
779 query = g_strndup (query + 1,
780 hash - query - 1);
781 else if (query)
782 query = g_strdup (query + 1);
783
784 if (hash)
785 hash = g_strdup (hash + 1);
786
787 if (*(colon + 1) == '/') {
788 gchar *path;
789
790 path = g_build_filename ("/", slash, NULL);
791 if (g_file_test (path, G_FILE_TEST_EXISTS)) {
792 priv->gfile = g_file_new_for_path (path);
793 resolve_gfile (uri, query, hash);
794 } else {
795 gchar *dirname = g_path_get_dirname (path);
796 gchar *basename = g_path_get_basename (path);
797
798 priv->gfile = g_file_new_for_path (dirname);
799 g_free (dirname);
800 resolve_gfile (uri, basename, hash);
801 g_free (basename);
802 }
803 g_free (path);
804 g_free (slash);
805 g_free (query);
806 g_free (hash);
807 g_free (document);
808
809 return;
810 }
811
812 resolve_data_dirs (uri, "gnome/help", document, slash ? slash : document, FALSE);
813
814 if (query && hash) {
815 priv->page_id = query;
816 priv->frag_id = hash;
817 }
818 else if (query) {
819 priv->page_id = query;
820 if (priv->tmptype != YELP_URI_DOCUMENT_TYPE_MALLARD)
821 priv->frag_id = g_strdup (query);
822 }
823 else if (hash) {
824 priv->page_id = hash;
825 priv->frag_id = g_strdup (hash);
826 }
827
828 if (priv->frag_id && g_str_has_prefix (priv->frag_id, "search=")) {
829 g_free (priv->frag_id);
830 priv->frag_id = NULL;
831 }
832
833 priv->docuri = g_strconcat ("ghelp:", document,
834 slash ? "/" : NULL,
835 slash, NULL);
836
837 build_ghelp_fulluri (uri);
838
839 g_free (document);
840 g_free (slash);
841 return;
842 }
843
844 static void
resolve_help_uri(YelpUri * uri)845 resolve_help_uri (YelpUri *uri)
846 {
847 /* help:document[/page][?query][#frag]
848 */
849 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
850 gchar *document, *slash, *query, *hash;
851 gchar *colon, *c; /* do not free */
852
853 colon = strchr (priv->res_arg, ':');
854 if (!colon) {
855 priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
856 return;
857 }
858
859 slash = query = hash = NULL;
860 for (c = colon; *c != '\0'; c++) {
861 if (*c == '#' && hash == NULL)
862 hash = c;
863 else if (*c == '?' && query == NULL && hash == NULL)
864 query = c;
865 else if (*c == '/' && slash == NULL && query == NULL && hash == NULL)
866 slash = c;
867 }
868
869 if (slash || query || hash)
870 document = g_strndup (colon + 1,
871 (slash ? slash : (query ? query : hash)) - colon - 1);
872 else
873 document = g_strdup (colon + 1);
874
875 if (slash && (query || hash))
876 slash = g_strndup (slash + 1,
877 (query ? query : hash) - slash - 1);
878 else if (slash)
879 slash = g_strdup (slash + 1);
880
881 if (query && hash)
882 query = g_strndup (query + 1,
883 hash - query - 1);
884 else if (query)
885 query = g_strdup (query + 1);
886
887 if (query) {
888 gchar **keyvals = g_strsplit (query, "&", 0);
889 gint i;
890
891 for (i = 0; keyvals[i]; i++) {
892 gchar *key, *val;
893 val = strchr (keyvals[i], '=');
894 if (val == NULL)
895 continue;
896 key = g_uri_unescape_segment (keyvals[i], val, NULL);
897 val = g_uri_unescape_string (val + 1, NULL);
898
899 g_hash_table_insert (priv->query, key, val);
900 }
901
902 g_strfreev (keyvals);
903 }
904
905 if (hash)
906 hash = g_strdup (hash + 1);
907
908 priv->page_id = (slash ? slash : g_strdup ("index"));
909 resolve_data_dirs (uri, "help", document, priv->page_id, TRUE);
910
911 if (hash)
912 priv->frag_id = hash;
913 if (priv->frag_id && g_str_has_prefix (priv->frag_id, "search=")) {
914 g_free (priv->frag_id);
915 priv->frag_id = NULL;
916 }
917
918 priv->docuri = g_strconcat ("help:", document, NULL);
919
920 priv->fulluri = g_strconcat (priv->docuri,
921 priv->page_id ? "/" : "",
922 priv->page_id ? priv->page_id : "",
923 query ? "?" : "",
924 query ? query : "",
925 priv->frag_id ? "#" : "",
926 priv->frag_id ? priv->frag_id : "",
927 NULL);
928
929 g_free (query);
930 g_free (document);
931 return;
932 }
933
934 static void
resolve_help_list_uri(YelpUri * uri)935 resolve_help_list_uri (YelpUri *uri)
936 {
937 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
938 priv->docuri = g_strdup ("help-list:");
939 priv->fulluri = g_strdup (priv->res_arg);
940 priv->page_id = g_strdup ("index");
941 priv->tmptype = YELP_URI_DOCUMENT_TYPE_HELP_LIST;
942 }
943
944 /*
945 Resolve a manual file's path using 'man -w'. section may be NULL,
946 otherwise should be the section of the manual (ie should have dealt
947 with empty strings before calling this!) Returns NULL if the file
948 can't be found.
949 */
950 static gchar*
find_man_path(gchar * name,gchar * section)951 find_man_path (gchar* name, gchar* section)
952 {
953 const gchar* argv[] = { "man", "-w", NULL, NULL, NULL };
954 gchar **my_argv;
955 gchar *ystdout = NULL;
956 gint status;
957 gchar **lines;
958 GError *error = NULL;
959
960 /* Syntax for man is "man -w <section> <name>", possibly omitting
961 section */
962 if (section) {
963 argv[2] = section;
964 argv[3] = name;
965 } else {
966 argv[2] = name;
967 }
968
969 /* g_strdupv() should accept a "const gchar **". */
970 my_argv = g_strdupv ((gchar **) argv);
971
972 if (!g_spawn_sync (NULL, my_argv, NULL,
973 G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
974 NULL, NULL,
975 &ystdout, NULL, &status, &error)) {
976 g_warning ("Couldn't find path for %s(%s). Error: %s",
977 name, section, error->message);
978 g_error_free (error);
979 }
980
981 g_strfreev (my_argv);
982
983 if (status == 0) {
984 lines = g_strsplit (ystdout, "\n", 2);
985 g_free (ystdout);
986 ystdout = g_strdup (lines[0]);
987
988 g_strfreev (lines);
989 return ystdout;
990 } else {
991 g_free (ystdout);
992 return NULL;
993 }
994 }
995
996 static void
build_man_uris(YelpUri * uri,const char * name,const char * section)997 build_man_uris (YelpUri *uri, const char *name, const char *section)
998 {
999 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
1000
1001 g_assert (priv->tmptype == YELP_URI_DOCUMENT_TYPE_MAN);
1002 priv->docuri = g_strdup ("man:");
1003 priv->fulluri = g_strconcat ("man:", name,
1004 section ? "." : "",
1005 section ? section : "",
1006 NULL);
1007 priv->page_id = g_strconcat (name,
1008 section ? "." : "",
1009 section ? section : "",
1010 NULL);
1011 }
1012
1013 static void
resolve_man_uri(YelpUri * uri)1014 resolve_man_uri (YelpUri *uri)
1015 {
1016 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
1017 /* man:/path/to/file
1018 * man:name(section)
1019 * man:name.section
1020 * man:name
1021 */
1022
1023 /* Search via regular expressions for name, name(section) and
1024 * name.section (assuming that name doesn't contain forward
1025 * slashes or other nasties)
1026 *
1027 * If these don't match, assume that we were given a filename
1028 * (absolute iff it starts with a /).
1029 */
1030 static GRegex* man_not_path = NULL;
1031 GError *error = NULL;
1032 GMatchInfo *match_info = NULL;
1033 gchar *name, *section, *hash;
1034 gchar *path;
1035
1036 if (!man_not_path) {
1037 /* Match group 1 should contain the name; then one of groups 3
1038 * and 4 will contain the section if there was one. Group 6
1039 * will contain any hash fragment. */
1040 man_not_path = g_regex_new ("man:((?:[^ /.()#]|\\.(?=[^0-9]))+)"
1041 "(\\(([0-9A-Za-z]+)\\)|\\.([0-9A-Za-z]+)|)"
1042 "(#([^/ ()]+))?",
1043 0, 0, &error);
1044 if (!man_not_path) {
1045 g_error ("Error with regex in man uri: %s\n",
1046 error->message);
1047 }
1048 }
1049
1050 if (!g_regex_match (man_not_path, priv->res_arg,
1051 0, &match_info)) {
1052 /* The regexp didn't match, so treat as a file name. */
1053 guint i;
1054 static const char *man_suffixes[] = { "gz", "bz2", "lzma", NULL };
1055
1056 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1057 priv->gfile = g_file_new_for_path (priv->res_arg + 4);
1058 name = g_file_get_basename (priv->gfile);
1059 for (i = 0; i < G_N_ELEMENTS (man_suffixes); i++) {
1060 if (is_man_path (name, man_suffixes[i])) {
1061 if (man_suffixes[i])
1062 name[strlen (name) - strlen (man_suffixes[i]) - 1] = '\0';
1063 break;
1064 }
1065 }
1066 build_man_uris (uri, name, NULL);
1067 }
1068 else {
1069 /* The regexp matched, so we've got a name/section pair that
1070 * needs resolving. */
1071 name = g_match_info_fetch (match_info, 1);
1072 section = g_match_info_fetch (match_info, 3);
1073 hash = g_match_info_fetch (match_info, 6);
1074 if (!name) {
1075 g_error ("Error matching strings in man uri '%s'",
1076 priv->res_arg);
1077 }
1078 if ((!section) || (section[0] == '\0')) {
1079 section = g_match_info_fetch (match_info, 4);
1080 }
1081 if (section && section[0] == '\0') section = NULL;
1082
1083 path = find_man_path (name, section);
1084
1085 if (!path) {
1086 priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
1087 return;
1088 }
1089 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1090 priv->gfile = g_file_new_for_path (path);
1091 build_man_uris (uri, name, section);
1092
1093 if (hash && hash[0] != '\0')
1094 resolve_page_and_frag (uri, hash + 1);
1095
1096 g_free (path);
1097 g_match_info_free (match_info);
1098 }
1099 }
1100
1101 static void
build_info_uris(YelpUri * uri,const char * name,const char * section)1102 build_info_uris (YelpUri *uri, const char *name, const char *section)
1103 {
1104 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
1105
1106 g_assert (priv->tmptype == YELP_URI_DOCUMENT_TYPE_INFO);
1107 priv->docuri = g_strconcat ("info:", name, NULL);
1108 priv->fulluri = g_strconcat (priv->docuri,
1109 section ? "#" : "",
1110 section ? section : "",
1111 NULL);
1112 priv->page_id = g_strdup (section);
1113 priv->frag_id = g_strdup (section);
1114 }
1115
1116 static void
resolve_info_uri(YelpUri * uri)1117 resolve_info_uri (YelpUri *uri)
1118 {
1119 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
1120 /* info:/path/to/file
1121 * info:name#node
1122 * info:name
1123 * info:(name)node
1124 * info:(name)
1125 */
1126 static gchar **infopath = NULL;
1127 gchar *name = NULL;
1128 gchar *sect = NULL;
1129 gchar *fullpath = NULL;
1130 /* do not free */
1131 gchar *colon;
1132 gint infopath_i, suffix_i;
1133
1134 if (g_str_has_prefix (priv->res_arg, "info:/")) {
1135 const gchar *hash;
1136
1137 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1138
1139 hash = strchr (priv->res_arg + 5, '#');
1140 if (hash) {
1141 gchar *path;
1142
1143 path = g_strndup (priv->res_arg + 5, hash - (priv->res_arg + 5));
1144 priv->gfile = g_file_new_for_path (path);
1145 g_free (path);
1146 sect = g_strdup (hash + 1);
1147 }
1148 else
1149 priv->gfile = g_file_new_for_path (priv->res_arg + 5);
1150
1151 name = g_file_get_basename (priv->gfile);
1152 for (suffix_i = 0; infosuffix[suffix_i]; suffix_i++) {
1153 if (g_str_has_suffix (name, infosuffix[suffix_i])) {
1154 name[strlen (name) - strlen (infosuffix[suffix_i])] = '\0';
1155 break;
1156 }
1157 }
1158
1159 build_info_uris (uri, name, sect);
1160 g_free (name);
1161 g_free (sect);
1162 return;
1163 }
1164
1165 if (!infopath) {
1166 /* Initialize infopath only once */
1167
1168 /* Use the same logic as the info program. If INFOPATH is not
1169 specified, use the default. If it is specified, just use it
1170 unless it ends with a colon, in which case we add the
1171 default as a suffix.
1172 */
1173 const gchar *env = g_getenv ("INFOPATH");
1174 gchar *paths;
1175 if (!env || env[0] == '\0')
1176 paths = g_strdup (default_info_path);
1177 else if (env[strlen (env)-1] == ':')
1178 paths = g_strconcat (env, default_info_path, NULL);
1179 else
1180 paths = g_strdup (env);
1181
1182 infopath = g_strsplit (paths, ":", 0);
1183
1184 g_free (paths);
1185 }
1186
1187 colon = strchr (priv->res_arg, ':');
1188 if (colon)
1189 colon++;
1190 else
1191 colon = (gchar *) priv->res_arg;
1192
1193 if (colon[0] == '(') {
1194 const gchar *rbrace = strchr (colon, ')');
1195 if (rbrace) {
1196 name = g_strndup (colon + 1, rbrace - colon - 1);
1197 sect = g_strdup (rbrace + 1);
1198 }
1199 }
1200 else {
1201 const gchar *hash = strchr (colon, '#');
1202 if (hash) {
1203 name = g_strndup (colon, hash - colon);
1204 sect = g_strdup (hash + 1);
1205 }
1206 else {
1207 name = g_strdup (colon);
1208 sect = NULL;
1209 }
1210 }
1211
1212 for (infopath_i = 0; infopath[infopath_i]; infopath_i++) {
1213 if (!g_file_test (infopath[infopath_i], G_FILE_TEST_IS_DIR))
1214 continue;
1215 for (suffix_i = 0; infosuffix[suffix_i]; suffix_i++) {
1216 fullpath = g_strconcat (infopath[infopath_i], "/",
1217 name, infosuffix[suffix_i], NULL);
1218 if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR))
1219 break;
1220 g_free (fullpath);
1221 fullpath = NULL;
1222 }
1223 if (fullpath != NULL)
1224 break;
1225 }
1226
1227 if (fullpath) {
1228 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1229 priv->gfile = g_file_new_for_path (fullpath);
1230 build_info_uris (uri, name, sect);
1231 } else {
1232 gchar *res_arg = priv->res_arg;
1233 priv->res_arg = g_strconcat ("man:", name, NULL);
1234 resolve_man_uri (uri);
1235 if (priv->tmptype == YELP_URI_DOCUMENT_TYPE_MAN) {
1236 g_free (priv->res_arg);
1237 priv->res_arg = res_arg;
1238 }
1239 else {
1240 g_free (res_arg);
1241 priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
1242 }
1243 }
1244 g_free (fullpath);
1245 g_free (name);
1246 g_free (sect);
1247 }
1248
1249 static void
resolve_xref_uri(YelpUri * uri)1250 resolve_xref_uri (YelpUri *uri)
1251 {
1252 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
1253 const gchar *arg = priv->res_arg + 5;
1254 YelpUriPrivate *base_priv = yelp_uri_get_instance_private (priv->res_base);
1255
1256 priv->tmptype = base_priv->doctype;
1257 priv->gfile = g_object_ref (base_priv->gfile);
1258 priv->search_path = g_strdupv (base_priv->search_path);
1259 priv->docuri = g_strdup (base_priv->docuri);
1260
1261 if (arg[0] == '#') {
1262 priv->page_id = g_strdup (base_priv->page_id);
1263 priv->frag_id = g_strdup (arg + 1);
1264 }
1265 else {
1266 gchar *hash = strchr (arg, '#');
1267 if (hash) {
1268 priv->page_id = g_strndup (arg, hash - arg);
1269 priv->frag_id = g_strdup (hash + 1);
1270 }
1271 else {
1272 priv->page_id = g_strdup (arg);
1273 priv->frag_id = NULL;
1274 }
1275 }
1276 if (priv->page_id && priv->page_id[0] == '\0') {
1277 g_free (priv->page_id);
1278 if (g_str_has_prefix (priv->docuri, "help:"))
1279 priv->page_id = g_strdup ("index");
1280 else
1281 priv->page_id = NULL;
1282 }
1283
1284 if (priv->page_id &&
1285 g_str_has_prefix (priv->docuri, "info:")) {
1286 /*
1287 Special characters get url-encoded when they get clicked on
1288 as links. Info files, at least, don't want that so decode
1289 the url again here.
1290 */
1291 gchar* tmp = priv->page_id;
1292 priv->page_id = g_uri_unescape_string (tmp, NULL);
1293 g_free (tmp);
1294 }
1295
1296 if (g_str_has_prefix (priv->docuri, "ghelp:"))
1297 build_ghelp_fulluri (uri);
1298 else if (g_str_has_prefix (priv->docuri, "help:"))
1299 priv->fulluri = g_strconcat (priv->docuri,
1300 priv->page_id ? "/" : "",
1301 priv->page_id ? priv->page_id : "",
1302 priv->frag_id ? "#" : "",
1303 priv->frag_id ? priv->frag_id : "",
1304 NULL);
1305 else if (g_str_has_prefix (priv->docuri, "file:") ||
1306 g_str_has_prefix (priv->docuri, "info:") )
1307 priv->fulluri = g_strconcat (priv->docuri,
1308 (priv->page_id || priv->frag_id) ? "#" : "",
1309 priv->page_id ? priv->page_id : "",
1310 priv->frag_id ? "#" : "",
1311 priv->frag_id,
1312 NULL);
1313 else
1314 /* FIXME: other URI schemes */
1315 priv->fulluri = g_strconcat (priv->docuri,
1316 priv->page_id ? "#" : "",
1317 priv->page_id,
1318 NULL);
1319 }
1320
1321 static void
resolve_page_and_frag(YelpUri * uri,const gchar * arg)1322 resolve_page_and_frag (YelpUri *uri, const gchar *arg)
1323 {
1324 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
1325 gchar *hash;
1326
1327 if (!arg || arg[0] == '\0')
1328 return;
1329
1330 hash = strchr (arg, '#');
1331 if (hash) {
1332 priv->page_id = g_strndup (arg, hash - arg);
1333 priv->frag_id = g_strdup (hash + 1);
1334 } else {
1335 priv->page_id = g_strdup (arg);
1336 priv->frag_id = g_strdup (arg);
1337 }
1338 return;
1339 }
1340
1341 static void
resolve_gfile(YelpUri * uri,const gchar * query,const gchar * hash)1342 resolve_gfile (YelpUri *uri, const gchar *query, const gchar *hash)
1343 {
1344 YelpUriPrivate *priv = yelp_uri_get_instance_private (uri);
1345 GFileInfo *info;
1346 GError *error = NULL;
1347
1348 info = g_file_query_info (priv->gfile,
1349 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
1350 G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE,
1351 G_FILE_QUERY_INFO_NONE,
1352 NULL, &error);
1353 if (error) {
1354 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
1355 priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
1356 }
1357 else
1358 priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
1359 g_error_free (error);
1360 return;
1361 }
1362
1363 if (priv->search_path == NULL) {
1364 if (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) ==
1365 G_FILE_TYPE_DIRECTORY) {
1366 priv->search_path = g_new0 (gchar *, 2);
1367 priv->search_path[0] = g_file_get_path (priv->gfile);
1368 } else {
1369 GFile *parent = g_file_get_parent (priv->gfile);
1370 priv->search_path = g_new0 (gchar *, 2);
1371 priv->search_path[0] = g_file_get_path (parent);
1372 g_object_unref (parent);
1373 }
1374 }
1375
1376 if (priv->tmptype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED) {
1377 priv->tmptype = YELP_URI_DOCUMENT_TYPE_EXTERNAL;
1378
1379 if (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) ==
1380 G_FILE_TYPE_DIRECTORY) {
1381 GFile *child = g_file_get_child (priv->gfile, "index.page");
1382 if (g_file_query_exists (child, NULL)) {
1383 char *path;
1384
1385 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
1386 if (priv->page_id == NULL)
1387 priv->page_id = g_strdup (query);
1388 if (priv->frag_id == NULL)
1389 priv->frag_id = g_strdup (hash);
1390
1391 path = g_file_get_path (priv->gfile);
1392 priv->docuri = g_strconcat ("ghelp:", path, NULL);
1393 build_ghelp_fulluri (uri);
1394 g_free (path);
1395 }
1396 else if (yelp_settings_get_editor_mode (yelp_settings_get_default ())) {
1397 g_object_unref (child);
1398 child = g_file_get_child (priv->gfile, "index.page.stub");
1399 if (g_file_query_exists (child, NULL)) {
1400 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
1401 if (priv->page_id == NULL)
1402 priv->page_id = g_strdup (query);
1403 if (priv->frag_id == NULL)
1404 priv->frag_id = g_strdup (hash);
1405 }
1406 }
1407 g_object_unref (child);
1408 }
1409 else {
1410 gchar *basename;
1411 const gchar *mime_type = g_file_info_get_attribute_string (info,
1412 G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
1413 basename = g_file_get_basename (priv->gfile);
1414 if (g_str_has_suffix (basename, ".page")) {
1415 GFile *old;
1416 char *path;
1417
1418 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
1419 old = priv->gfile;
1420 priv->gfile = g_file_get_parent (old);
1421 if (priv->page_id == NULL) {
1422 /* File names aren't really page IDs, so we stick an illegal character
1423 on the beginning so it can't possibly be confused with a real page
1424 ID. Then we let YelpMallardDocument map file names to pages IDs.
1425 */
1426 gchar *tmp = g_file_get_basename (old);
1427 priv->page_id = g_strconcat (G_DIR_SEPARATOR_S, tmp, NULL);
1428 g_free (tmp);
1429 }
1430
1431 if (priv->frag_id == NULL)
1432 priv->frag_id = g_strdup (hash);
1433 path = g_file_get_path (priv->gfile);
1434 priv->docuri = g_strconcat ("ghelp:", path, NULL);
1435 build_ghelp_fulluri (uri);
1436 g_free (path);
1437 g_object_unref (old);
1438 }
1439 else if (g_str_equal (mime_type, "text/xml") ||
1440 g_str_equal (mime_type, "application/docbook+xml") ||
1441 g_str_equal (mime_type, "application/xml") ||
1442 g_str_has_suffix (basename, ".docbook")) {
1443 char *path;
1444
1445 priv->tmptype = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
1446
1447 if (priv->page_id == NULL)
1448 priv->page_id = g_strdup (query);
1449 if (priv->frag_id == NULL)
1450 priv->frag_id = g_strdup (hash);
1451
1452 path = g_file_get_path (priv->gfile);
1453 priv->docuri = g_strconcat ("ghelp:", path, NULL);
1454 build_ghelp_fulluri (uri);
1455 g_free (path);
1456 }
1457 else if (g_str_equal (mime_type, "text/html") ||
1458 g_str_equal (mime_type, "application/xhtml+xml")) {
1459 GFile *parent = g_file_get_parent (priv->gfile);
1460 priv->docuri = g_file_get_uri (parent);
1461 g_object_unref (parent);
1462 priv->tmptype = mime_type[0] == 't' ? YELP_URI_DOCUMENT_TYPE_HTML : YELP_URI_DOCUMENT_TYPE_XHTML;
1463 if (priv->page_id == NULL)
1464 priv->page_id = g_strdup (basename);
1465 if (priv->frag_id == NULL)
1466 priv->frag_id = g_strdup (hash);
1467 if (priv->fulluri == NULL) {
1468 gchar *fulluri;
1469 fulluri = g_file_get_uri (priv->gfile);
1470 priv->fulluri = g_strconcat (fulluri,
1471 priv->frag_id ? "#" : NULL,
1472 priv->frag_id,
1473 NULL);
1474 g_free (fulluri);
1475 }
1476 }
1477 else if (g_str_equal (mime_type, "application/x-gzip") ||
1478 g_str_equal (mime_type, "application/gzip")) {
1479 if (g_str_has_suffix (basename, ".info.gz")) {
1480 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1481 basename[strlen (basename) - strlen (".info.gz")] = '\0';
1482 build_info_uris (uri, basename, hash);
1483 }
1484 else if (is_man_path (basename, "gz")) {
1485 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1486 basename[strlen (basename) - strlen ("gz") - 1] = '\0';
1487 build_man_uris (uri, basename, NULL);
1488 }
1489 }
1490 else if (g_str_equal (mime_type, "application/x-bzip")) {
1491 if (g_str_has_suffix (basename, ".info.bz2")) {
1492 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1493 basename[strlen (basename) - strlen (".info.bz2")] = '\0';
1494 build_info_uris (uri, basename, hash);
1495 }
1496 else if (is_man_path (basename, "bz2")) {
1497 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1498 basename[strlen (basename) - strlen ("bz2") - 1] = '\0';
1499 build_man_uris (uri, basename, NULL);
1500 }
1501 }
1502 else if (g_str_equal (mime_type, "application/x-lzma")) {
1503 if (g_str_has_suffix (basename, ".info.lzma")) {
1504 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1505 basename[strlen (basename) - strlen (".info.lzma")] = '\0';
1506 build_info_uris (uri, basename, hash);
1507 }
1508 else if (is_man_path (basename, "lzma")) {
1509 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1510 basename[strlen (basename) - strlen ("lzma") - 1] = '\0';
1511 build_man_uris (uri, basename, NULL);
1512 }
1513 }
1514 else if (g_str_equal (mime_type, "application/octet-stream")) {
1515 if (g_str_has_suffix (basename, ".info")) {
1516 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1517 basename[strlen (basename) - strlen (".info")] = '\0';
1518 build_info_uris (uri, basename, hash);
1519 }
1520 else if (is_man_path (basename, NULL)) {
1521 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1522 build_man_uris (uri, basename, NULL);
1523 }
1524 }
1525 else if (g_str_equal (mime_type, "text/plain")) {
1526 if (g_str_has_suffix (basename, ".info")) {
1527 priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
1528 basename[strlen (basename) - strlen (".info")] = '\0';
1529 build_info_uris (uri, basename, hash);
1530 }
1531 else if (is_man_path (basename, NULL)) {
1532 priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
1533 build_man_uris (uri, basename, NULL);
1534 } else
1535 priv->tmptype = YELP_URI_DOCUMENT_TYPE_TEXT;
1536 if (priv->frag_id == NULL)
1537 priv->frag_id = g_strdup (hash);
1538 }
1539 else if (g_str_equal (mime_type, "text/x-readme")) {
1540 priv->tmptype = YELP_URI_DOCUMENT_TYPE_TEXT;
1541 }
1542 else {
1543 priv->tmptype = YELP_URI_DOCUMENT_TYPE_EXTERNAL;
1544 }
1545 g_free (basename);
1546 }
1547 }
1548
1549 if (priv->docuri == NULL)
1550 priv->docuri = g_file_get_uri (priv->gfile);
1551
1552 if (priv->fulluri == NULL)
1553 priv->fulluri = g_strconcat (priv->docuri,
1554 (priv->page_id || priv->frag_id) ? "#" : NULL,
1555 priv->page_id ? priv->page_id : "",
1556 priv->frag_id ? "#" : NULL,
1557 priv->frag_id ? priv->frag_id : NULL,
1558 NULL);
1559
1560 g_object_unref (info);
1561 }
1562
1563 static gboolean
is_man_path(const gchar * path,const gchar * encoding)1564 is_man_path (const gchar *path, const gchar *encoding)
1565 {
1566 gchar **iter = (gchar **) mancats;
1567
1568 if (encoding && *encoding) {
1569 while (iter && *iter) {
1570 gchar *ending = g_strdup_printf ("%s.%s", *iter, encoding);
1571 if (g_str_has_suffix (path, ending)) {
1572 g_free (ending);
1573 return TRUE;
1574 }
1575 g_free (ending);
1576 iter++;
1577 }
1578 } else {
1579 while (iter && *iter) {
1580 if (g_str_has_suffix (path, *iter)) {
1581 return TRUE;
1582 }
1583 iter++;
1584 }
1585 }
1586 return FALSE;
1587 }
1588