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, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Shaun McCance <shaunm@gnome.org>
28 #include <glib/gi18n.h>
30 #include <libxml/tree.h>
32 #include "yelp-error.h"
33 #include "yelp-man-document.h"
34 #include "yelp-man-parser.h"
35 #include "yelp-transform.h"
36 #include "yelp-settings.h"
38 #define STYLESHEET DATADIR"/yelp/xslt/man2html.xsl"
41 MAN_STATE_BLANK
, /* Brand new, run transform as needed */
42 MAN_STATE_PARSING
, /* Parsing/transforming document, please wait */
43 MAN_STATE_PARSED
, /* All done, if we ain't got it, it ain't here */
44 MAN_STATE_STOP
/* Stop everything now, object to be disposed */
47 typedef struct _YelpManDocumentPrivate YelpManDocumentPrivate
;
48 struct _YelpManDocumentPrivate
{
57 gboolean process_running
;
58 gboolean transform_running
;
60 YelpTransform
*transform
;
66 typedef struct _YelpLangEncodings YelpLangEncodings
;
67 struct _YelpLangEncodings
{
71 /* http://www.w3.org/International/O-charset-lang.html */
72 static const YelpLangEncodings langmap
[] = {
73 { "C", "ISO-8859-1" },
74 { "af", "ISO-8859-1" },
75 { "ar", "ISO-8859-6" },
76 { "bg", "ISO-8859-5" },
77 { "be", "ISO-8859-5" },
78 { "ca", "ISO-8859-1" },
79 { "cs", "ISO-8859-2" },
80 { "da", "ISO-8859-1" },
81 { "de", "ISO-8859-1" },
82 { "el", "ISO-8859-7" },
83 { "en", "ISO-8859-1" },
84 { "eo", "ISO-8859-3" },
85 { "es", "ISO-8859-1" },
86 { "et", "ISO-8859-15" },
87 { "eu", "ISO-8859-1" },
88 { "fi", "ISO-8859-1" },
89 { "fo", "ISO-8859-1" },
90 { "fr", "ISO-8859-1" },
91 { "ga", "ISO-8859-1" },
92 { "gd", "ISO-8859-1" },
93 { "gl", "ISO-8859-1" },
94 { "hu", "ISO-8859-2" },
95 { "id", "ISO-8859-1" }, /* is this right */
96 { "mt", "ISO-8859-3" },
97 { "is", "ISO-8859-1" },
98 { "it", "ISO-8859-1" },
99 { "iw", "ISO-8859-8" },
102 { "lt", "ISO-8859-13" },
103 { "lv", "ISO-8859-13" },
104 { "mk", "ISO-8859-5" },
105 { "mt", "ISO-8859-3" },
106 { "no", "ISO-8859-1" },
107 { "pl", "ISO-8859-2" },
108 { "pt_BR", "ISO-8859-1" },
109 { "ro", "ISO-8859-2" },
111 { "sl", "ISO-8859-2" },
112 { "sr", "ISO-8859-2" }, /* Latin, not cyrillic */
113 { "sk", "ISO-8859-2" },
114 { "sv", "ISO-8859-1" },
115 { "tr", "ISO-8859-9" },
116 { "uk", "ISO-8859-5" },
122 static void yelp_man_document_class_init (YelpManDocumentClass
*klass
);
123 static void yelp_man_document_init (YelpManDocument
*man
);
124 static void yelp_man_document_dispose (GObject
*object
);
125 static void yelp_man_document_finalize (GObject
*object
);
128 static gboolean
man_request_page (YelpDocument
*document
,
129 const gchar
*page_id
,
130 GCancellable
*cancellable
,
131 YelpDocumentCallback callback
,
135 static void transform_chunk_ready (YelpTransform
*transform
,
137 YelpManDocument
*man
);
138 static void transform_finished (YelpTransform
*transform
,
139 YelpManDocument
*man
);
140 static void transform_error (YelpTransform
*transform
,
141 YelpManDocument
*man
);
142 static void transform_finalized (YelpManDocument
*man
,
146 static void man_document_process (YelpManDocument
*man
);
148 static void man_document_disconnect (YelpManDocument
*man
);
151 G_DEFINE_TYPE (YelpManDocument
, yelp_man_document
, YELP_TYPE_DOCUMENT
);
152 #define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_MAN_DOCUMENT, YelpManDocumentPrivate))
155 yelp_man_document_class_init (YelpManDocumentClass
*klass
)
157 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
158 YelpDocumentClass
*document_class
= YELP_DOCUMENT_CLASS (klass
);
160 object_class
->dispose
= yelp_man_document_dispose
;
161 object_class
->finalize
= yelp_man_document_finalize
;
163 document_class
->request_page
= man_request_page
;
165 g_type_class_add_private (klass
, sizeof (YelpManDocumentPrivate
));
169 yelp_man_document_init (YelpManDocument
*man
)
171 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
173 priv
->state
= MAN_STATE_BLANK
;
174 priv
->mutex
= g_mutex_new ();
178 yelp_man_document_dispose (GObject
*object
)
180 YelpManDocumentPrivate
*priv
= GET_PRIV (object
);
183 g_object_unref (priv
->uri
);
187 G_OBJECT_CLASS (yelp_man_document_parent_class
)->dispose (object
);
191 yelp_man_document_finalize (GObject
*object
)
193 YelpManDocumentPrivate
*priv
= GET_PRIV (object
);
196 xmlFreeDoc (priv
->xmldoc
);
198 g_mutex_free (priv
->mutex
);
200 G_OBJECT_CLASS (yelp_man_document_parent_class
)->finalize (object
);
203 /******************************************************************************/
206 yelp_man_document_new (YelpUri
*uri
)
208 YelpManDocument
*man
;
209 YelpManDocumentPrivate
*priv
;
211 g_return_val_if_fail (uri
!= NULL
, NULL
);
213 man
= (YelpManDocument
*) g_object_new (YELP_TYPE_MAN_DOCUMENT
, NULL
);
214 priv
= GET_PRIV (man
);
216 priv
->uri
= g_object_ref (uri
);
218 return (YelpDocument
*) man
;
222 /******************************************************************************/
223 /** YelpDocument **************************************************************/
226 man_request_page (YelpDocument
*document
,
227 const gchar
*page_id
,
228 GCancellable
*cancellable
,
229 YelpDocumentCallback callback
,
232 YelpManDocumentPrivate
*priv
= GET_PRIV (document
);
241 YELP_DOCUMENT_CLASS (yelp_man_document_parent_class
)->request_page (document
,
250 g_mutex_lock (priv
->mutex
);
252 switch (priv
->state
) {
253 case MAN_STATE_BLANK
:
254 priv
->state
= MAN_STATE_PARSING
;
255 priv
->process_running
= TRUE
;
256 g_object_ref (document
);
257 yelp_document_set_page_id (document
, NULL
, "//index");
258 yelp_document_set_page_id (document
, "//index", "//index");
259 yelp_document_set_root_id (document
, "//index", "//index");
260 priv
->thread
= g_thread_create ((GThreadFunc
) man_document_process
,
261 document
, FALSE
, NULL
);
263 case MAN_STATE_PARSING
:
265 case MAN_STATE_PARSED
:
267 docuri
= yelp_uri_get_document_uri (priv
->uri
);
268 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
269 _("The page ‘%s’ was not found in the document ‘%s’."),
272 yelp_document_signal (document
, page_id
,
273 YELP_DOCUMENT_SIGNAL_ERROR
,
275 g_error_free (error
);
279 g_mutex_unlock (priv
->mutex
);
283 /******************************************************************************/
284 /** YelpTransform *************************************************************/
287 transform_chunk_ready (YelpTransform
*transform
,
289 YelpManDocument
*man
)
291 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
294 g_assert (transform
== priv
->transform
);
296 if (priv
->state
== MAN_STATE_STOP
) {
297 man_document_disconnect (man
);
301 content
= yelp_transform_take_chunk (transform
, chunk_id
);
302 yelp_document_give_contents (YELP_DOCUMENT (man
),
305 "application/xhtml+xml");
307 yelp_document_signal (YELP_DOCUMENT (man
),
309 YELP_DOCUMENT_SIGNAL_INFO
,
311 yelp_document_signal (YELP_DOCUMENT (man
),
313 YELP_DOCUMENT_SIGNAL_CONTENTS
,
318 transform_finished (YelpTransform
*transform
,
319 YelpManDocument
*man
)
321 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
325 g_assert (transform
== priv
->transform
);
327 if (priv
->state
== MAN_STATE_STOP
) {
328 man_document_disconnect (man
);
332 man_document_disconnect (man
);
333 priv
->state
= MAN_STATE_PARSED
;
335 /* We want to free priv->xmldoc, but we can't free it before transform
336 is finalized. Otherwise, we could crash when YelpTransform frees
337 its libxslt resources.
339 g_object_weak_ref ((GObject
*) transform
,
340 (GWeakNotify
) transform_finalized
,
343 docuri
= yelp_uri_get_document_uri (priv
->uri
);
344 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
345 _("The requested page was not found in the document ‘%s’."),
348 yelp_document_error_pending ((YelpDocument
*) man
, error
);
349 g_error_free (error
);
353 transform_error (YelpTransform
*transform
,
354 YelpManDocument
*man
)
356 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
359 g_assert (transform
== priv
->transform
);
361 if (priv
->state
== MAN_STATE_STOP
) {
362 man_document_disconnect (man
);
366 error
= yelp_transform_get_error (transform
);
367 yelp_document_error_pending ((YelpDocument
*) man
, error
);
368 g_error_free (error
);
370 man_document_disconnect (man
);
374 transform_finalized (YelpManDocument
*man
,
377 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
380 xmlFreeDoc (priv
->xmldoc
);
385 /******************************************************************************/
386 /** Threaded ******************************************************************/
389 man_document_process (YelpManDocument
*man
)
391 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
393 gchar
*filepath
= NULL
;
396 gchar
**params
= NULL
;
397 YelpManParser
*parser
;
398 const gchar
*language
, *encoding
;
400 file
= yelp_uri_get_file (priv
->uri
);
402 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
403 _("The file does not exist."));
404 yelp_document_error_pending ((YelpDocument
*) man
, error
);
405 g_error_free (error
);
409 filepath
= g_file_get_path (file
);
410 g_object_unref (file
);
411 if (!g_file_test (filepath
, G_FILE_TEST_IS_REGULAR
)) {
412 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
413 _("The file ‘%s’ does not exist."),
415 yelp_document_error_pending ((YelpDocument
*) man
, error
);
416 g_error_free (error
);
420 /* FIXME: get the language */
423 /* default encoding if the language doesn't match below */
424 encoding
= g_getenv("MAN_ENCODING");
425 if (encoding
== NULL
)
426 encoding
= "ISO-8859-1";
428 if (language
!= NULL
) {
430 for (i
= 0; langmap
[i
].language
!= NULL
; i
++) {
431 if (g_str_equal (language
, langmap
[i
].language
)) {
432 encoding
= langmap
[i
].encoding
;
438 parser
= yelp_man_parser_new ();
439 priv
->xmldoc
= yelp_man_parser_parse_file (parser
, filepath
, encoding
);
440 yelp_man_parser_free (parser
);
442 if (priv
->xmldoc
== NULL
) {
443 error
= g_error_new (YELP_ERROR
, YELP_ERROR_PROCESSING
,
444 _("The file ‘%s’ could not be parsed because it is"
445 " not a well-formed man page."),
447 yelp_document_error_pending ((YelpDocument
*) man
, error
);
450 g_mutex_lock (priv
->mutex
);
451 if (priv
->state
== MAN_STATE_STOP
) {
452 g_mutex_unlock (priv
->mutex
);
456 priv
->transform
= yelp_transform_new (STYLESHEET
);
458 g_signal_connect (priv
->transform
, "chunk-ready",
459 (GCallback
) transform_chunk_ready
,
462 g_signal_connect (priv
->transform
, "finished",
463 (GCallback
) transform_finished
,
466 g_signal_connect (priv
->transform
, "error",
467 (GCallback
) transform_error
,
470 params
= yelp_settings_get_all_params (yelp_settings_get_default (), 0, ¶ms_i
);
472 priv
->transform_running
= TRUE
;
473 yelp_transform_start (priv
->transform
,
476 (const gchar
* const *) params
);
478 g_mutex_unlock (priv
->mutex
);
482 priv
->process_running
= FALSE
;
483 g_object_unref (man
);
487 man_document_disconnect (YelpManDocument
*man
)
489 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
490 if (priv
->chunk_ready
) {
491 g_signal_handler_disconnect (priv
->transform
, priv
->chunk_ready
);
492 priv
->chunk_ready
= 0;
494 if (priv
->finished
) {
495 g_signal_handler_disconnect (priv
->transform
, priv
->finished
);
499 g_signal_handler_disconnect (priv
->transform
, priv
->error
);
502 yelp_transform_cancel (priv
->transform
);
503 g_object_unref (priv
->transform
);
504 priv
->transform
= NULL
;
505 priv
->transform_running
= FALSE
;