summaryrefslogtreecommitdiff
path: root/src/yelp-man.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/yelp-man.c')
-rw-r--r--src/yelp-man.c481
1 files changed, 481 insertions, 0 deletions
diff --git a/src/yelp-man.c b/src/yelp-man.c
new file mode 100644
index 00000000..2b8d1084
--- /dev/null
+++ b/src/yelp-man.c
@@ -0,0 +1,481 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2007 Shaun McCance <shaunm@gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance <shaunm@gnome.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <libxml/tree.h>
+
+#include "yelp-error.h"
+#include "yelp-man.h"
+#include "yelp-man-parser.h"
+#include "yelp-transform.h"
+#include "yelp-debug.h"
+
+#define STYLESHEET DATADIR"/yelp/xslt/man2html.xsl"
+
+#define YELP_MAN_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_MAN, YelpManPriv))
+
+typedef enum {
+ MAN_STATE_BLANK, /* Brand new, run transform as needed */
+ MAN_STATE_PARSING, /* Parsing/transforming document, please wait */
+ MAN_STATE_PARSED, /* All done, if we ain't got it, it ain't here */
+ MAN_STATE_STOP /* Stop everything now, object to be disposed */
+} ManState;
+
+struct _YelpManPriv {
+ gchar *filename;
+ ManState state;
+
+ GMutex *mutex;
+ GThread *thread;
+
+ xmlDocPtr xmldoc;
+
+ gboolean process_running;
+ gboolean transform_running;
+
+ YelpTransform *transform;
+};
+
+typedef struct _YelpLangEncodings YelpLangEncodings;
+struct _YelpLangEncodings {
+ gchar *language;
+ gchar *encoding;
+};
+/* http://www.w3.org/International/O-charset-lang.html */
+static const YelpLangEncodings langmap[] = {
+ { "C", "ISO-8859-1" },
+ { "af", "ISO-8859-1" },
+ { "ar", "ISO-8859-6" },
+ { "bg", "ISO-8859-5" },
+ { "be", "ISO-8859-5" },
+ { "ca", "ISO-8859-1" },
+ { "cs", "ISO-8859-2" },
+ { "da", "ISO-8859-1" },
+ { "de", "ISO-8859-1" },
+ { "el", "ISO-8859-7" },
+ { "en", "ISO-8859-1" },
+ { "eo", "ISO-8859-3" },
+ { "es", "ISO-8859-1" },
+ { "et", "ISO-8859-15" },
+ { "eu", "ISO-8859-1" },
+ { "fi", "ISO-8859-1" },
+ { "fo", "ISO-8859-1" },
+ { "fr", "ISO-8859-1" },
+ { "ga", "ISO-8859-1" },
+ { "gd", "ISO-8859-1" },
+ { "gl", "ISO-8859-1" },
+ { "hu", "ISO-8859-2" },
+ { "id", "ISO-8859-1" }, /* is this right */
+ { "mt", "ISO-8859-3" },
+ { "is", "ISO-8859-1" },
+ { "it", "ISO-8859-1" },
+ { "iw", "ISO-8859-8" },
+ { "ja", "EUC-JP" },
+ { "ko", "EUC-KR" },
+ { "lt", "ISO-8859-13" },
+ { "lv", "ISO-8859-13" },
+ { "mk", "ISO-8859-5" },
+ { "mt", "ISO-8859-3" },
+ { "no", "ISO-8859-1" },
+ { "pl", "ISO-8859-2" },
+ { "pt_BR", "ISO-8859-1" },
+ { "ro", "ISO-8859-2" },
+ { "ru", "KOI8-R" },
+ { "sl", "ISO-8859-2" },
+ { "sr", "ISO-8859-2" }, /* Latin, not cyrillic */
+ { "sk", "ISO-8859-2" },
+ { "sv", "ISO-8859-1" },
+ { "tr", "ISO-8859-9" },
+ { "uk", "ISO-8859-5" },
+ { "zh_CN", "BIG5" },
+ { "zh_TW", "BIG5" },
+ { NULL, NULL },
+};
+
+static void man_class_init (YelpManClass *klass);
+static void man_init (YelpMan *man);
+static void man_try_dispose (GObject *object);
+static void man_dispose (GObject *object);
+
+/* YelpDocument */
+static void man_request (YelpDocument *document,
+ gint req_id,
+ gboolean handled,
+ gchar *page_id,
+ YelpDocumentFunc func,
+ gpointer user_data);
+
+/* YelpTransform */
+static void transform_func (YelpTransform *transform,
+ YelpTransformSignal signal,
+ gpointer func_data,
+ YelpMan *man);
+static void transform_page_func (YelpTransform *transform,
+ gchar *page_id,
+ YelpMan *man);
+static void transform_final_func (YelpTransform *transform,
+ YelpMan *man);
+
+/* Threaded */
+static void man_process (YelpMan *man);
+
+static YelpDocumentClass *parent_class;
+
+GType
+yelp_man_get_type (void)
+{
+ static GType type = 0;
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (YelpManClass),
+ NULL, NULL,
+ (GClassInitFunc) man_class_init,
+ NULL, NULL,
+ sizeof (YelpMan),
+ 0,
+ (GInstanceInitFunc) man_init,
+ };
+ type = g_type_register_static (YELP_TYPE_DOCUMENT,
+ "YelpMan",
+ &info, 0);
+ }
+ return type;
+}
+
+static void
+man_class_init (YelpManClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->dispose = man_try_dispose;
+
+ document_class->request = man_request;
+ document_class->cancel = NULL;
+
+ g_type_class_add_private (klass, sizeof (YelpManPriv));
+}
+
+static void
+man_init (YelpMan *man)
+{
+ YelpManPriv *priv;
+
+ priv = man->priv = YELP_MAN_GET_PRIVATE (man);
+
+ priv->state = MAN_STATE_BLANK;
+
+ priv->mutex = g_mutex_new ();
+}
+
+static void
+man_try_dispose (GObject *object)
+{
+ YelpManPriv *priv;
+
+ g_assert (object != NULL && YELP_IS_MAN (object));
+ priv = YELP_MAN (object)->priv;
+
+ g_mutex_lock (priv->mutex);
+ if (priv->process_running || priv->transform_running) {
+ priv->state = MAN_STATE_STOP;
+ g_idle_add ((GSourceFunc) man_try_dispose, object);
+ g_mutex_unlock (priv->mutex);
+ } else {
+ g_mutex_unlock (priv->mutex);
+ man_dispose (object);
+ }
+}
+
+static void
+man_dispose (GObject *object)
+{
+ YelpMan *man = YELP_MAN (object);
+
+ g_free (man->priv->filename);
+
+ if (man->priv->xmldoc)
+ xmlFreeDoc (man->priv->xmldoc);
+
+ g_mutex_free (man->priv->mutex);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+/******************************************************************************/
+
+YelpDocument *
+yelp_man_new (gchar *filename)
+{
+ YelpMan *man;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ man = (YelpMan *) g_object_new (YELP_TYPE_MAN, NULL);
+ man->priv->filename = g_strdup (filename);
+
+ debug_print (DB_FUNCTION, "entering\n");
+ debug_print (DB_ARG, " filename = \"%s\"\n", filename);
+
+ yelp_document_add_page_id (YELP_DOCUMENT (man), "x-yelp-index", "index");
+
+ return (YelpDocument *) man;
+}
+
+
+/******************************************************************************/
+/** YelpDocument **************************************************************/
+
+static void
+man_request (YelpDocument *document,
+ gint req_id,
+ gboolean handled,
+ gchar *page_id,
+ YelpDocumentFunc func,
+ gpointer user_data)
+{
+ YelpMan *man;
+ YelpManPriv *priv;
+ YelpError *error;
+
+ debug_print (DB_FUNCTION, "entering\n");
+ debug_print (DB_ARG, " req_id = %i\n", req_id);
+ debug_print (DB_ARG, " page_id = \"%s\"\n", page_id);
+
+ g_assert (document != NULL && YELP_IS_MAN (document));
+
+ if (handled)
+ return;
+
+ man = YELP_MAN (document);
+ priv = man->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ switch (priv->state) {
+ case MAN_STATE_BLANK:
+ priv->state = MAN_STATE_PARSING;
+ priv->process_running = TRUE;
+ priv->thread = g_thread_create ((GThreadFunc) man_process, man, FALSE, NULL);
+ break;
+ case MAN_STATE_PARSING:
+ break;
+ case MAN_STATE_PARSED:
+ case MAN_STATE_STOP:
+ error = yelp_error_new (_("Page not found"),
+ _("The page %s was not found in the document %s."),
+ page_id, priv->filename);
+ yelp_document_error_request (document, req_id, error);
+ break;
+ }
+
+ g_mutex_unlock (priv->mutex);
+}
+
+
+/******************************************************************************/
+/** YelpTransform *************************************************************/
+
+static void
+transform_func (YelpTransform *transform,
+ YelpTransformSignal signal,
+ gpointer func_data,
+ YelpMan *man)
+{
+ YelpManPriv *priv;
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ g_assert (man != NULL && YELP_IS_MAN (man));
+
+ priv = man->priv;
+
+ g_assert (transform == priv->transform);
+
+ if (priv->state == MAN_STATE_STOP) {
+ switch (signal) {
+ case YELP_TRANSFORM_CHUNK:
+ g_free (func_data);
+ break;
+ case YELP_TRANSFORM_ERROR:
+ yelp_error_free ((YelpError *) func_data);
+ break;
+ case YELP_TRANSFORM_FINAL:
+ break;
+ }
+ yelp_transform_release (transform);
+ priv->transform = NULL;
+ priv->transform_running = FALSE;
+ return;
+ }
+
+ switch (signal) {
+ case YELP_TRANSFORM_CHUNK:
+ transform_page_func (transform, (gchar *) func_data, man);
+ break;
+ case YELP_TRANSFORM_ERROR:
+ yelp_document_error_pending (YELP_DOCUMENT (man), (YelpError *) func_data);
+ yelp_transform_release (transform);
+ priv->transform = NULL;
+ priv->transform_running = FALSE;
+ break;
+ case YELP_TRANSFORM_FINAL:
+ transform_final_func (transform, man);
+ break;
+ }
+}
+
+static void
+transform_page_func (YelpTransform *transform,
+ gchar *page_id,
+ YelpMan *man)
+{
+ YelpManPriv *priv;
+ gchar *content;
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ priv = man->priv;
+ g_mutex_lock (priv->mutex);
+
+ content = yelp_transform_eat_chunk (transform, page_id);
+
+ yelp_document_add_page (YELP_DOCUMENT (man), page_id, content);
+
+ g_free (page_id);
+
+ g_mutex_unlock (priv->mutex);
+}
+
+static void
+transform_final_func (YelpTransform *transform, YelpMan *man)
+{
+ YelpError *error;
+ YelpManPriv *priv = man->priv;
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ g_mutex_lock (priv->mutex);
+
+ error = yelp_error_new (_("Page not found"),
+ _("The requested page was not found in the document %s."),
+ priv->filename);
+ yelp_document_error_pending (YELP_DOCUMENT (man), error);
+
+ yelp_transform_release (transform);
+ priv->transform = NULL;
+ priv->transform_running = FALSE;
+
+ if (priv->xmldoc)
+ xmlFreeDoc (priv->xmldoc);
+ priv->xmldoc = NULL;
+
+ g_mutex_unlock (priv->mutex);
+}
+
+
+/******************************************************************************/
+/** Threaded ******************************************************************/
+
+static void
+man_process (YelpMan *man)
+{
+ YelpManPriv *priv;
+ const gchar *language;
+ const gchar *encoding;
+ YelpManParser *parser;
+ YelpError *error = NULL;
+ YelpDocument *document;
+ gint i;
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ g_assert (man != NULL && YELP_IS_MAN (man));
+ g_object_ref (man);
+ priv = man->priv;
+ document = YELP_DOCUMENT (man);
+
+ if (!g_file_test (priv->filename, G_FILE_TEST_IS_REGULAR)) {
+ error = yelp_error_new (_("File not found"),
+ _("The file ā€˜%sā€™ does not exist."),
+ priv->filename);
+ yelp_document_error_pending (document, error);
+ goto done;
+ }
+
+ /* FIXME: get the language */
+ language = "C";
+
+ /* default encoding if the language doesn't match below */
+ encoding = g_getenv("MAN_ENCODING");
+ if (encoding == NULL)
+ encoding = "ISO-8859-1";
+
+ if (language != NULL) {
+ for (i = 0; langmap[i].language != NULL; i++) {
+ if (g_str_equal (language, langmap[i].language)) {
+ encoding = langmap[i].encoding;
+ break;
+ }
+ }
+ }
+
+ parser = yelp_man_parser_new ();
+ priv->xmldoc = yelp_man_parser_parse_file (parser, priv->filename, encoding);
+ yelp_man_parser_free (parser);
+
+ if (priv->xmldoc == NULL) {
+ error = yelp_error_new (_("Could not parse file"),
+ _("The file ā€˜%sā€™ could not be parsed because it is"
+ " not a well-formed man page."),
+ priv->filename);
+ yelp_document_error_pending (document, error);
+ }
+
+ g_mutex_lock (priv->mutex);
+ if (priv->state == MAN_STATE_STOP) {
+ g_mutex_unlock (priv->mutex);
+ goto done;
+ }
+
+ priv->transform = yelp_transform_new (STYLESHEET,
+ (YelpTransformFunc) transform_func,
+ man);
+ priv->transform_running = TRUE;
+ /* FIXME: we probably need to set our own params */
+ yelp_transform_start (priv->transform,
+ priv->xmldoc,
+ NULL);
+ g_mutex_unlock (priv->mutex);
+
+ done:
+ priv->process_running = FALSE;
+ g_object_unref (man);
+}