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
{
58 gboolean process_running
;
59 gboolean transform_running
;
61 YelpTransform
*transform
;
67 typedef struct _YelpLangEncodings YelpLangEncodings
;
68 struct _YelpLangEncodings
{
72 /* http://www.w3.org/International/O-charset-lang.html */
73 static const YelpLangEncodings langmap
[] = {
74 { "C", "ISO-8859-1" },
75 { "af", "ISO-8859-1" },
76 { "ar", "ISO-8859-6" },
77 { "bg", "ISO-8859-5" },
78 { "be", "ISO-8859-5" },
79 { "ca", "ISO-8859-1" },
80 { "cs", "ISO-8859-2" },
81 { "da", "ISO-8859-1" },
82 { "de", "ISO-8859-1" },
83 { "el", "ISO-8859-7" },
84 { "en", "ISO-8859-1" },
85 { "eo", "ISO-8859-3" },
86 { "es", "ISO-8859-1" },
87 { "et", "ISO-8859-15" },
88 { "eu", "ISO-8859-1" },
89 { "fi", "ISO-8859-1" },
90 { "fo", "ISO-8859-1" },
91 { "fr", "ISO-8859-1" },
92 { "ga", "ISO-8859-1" },
93 { "gd", "ISO-8859-1" },
94 { "gl", "ISO-8859-1" },
95 { "hu", "ISO-8859-2" },
96 { "id", "ISO-8859-1" }, /* is this right */
97 { "mt", "ISO-8859-3" },
98 { "is", "ISO-8859-1" },
99 { "it", "ISO-8859-1" },
100 { "iw", "ISO-8859-8" },
103 { "lt", "ISO-8859-13" },
104 { "lv", "ISO-8859-13" },
105 { "mk", "ISO-8859-5" },
106 { "mt", "ISO-8859-3" },
107 { "no", "ISO-8859-1" },
108 { "pl", "ISO-8859-2" },
109 { "pt_BR", "ISO-8859-1" },
110 { "ro", "ISO-8859-2" },
112 { "sl", "ISO-8859-2" },
113 { "sr", "ISO-8859-2" }, /* Latin, not cyrillic */
114 { "sk", "ISO-8859-2" },
115 { "sv", "ISO-8859-1" },
116 { "tr", "ISO-8859-9" },
117 { "uk", "ISO-8859-5" },
123 static void yelp_man_document_class_init (YelpManDocumentClass
*klass
);
124 static void yelp_man_document_init (YelpManDocument
*man
);
125 static void yelp_man_document_dispose (GObject
*object
);
126 static void yelp_man_document_finalize (GObject
*object
);
129 static gboolean
man_request_page (YelpDocument
*document
,
130 const gchar
*page_id
,
131 GCancellable
*cancellable
,
132 YelpDocumentCallback callback
,
136 static void transform_chunk_ready (YelpTransform
*transform
,
138 YelpManDocument
*man
);
139 static void transform_finished (YelpTransform
*transform
,
140 YelpManDocument
*man
);
141 static void transform_error (YelpTransform
*transform
,
142 YelpManDocument
*man
);
143 static void transform_finalized (YelpManDocument
*man
,
147 static void man_document_process (YelpManDocument
*man
);
149 static void man_document_disconnect (YelpManDocument
*man
);
152 G_DEFINE_TYPE (YelpManDocument
, yelp_man_document
, YELP_TYPE_DOCUMENT
);
153 #define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_MAN_DOCUMENT, YelpManDocumentPrivate))
156 yelp_man_document_class_init (YelpManDocumentClass
*klass
)
158 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
159 YelpDocumentClass
*document_class
= YELP_DOCUMENT_CLASS (klass
);
161 object_class
->dispose
= yelp_man_document_dispose
;
162 object_class
->finalize
= yelp_man_document_finalize
;
164 document_class
->request_page
= man_request_page
;
166 g_type_class_add_private (klass
, sizeof (YelpManDocumentPrivate
));
170 yelp_man_document_init (YelpManDocument
*man
)
172 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
174 priv
->state
= MAN_STATE_BLANK
;
175 priv
->mutex
= g_mutex_new ();
179 yelp_man_document_dispose (GObject
*object
)
181 YelpManDocumentPrivate
*priv
= GET_PRIV (object
);
184 g_object_unref (priv
->uri
);
188 G_OBJECT_CLASS (yelp_man_document_parent_class
)->dispose (object
);
192 yelp_man_document_finalize (GObject
*object
)
194 YelpManDocumentPrivate
*priv
= GET_PRIV (object
);
197 xmlFreeDoc (priv
->xmldoc
);
199 g_mutex_free (priv
->mutex
);
200 g_free (priv
->page_id
);
202 G_OBJECT_CLASS (yelp_man_document_parent_class
)->finalize (object
);
205 /******************************************************************************/
208 yelp_man_document_new (YelpUri
*uri
)
210 YelpManDocument
*man
;
211 YelpManDocumentPrivate
*priv
;
214 g_return_val_if_fail (uri
!= NULL
, NULL
);
216 doc_uri
= yelp_uri_get_document_uri (uri
);
217 man
= (YelpManDocument
*) g_object_new (YELP_TYPE_MAN_DOCUMENT
,
218 "document-uri", doc_uri
,
221 priv
= GET_PRIV (man
);
223 priv
->uri
= g_object_ref (uri
);
225 return (YelpDocument
*) man
;
229 /******************************************************************************/
230 /** YelpDocument **************************************************************/
233 man_request_page (YelpDocument
*document
,
234 const gchar
*page_id
,
235 GCancellable
*cancellable
,
236 YelpDocumentCallback callback
,
239 YelpManDocumentPrivate
*priv
= GET_PRIV (document
);
240 gchar
*docuri
, *fulluri
;
244 fulluri
= yelp_uri_get_canonical_uri (priv
->uri
);
245 if (g_str_has_prefix (fulluri
, "man:"))
246 priv
->page_id
= g_strdup (fulluri
+ 4);
248 priv
->page_id
= g_strdup ("//index");
252 YELP_DOCUMENT_CLASS (yelp_man_document_parent_class
)->request_page (document
,
261 g_mutex_lock (priv
->mutex
);
263 switch (priv
->state
) {
264 case MAN_STATE_BLANK
:
265 priv
->state
= MAN_STATE_PARSING
;
266 priv
->process_running
= TRUE
;
267 g_object_ref (document
);
268 yelp_document_set_page_id (document
, page_id
, priv
->page_id
);
269 yelp_document_set_page_id (document
, NULL
, priv
->page_id
);
270 yelp_document_set_page_id (document
, "//index", priv
->page_id
);
271 yelp_document_set_page_id (document
, priv
->page_id
, priv
->page_id
);
272 yelp_document_set_root_id (document
, priv
->page_id
, priv
->page_id
);
273 priv
->thread
= g_thread_create ((GThreadFunc
) man_document_process
,
274 document
, FALSE
, NULL
);
276 case MAN_STATE_PARSING
:
278 case MAN_STATE_PARSED
:
280 docuri
= yelp_uri_get_document_uri (priv
->uri
);
281 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
282 _("The page ‘%s’ was not found in the document ‘%s’."),
285 yelp_document_signal (document
, page_id
,
286 YELP_DOCUMENT_SIGNAL_ERROR
,
288 g_error_free (error
);
292 g_mutex_unlock (priv
->mutex
);
297 /******************************************************************************/
298 /** YelpTransform *************************************************************/
301 transform_chunk_ready (YelpTransform
*transform
,
303 YelpManDocument
*man
)
305 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
308 g_assert (transform
== priv
->transform
);
310 if (priv
->state
== MAN_STATE_STOP
) {
311 man_document_disconnect (man
);
315 content
= yelp_transform_take_chunk (transform
, chunk_id
);
316 yelp_document_give_contents (YELP_DOCUMENT (man
),
319 "application/xhtml+xml");
321 yelp_document_signal (YELP_DOCUMENT (man
),
323 YELP_DOCUMENT_SIGNAL_INFO
,
325 yelp_document_signal (YELP_DOCUMENT (man
),
327 YELP_DOCUMENT_SIGNAL_CONTENTS
,
332 transform_finished (YelpTransform
*transform
,
333 YelpManDocument
*man
)
335 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
339 g_assert (transform
== priv
->transform
);
341 if (priv
->state
== MAN_STATE_STOP
) {
342 man_document_disconnect (man
);
346 man_document_disconnect (man
);
347 priv
->state
= MAN_STATE_PARSED
;
349 /* We want to free priv->xmldoc, but we can't free it before transform
350 is finalized. Otherwise, we could crash when YelpTransform frees
351 its libxslt resources.
353 g_object_weak_ref ((GObject
*) transform
,
354 (GWeakNotify
) transform_finalized
,
357 docuri
= yelp_uri_get_document_uri (priv
->uri
);
358 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
359 _("The requested page was not found in the document ‘%s’."),
362 yelp_document_error_pending ((YelpDocument
*) man
, error
);
363 g_error_free (error
);
367 transform_error (YelpTransform
*transform
,
368 YelpManDocument
*man
)
370 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
373 g_assert (transform
== priv
->transform
);
375 if (priv
->state
== MAN_STATE_STOP
) {
376 man_document_disconnect (man
);
380 error
= yelp_transform_get_error (transform
);
381 yelp_document_error_pending ((YelpDocument
*) man
, error
);
382 g_error_free (error
);
384 man_document_disconnect (man
);
388 transform_finalized (YelpManDocument
*man
,
391 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
394 xmlFreeDoc (priv
->xmldoc
);
399 /******************************************************************************/
400 /** Threaded ******************************************************************/
403 man_document_process (YelpManDocument
*man
)
405 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
407 gchar
*filepath
= NULL
;
410 gchar
**params
= NULL
;
411 YelpManParser
*parser
;
412 const gchar
*language
, *encoding
;
414 file
= yelp_uri_get_file (priv
->uri
);
416 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
417 _("The file does not exist."));
418 yelp_document_error_pending ((YelpDocument
*) man
, error
);
419 g_error_free (error
);
423 filepath
= g_file_get_path (file
);
424 g_object_unref (file
);
425 if (!g_file_test (filepath
, G_FILE_TEST_IS_REGULAR
)) {
426 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
427 _("The file ‘%s’ does not exist."),
429 yelp_document_error_pending ((YelpDocument
*) man
, error
);
430 g_error_free (error
);
434 /* FIXME: get the language */
437 /* default encoding if the language doesn't match below */
438 encoding
= g_getenv("MAN_ENCODING");
439 if (encoding
== NULL
)
440 encoding
= "ISO-8859-1";
442 if (language
!= NULL
) {
444 for (i
= 0; langmap
[i
].language
!= NULL
; i
++) {
445 if (g_str_equal (language
, langmap
[i
].language
)) {
446 encoding
= langmap
[i
].encoding
;
452 parser
= yelp_man_parser_new ();
453 priv
->xmldoc
= yelp_man_parser_parse_file (parser
, filepath
, &error
);
454 yelp_man_parser_free (parser
);
456 if (priv
->xmldoc
== NULL
) {
457 yelp_document_error_pending ((YelpDocument
*) man
, error
);
460 g_mutex_lock (priv
->mutex
);
461 if (priv
->state
== MAN_STATE_STOP
) {
462 g_mutex_unlock (priv
->mutex
);
466 priv
->transform
= yelp_transform_new (STYLESHEET
);
468 g_signal_connect (priv
->transform
, "chunk-ready",
469 (GCallback
) transform_chunk_ready
,
472 g_signal_connect (priv
->transform
, "finished",
473 (GCallback
) transform_finished
,
476 g_signal_connect (priv
->transform
, "error",
477 (GCallback
) transform_error
,
480 params
= yelp_settings_get_all_params (yelp_settings_get_default (), 0, ¶ms_i
);
482 priv
->transform_running
= TRUE
;
483 yelp_transform_start (priv
->transform
,
486 (const gchar
* const *) params
);
488 g_mutex_unlock (priv
->mutex
);
492 priv
->process_running
= FALSE
;
493 g_object_unref (man
);
497 man_document_disconnect (YelpManDocument
*man
)
499 YelpManDocumentPrivate
*priv
= GET_PRIV (man
);
500 if (priv
->chunk_ready
) {
501 g_signal_handler_disconnect (priv
->transform
, priv
->chunk_ready
);
502 priv
->chunk_ready
= 0;
504 if (priv
->finished
) {
505 g_signal_handler_disconnect (priv
->transform
, priv
->finished
);
509 g_signal_handler_disconnect (priv
->transform
, priv
->error
);
512 yelp_transform_cancel (priv
->transform
);
513 g_object_unref (priv
->transform
);
514 priv
->transform
= NULL
;
515 priv
->transform_running
= FALSE
;