1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * Copyright (C) 2007-2010 Shaun McCance <shaunm@gnome.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 * Author: Shaun McCance <shaunm@gnome.org>
26 #include <glib/gi18n.h>
28 #include <libxml/tree.h>
30 #include "yelp-error.h"
31 #include "yelp-man-document.h"
32 #include "yelp-man-parser.h"
33 #include "yelp-transform.h"
34 #include "yelp-settings.h"
36 #define STYLESHEET DATADIR"/yelp/xslt/man2html.xsl"
39 MAN_STATE_BLANK
, /* Brand new, run transform as needed */
40 MAN_STATE_PARSING
, /* Parsing/transforming document, please wait */
41 MAN_STATE_PARSED
, /* All done, if we ain't got it, it ain't here */
42 MAN_STATE_STOP
/* Stop everything now, object to be disposed */
45 typedef struct _YelpManDocumentPrivate YelpManDocumentPrivate
;
46 struct _YelpManDocumentPrivate
{
55 gboolean process_running
;
56 gboolean transform_running
;
58 YelpTransform
*transform
;
64 typedef struct _YelpLangEncodings YelpLangEncodings
;
65 struct _YelpLangEncodings
{
66 const gchar
*language
;
67 const gchar
*encoding
;
69 /* http://www.w3.org/International/O-charset-lang.html */
70 static const YelpLangEncodings langmap
[] = {
71 { "C", "ISO-8859-1" },
72 { "af", "ISO-8859-1" },
73 { "ar", "ISO-8859-6" },
74 { "bg", "ISO-8859-5" },
75 { "be", "ISO-8859-5" },
76 { "ca", "ISO-8859-1" },
77 { "cs", "ISO-8859-2" },
78 { "da", "ISO-8859-1" },
79 { "de", "ISO-8859-1" },
80 { "el", "ISO-8859-7" },
81 { "en", "ISO-8859-1" },
82 { "eo", "ISO-8859-3" },
83 { "es", "ISO-8859-1" },
84 { "et", "ISO-8859-15" },
85 { "eu", "ISO-8859-1" },
86 { "fi", "ISO-8859-1" },
87 { "fo", "ISO-8859-1" },
88 { "fr", "ISO-8859-1" },
89 { "ga", "ISO-8859-1" },
90 { "gd", "ISO-8859-1" },
91 { "gl", "ISO-8859-1" },
92 { "hu", "ISO-8859-2" },
93 { "id", "ISO-8859-1" }, /* is this right */
94 { "mt", "ISO-8859-3" },
95 { "is", "ISO-8859-1" },
96 { "it", "ISO-8859-1" },
97 { "iw", "ISO-8859-8" },
100 { "lt", "ISO-8859-13" },
101 { "lv", "ISO-8859-13" },
102 { "mk", "ISO-8859-5" },
103 { "mt", "ISO-8859-3" },
104 { "no", "ISO-8859-1" },
105 { "pl", "ISO-8859-2" },
106 { "pt_BR", "ISO-8859-1" },
107 { "ro", "ISO-8859-2" },
109 { "sl", "ISO-8859-2" },
110 { "sr", "ISO-8859-2" }, /* Latin, not cyrillic */
111 { "sk", "ISO-8859-2" },
112 { "sv", "ISO-8859-1" },
113 { "tr", "ISO-8859-9" },
114 { "uk", "ISO-8859-5" },
120 static void yelp_man_document_finalize (GObject
*object
);
123 static gboolean
man_request_page (YelpDocument
*document
,
124 const gchar
*page_id
,
125 GCancellable
*cancellable
,
126 YelpDocumentCallback callback
,
128 GDestroyNotify notify
);
131 static void transform_chunk_ready (YelpTransform
*transform
,
133 YelpManDocument
*man
);
134 static void transform_finished (YelpTransform
*transform
,
135 YelpManDocument
*man
);
136 static void transform_error (YelpTransform
*transform
,
137 YelpManDocument
*man
);
138 static void transform_finalized (YelpManDocument
*man
,
142 static void man_document_process (YelpManDocument
*man
);
144 static void man_document_disconnect (YelpManDocument
*man
);
147 G_DEFINE_TYPE (YelpManDocument
, yelp_man_document
, YELP_TYPE_DOCUMENT
)
148 #define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_MAN_DOCUMENT, YelpManDocumentPrivate))
151 yelp_man_document_class_init (YelpManDocumentClass
*klass
)
153 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
154 YelpDocumentClass
*document_class
= YELP_DOCUMENT_CLASS (klass
);
156 object_class
->finalize
= yelp_man_document_finalize
;
158 document_class
->request_page
= man_request_page
;
160 g_type_class_add_private (klass
, sizeof (YelpManDocumentPrivate
));
164 yelp_man_document_init (YelpManDocument
*man
)
166 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
168 priv
->state
= MAN_STATE_BLANK
;
169 g_mutex_init (&priv
->mutex
);
173 yelp_man_document_finalize (GObject
*object
)
175 YelpManDocumentPrivate
*priv
= GET_PRIV (object
);
178 xmlFreeDoc (priv
->xmldoc
);
180 g_mutex_clear (&priv
->mutex
);
181 g_free (priv
->page_id
);
183 G_OBJECT_CLASS (yelp_man_document_parent_class
)->finalize (object
);
186 /******************************************************************************/
189 yelp_man_document_new (YelpUri
*uri
)
191 g_return_val_if_fail (uri
!= NULL
, NULL
);
193 return (YelpDocument
*) g_object_new (YELP_TYPE_MAN_DOCUMENT
,
199 /******************************************************************************/
200 /** YelpDocument **************************************************************/
203 man_request_page (YelpDocument
*document
,
204 const gchar
*page_id
,
205 GCancellable
*cancellable
,
206 YelpDocumentCallback callback
,
208 GDestroyNotify notify
)
210 YelpManDocumentPrivate
*priv
= GET_PRIV (document
);
211 gchar
*docuri
, *fulluri
;
215 fulluri
= yelp_uri_get_canonical_uri (yelp_document_get_uri (document
));
216 if (g_str_has_prefix (fulluri
, "man:"))
217 priv
->page_id
= g_strdup (fulluri
+ 4);
219 priv
->page_id
= g_strdup ("//index");
223 YELP_DOCUMENT_CLASS (yelp_man_document_parent_class
)->request_page (document
,
233 g_mutex_lock (&priv
->mutex
);
235 switch (priv
->state
) {
236 case MAN_STATE_BLANK
:
237 priv
->state
= MAN_STATE_PARSING
;
238 priv
->process_running
= TRUE
;
239 g_object_ref (document
);
240 yelp_document_set_page_id (document
, page_id
, priv
->page_id
);
241 yelp_document_set_page_id (document
, NULL
, priv
->page_id
);
242 yelp_document_set_page_id (document
, "//index", priv
->page_id
);
243 yelp_document_set_page_id (document
, priv
->page_id
, priv
->page_id
);
244 yelp_document_set_root_id (document
, priv
->page_id
, priv
->page_id
);
245 priv
->thread
= g_thread_new ("man-page",
246 (GThreadFunc
)(GCallback
) man_document_process
,
249 case MAN_STATE_PARSING
:
251 case MAN_STATE_PARSED
:
253 docuri
= yelp_uri_get_document_uri (yelp_document_get_uri (document
));
254 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
255 _("The page ‘%s’ was not found in the document ‘%s’."),
258 yelp_document_signal (document
, page_id
,
259 YELP_DOCUMENT_SIGNAL_ERROR
,
261 g_error_free (error
);
264 g_assert_not_reached ();
268 g_mutex_unlock (&priv
->mutex
);
273 /******************************************************************************/
274 /** YelpTransform *************************************************************/
277 transform_chunk_ready (YelpTransform
*transform
,
279 YelpManDocument
*man
)
281 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
284 g_assert (transform
== priv
->transform
);
286 if (priv
->state
== MAN_STATE_STOP
) {
287 man_document_disconnect (man
);
291 content
= yelp_transform_take_chunk (transform
, chunk_id
);
292 yelp_document_give_contents (YELP_DOCUMENT (man
),
295 "application/xhtml+xml");
297 yelp_document_signal (YELP_DOCUMENT (man
),
299 YELP_DOCUMENT_SIGNAL_INFO
,
301 yelp_document_signal (YELP_DOCUMENT (man
),
303 YELP_DOCUMENT_SIGNAL_CONTENTS
,
308 transform_finished (YelpTransform
*transform
,
309 YelpManDocument
*man
)
311 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
315 g_assert (transform
== priv
->transform
);
317 if (priv
->state
== MAN_STATE_STOP
) {
318 man_document_disconnect (man
);
322 man_document_disconnect (man
);
323 priv
->state
= MAN_STATE_PARSED
;
325 /* We want to free priv->xmldoc, but we can't free it before transform
326 is finalized. Otherwise, we could crash when YelpTransform frees
327 its libxslt resources.
329 g_object_weak_ref ((GObject
*) transform
,
330 (GWeakNotify
) transform_finalized
,
333 docuri
= yelp_uri_get_document_uri (yelp_document_get_uri ((YelpDocument
*) man
));
334 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
335 _("The requested page was not found in the document ‘%s’."),
338 yelp_document_error_pending ((YelpDocument
*) man
, error
);
339 g_error_free (error
);
343 transform_error (YelpTransform
*transform
,
344 YelpManDocument
*man
)
346 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
349 g_assert (transform
== priv
->transform
);
351 if (priv
->state
== MAN_STATE_STOP
) {
352 man_document_disconnect (man
);
356 error
= yelp_transform_get_error (transform
);
357 yelp_document_error_pending ((YelpDocument
*) man
, error
);
358 g_error_free (error
);
360 man_document_disconnect (man
);
364 transform_finalized (YelpManDocument
*man
,
367 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
370 xmlFreeDoc (priv
->xmldoc
);
375 /******************************************************************************/
376 /** Threaded ******************************************************************/
379 man_document_process (YelpManDocument
*man
)
381 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
383 gchar
*filepath
= NULL
;
386 gchar
**params
= NULL
;
387 YelpManParser
*parser
;
388 const gchar
*language
, *encoding
;
390 file
= yelp_uri_get_file (yelp_document_get_uri ((YelpDocument
*) man
));
392 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
393 _("The file does not exist."));
394 yelp_document_error_pending ((YelpDocument
*) man
, error
);
395 g_error_free (error
);
399 filepath
= g_file_get_path (file
);
400 g_object_unref (file
);
401 if (!g_file_test (filepath
, G_FILE_TEST_IS_REGULAR
)) {
402 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
403 _("The file ‘%s’ does not exist."),
405 yelp_document_error_pending ((YelpDocument
*) man
, error
);
406 g_error_free (error
);
410 /* FIXME: get the language */
413 /* default encoding if the language doesn't match below */
414 encoding
= g_getenv("MAN_ENCODING");
415 if (encoding
== NULL
)
416 encoding
= "ISO-8859-1";
418 if (language
!= NULL
) {
420 for (i
= 0; langmap
[i
].language
!= NULL
; i
++) {
421 if (g_str_equal (language
, langmap
[i
].language
)) {
422 encoding
= langmap
[i
].encoding
;
428 parser
= yelp_man_parser_new ();
429 priv
->xmldoc
= yelp_man_parser_parse_file (parser
, filepath
, &error
);
430 yelp_man_parser_free (parser
);
432 if (priv
->xmldoc
== NULL
) {
433 yelp_document_error_pending ((YelpDocument
*) man
, error
);
436 g_mutex_lock (&priv
->mutex
);
437 if (priv
->state
== MAN_STATE_STOP
) {
438 g_mutex_unlock (&priv
->mutex
);
442 priv
->transform
= yelp_transform_new (STYLESHEET
);
444 g_signal_connect (priv
->transform
, "chunk-ready",
445 (GCallback
) transform_chunk_ready
,
448 g_signal_connect (priv
->transform
, "finished",
449 (GCallback
) transform_finished
,
452 g_signal_connect (priv
->transform
, "error",
453 (GCallback
) transform_error
,
456 params
= yelp_settings_get_all_params (yelp_settings_get_default (), 0, ¶ms_i
);
458 priv
->transform_running
= TRUE
;
459 yelp_transform_start (priv
->transform
,
462 (const gchar
* const *) params
);
464 g_mutex_unlock (&priv
->mutex
);
468 priv
->process_running
= FALSE
;
469 g_object_unref (man
);
473 man_document_disconnect (YelpManDocument
*man
)
475 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
476 if (priv
->chunk_ready
) {
477 g_signal_handler_disconnect (priv
->transform
, priv
->chunk_ready
);
478 priv
->chunk_ready
= 0;
480 if (priv
->finished
) {
481 g_signal_handler_disconnect (priv
->transform
, priv
->finished
);
485 g_signal_handler_disconnect (priv
->transform
, priv
->error
);
488 yelp_transform_cancel (priv
->transform
);
489 g_object_unref (priv
->transform
);
490 priv
->transform
= NULL
;
491 priv
->transform_running
= FALSE
;