summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaun McCance <shaunm@src.gnome.org>2007-04-22 20:09:11 +0000
committerShaun McCance <shaunm@src.gnome.org>2007-04-22 20:09:11 +0000
commit3390d20854154de304273e1d9e9320b8f38ec327 (patch)
tree5e066d55caac1e05030a4b2770be6c7249c369e5
parent388b2c3e65782f31d5c760aeb160eb560f929dfc (diff)
downloadyelp-3390d20854154de304273e1d9e9320b8f38ec327.tar.gz
ChangeLog
* ChangeLog * src/Makefile.am: * src/test-man-parser.c: * src/yelp-debug.h: * src/yelp-error.c: * src/yelp-error.h: * src/yelp-io-channel.c: * src/yelp-man-parser.c: * src/yelp-man-parser.h: * src/yelp-utils.c: * src/yelp-utils.h: - Merging yelp-document into yelp-spoon svn path=/branches/yelp-spoon/; revision=2808
-rw-r--r--ChangeLog219
-rw-r--r--src/Makefile.am93
-rw-r--r--src/test-document.c130
-rw-r--r--src/test-man-parser.c49
-rw-r--r--src/test-page.c83
-rw-r--r--src/test-transform.c163
-rw-r--r--src/yelp-debug.h3
-rw-r--r--src/yelp-docbook.c763
-rw-r--r--src/yelp-docbook.h53
-rw-r--r--src/yelp-document.c782
-rw-r--r--src/yelp-document.h126
-rw-r--r--src/yelp-error.c120
-rw-r--r--src/yelp-error.h38
-rw-r--r--src/yelp-io-channel.c2
-rw-r--r--src/yelp-man-parser.c45
-rw-r--r--src/yelp-man-parser.h4
-rw-r--r--src/yelp-man.c481
-rw-r--r--src/yelp-man.h53
-rw-r--r--src/yelp-page.c147
-rw-r--r--src/yelp-page.h87
-rw-r--r--src/yelp-transform.c416
-rw-r--r--src/yelp-transform.h81
-rw-r--r--src/yelp-utils.c17
-rw-r--r--src/yelp-utils.h14
24 files changed, 3789 insertions, 180 deletions
diff --git a/ChangeLog b/ChangeLog
index 269778f0..46e55a50 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2007-04-22 Shaun McCance <shaunm@gnome.org>
+
+ * ChangeLog
+ * src/Makefile.am:
+ * src/test-man-parser.c:
+ * src/yelp-debug.h:
+ * src/yelp-error.c:
+ * src/yelp-error.h:
+ * src/yelp-io-channel.c:
+ * src/yelp-man-parser.c:
+ * src/yelp-man-parser.h:
+ * src/yelp-utils.c:
+ * src/yelp-utils.h:
+ - Merging yelp-document into yelp-spoon
+
2007-04-14 Don Scorgie <dscorgie@cvs.gnome.org>
* src/yelp-toc-pager.c:
@@ -18,7 +33,6 @@
Search for pkg-config Spoon file
Run a spoon-for-each, printing doc names
-==================== 2.16.2 ====================
2007-03-12 Shaun McCance <shaunm@gnome.org>
* configure.in:
@@ -39,7 +53,6 @@
* src/Yelper.cpp:
Fix for mozilla API change on trunk.
-==================== 2.16.2 ====================
2006-11-20 Don Scorgie <dscorgie@cvs.gnome.org>
* NEWS:
@@ -344,6 +357,208 @@
* src/yelp-window.c:
Expand sections on double-click (bug #346871)
+=======
+2007-04-12 Shaun McCance <shaunm@gnome.org>
+
+ * src/Makefile.am:
+ * src/yelp-man.c:
+ * src/yelp-man.h:
+ * src/test-document.c:
+ - Made man pages work with YelpDocument
+
+ * src/yelp-docbook.c:
+ * src/yelp-document.c:
+ - General clean up
+
+2007-04-12 Shaun McCance <shaunm@gnome.org>
+
+ * src/yelp-document.c:
+ * src/yelp-page.c:
+ * src/yelp-page.h:
+ - Added preliminary support for mime types in yelp-page
+
+2007-04-12 Shaun McCance <shaunm@gnome.org>
+
+ * src/yelp-man-parser.c:
+ * src/Makefile.am:
+ - Made yelp-man-parser use yelp-debug
+
+2007-04-11 Shaun McCance <shaunm@gnome.org>
+
+ * src/yelp-error.c:
+ * src/yelp-error.h:
+ * src/yelp-io-channel.c:
+ - Added back the GError convenience functions
+
+ * src/Makefile.am:
+ * src/test-man-parser.c:
+ * src/yelp-man-parser.c:
+ * src/yelp-man-parser.h:
+ - Getting the man stuff to work in the new world order
+
+ * src/yelp-transform.c:
+ - Added a cast to shut gcc up
+
+ * src/test-document.c:
+ * src/yelp-document.h:
+ * src/yelp-docbook.c:
+ * src/yelp-docbook.h:
+ * src/yelp-page.c:
+ * src/yelp-page.h:
+ - Moving much of the request-handling to YelpDocument
+
+2007-04-04 Shaun McCance <shaunm@gnome.org>
+
+ * src/yelp-error.c:
+ * src/yelp-error.h:
+ - Added yelp_error_copy
+
+ * src/yelp-transform.c:
+ - Added a few debug statements
+
+ * src/test-transform.c:
+ - Added the --random-timeout option
+
+ * src/yelp-page.c:
+ - Don't use g_free with memory slicese.
+
+ * src/yelp-docbook.c:
+ * src/yelp-docbook.h:
+ * src/yelp-document.c:
+ * src/yelp-document.h:
+ - Added a more-or-less complete YelpDocument implementation for DocBook
+
+ * src/test-document.c:
+ * src/Makefile.am:
+ - Added a test program for YelpDocument, currently only using DocBook
+
+2007-03-22 Shaun McCance <shaunm@gnome.org>
+
+ * src/test-transform.c:
+ * src/yelp-transform.c:
+ * src/yelp-transform.h:
+ - Do not do callbacks if released
+
+ * src/yelp-page.h:
+ - Added a comment
+
+ * src/yelp-document.c:
+ * src/yelp-document.h:
+ - Filled out the abstract functions
+
+ * src/Makefile.am:
+ - Some changes to the test programs we build
+
+2007-03-20 Shaun McCance <shaunm@gnome.org>
+
+ * src/yelp-transform.c:
+ - Added some locks
+
+ * src/Makefile.am:
+ * src/test-page.c:
+ - Adding a test application for YelpPage
+
+ * src/yelp-page.c:
+ * src/yelp-page.h:
+ - Fairly complete YelpPage for strings
+
+ * src/yelp-document.c:
+ * src/yelp-document.h:
+ - Big not-yet-done changes, but needed for test-page
+
+2007-03-20 Shaun McCance <shaunm@gnome.org>
+
+ * src/yelp-transform.c:
+ - Fixed concurrency issues when freeing
+
+ * src/test-transform.c:
+ - Using GOption to get a --timeout option
+ - Don't release a transform twice
+
+2007-03-20 Shaun McCance <shaunm@gnome.org>
+
+ * src/test-transform.c:
+ * src/yelp-error.c:
+ * src/yelp-error.h:
+ - Making YelpError cleaner and more usable for our purposes
+
+ * src/yelp-transform.c:
+ * src/yelp-transform.h:
+ - Copyright in 2007 too
+
+ * src/yelp-page.c:
+ * src/yelp-page.h:
+ - The beginnings of the new read()-able YelpPage
+
+ * src/Makefile.am:
+ - yelp-error.h != yelp-transform.h
+
+2007-03-17 Shaun McCance <shaunm@gnome.org>
+
+ * src/test-transform.c:
+ * src/yelp-transform.c:
+ * src/yelp-transform.h:
+ - Changed the callback API a bit, basically ready
+
+2006-07-03 Brent Smith <gnome@nextreality.net>
+
+ * src/yelp-document.c: (yelp_document_dispose),
+ (yelp_document_finalize), (yelp_document_class_init),
+ (yelp_document_init), (document_set_property),
+ (document_get_property), (yelp_document_get_page),
+ (yelp_document_cancel_get), (yelp_document_get_sections),
+ (yelp_document_add_page):
+ * src/yelp-document.h:
+ * src/yelp-page.c: (yelp_page_new), (yelp_page_set_id),
+ (yelp_page_get_id), (yelp_page_set_title), (yelp_page_get_title),
+ (yelp_page_set_contents), (yelp_page_get_contents),
+ (yelp_page_set_prev_id), (yelp_page_get_prev_id),
+ (yelp_page_set_next_id), (yelp_page_get_next_id),
+ (yelp_page_set_toc_id), (yelp_page_get_toc_id), (yelp_page_free):
+ * src/yelp-page.h:
+ - Add these to the correct directory
+ * yelp-document.c:
+ * yelp-document.h:
+ * yelp-page.c:
+ * yelp-page.h:
+ - Remove these (I put them in the wrong directory in the last commit)
+
+2006-07-03 Brent Smith <gnome@nextreality.net>
+
+ * src/yelp-debug.h: Put an #ifdef around HAVE_CONFIG_H
+ * src/yelp-utils.c:
+ * src/yelp-utils.h:
+ - Get rid of the unused YelpDocPage type and related function(s)
+ - created branch "yelp-document", branchpoint
+ YELP_DOCUMENT_BRANCHPOINT for working on the new YelpDocument API; see
+ http://live.gnome.org/Yelp
+ * yelp-document.c: (yelp_document_dispose),
+ (yelp_document_finalize), (yelp_document_class_init),
+ (yelp_document_init), (document_set_property),
+ (document_get_property), (yelp_document_get_page),
+ (yelp_document_cancel_get), (yelp_document_get_sections),
+ (yelp_document_add_page):
+ * yelp-document.h:
+ * yelp-page.c: (yelp_page_new), (yelp_page_set_id),
+ (yelp_page_get_id), (yelp_page_set_title), (yelp_page_get_title),
+ (yelp_page_set_contents), (yelp_page_get_contents),
+ (yelp_page_set_prev_id), (yelp_page_get_prev_id),
+ (yelp_page_set_next_id), (yelp_page_get_next_id),
+ (yelp_page_set_toc_id), (yelp_page_get_toc_id), (yelp_page_free):
+ * yelp-page.h:
+ New files yelp-document.[ch] and yelp-page.[ch] for the new API
+
+2006-07-03 Shaun McCance <shaunm@gnome.org>
+
+ * src/Makefile.am:
+ * src/test-transform.c:
+ * src/yelp-transform.c:
+ * src/yelp-transform.h:
+ * src/yelp-error.c:
+ * src/yelp-error.h:
+ - Glorious threaded XSLT transformation
+
+>>>>>>> .merge-right.r2807
2006-07-01 Brent Smith <gnome@nextreality.net>
* src/yelp-toc-pager.c: (process_info_pending): Fix to properly escape
diff --git a/src/Makefile.am b/src/Makefile.am
index f8c48782..c312f761 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -102,68 +102,57 @@ yelp_LDADD = \
yelp_LDFLAGS = -R$(MOZILLA_HOME) $(AM_LDFLAGS)
-check_PROGRAMS = test-man-parser test-pager test-uri
+check_PROGRAMS = \
+ test-document \
+ test-man-parser \
+ test-page \
+ test-transform
+
+test_document_SOURCES = \
+ yelp-debug.c yelp-debug.h \
+ yelp-docbook.c yelp-docbook.h \
+ yelp-document.c yelp-document.h \
+ yelp-error.c yelp-error.h \
+ yelp-io-channel.c yelp-io-channel.h \
+ yelp-man.c yelp-man.h \
+ yelp-man-parser.c yelp-man-parser.h \
+ yelp-page.c yelp-page.h \
+ yelp-transform.c yelp-transform.h \
+ test-document.c
+test_document_CFLAGS = $(YELP_CFLAGS) $(AM_CFLAGS) $(YELP_DEFINES)
+test_document_LDADD = $(YELP_LIBS) $(Z_LIBS) $(BZ_LIBS)
+test_document_LDFLAGS = $(AM_LDFLAGS)
test_man_parser_SOURCES = \
+ yelp-debug.c yelp-debug.h \
yelp-error.c yelp-error.h \
yelp-io-channel.c yelp-io-channel.h \
yelp-man-parser.c yelp-man-parser.h \
- yelp-utils.c yelp-utils.h \
test-man-parser.c
-
-test_man_parser_CPPFLAGS = \
- $(YELP_DEFINES) \
- $(AM_CPPFLAGS)
-
-test_man_parser_CFLAGS = \
- $(YELP_CFLAGS) \
- $(AM_CFLAGS)
-
+test_man_parser_CPPFLAGS = $(YELP_DEFINES) $(AM_CPPFLAGS)
+test_man_parser_CFLAGS = $(YELP_CFLAGS) $(AM_CFLAGS)
test_man_parser_LDADD = $(YELP_LIBS) $(Z_LIBS) $(BZ_LIBS)
-
test_man_parser_LDFLAGS = $(AM_LDFLAGS)
-test_pager_SOURCES = \
- yelp-db-pager.c yelp-db-pager.h \
- yelp-db-print-pager.c yelp-db-print-pager.h \
- yelp-error.c yelp-error.h \
- yelp-io-channel.c yelp-io-channel.h \
- yelp-man-pager.c yelp-man-pager.h \
- yelp-man-parser.c yelp-man-parser.h \
- yelp-pager.c yelp-pager.h \
- yelp-toc-pager.c yelp-toc-pager.h \
- yelp-utils.c yelp-utils.h \
- yelp-xslt-pager.c yelp-xslt-pager.h \
- yelp-marshal.c yelp-marshal.h \
- test-pager.c
-
-test_pager_CPPFLAGS = \
- $(YELP_DEFINES) \
- $(AM_CPPFLAGS)
-
-test_pager_CFLAGS = \
- $(YELP_CFLAGS) \
- $(AM_CFLAGS)
-
-test_pager_LDADD = $(YELP_LIBS) $(POPT_LIBS) $(Z_LIBS) $(BZ_LIBS)
-
-test_pager_LDFLAGS = $(AM_LDFLAGS)
-
-test_uri_SOURCES = \
- yelp-utils.c yelp-utils.h \
- test-uri.c
-
-test_uri_CPPFLAGS = \
- $(YELP_DEFINES) \
- $(AM_CPPFLAGS)
-
-test_uri_CFLAGS = \
- $(YELP_CFLAGS) \
- $(AM_CFLAGS)
-
-test_uri_LDADD = $(YELP_LIBS)
+test_page_SOURCES = \
+ yelp-debug.c yelp-debug.h \
+ yelp-document.c yelp-document.h \
+ yelp-error.c yelp-error.h \
+ yelp-page.c yelp-page.h \
+ test-page.c
+test_page_CFLAGS = $(YELP_CFLAGS) $(AM_CFLAGS) $(YELP_DEFINES)
+test_page_LDADD = $(YELP_LIBS)
+test_page_LDFLAGS = $(AM_LDFLAGS)
+
+test_transform_SOURCES = \
+ yelp-debug.c yelp-debug.h \
+ yelp-error.c yelp-error.h \
+ yelp-transform.c yelp-transform.h \
+ test-transform.c
+test_transform_CFLAGS = $(YELP_CFLAGS) $(AM_CFLAGS) $(YELP_DEFINES)
+test_transform_LDADD = $(YELP_LIBS)
+test_transform_LDFLAGS = $(AM_LDFLAGS)
-test_uri_LDFLAGS = $(AM_LDFLAGS)
@INTLTOOL_SERVER_RULE@
diff --git a/src/test-document.c b/src/test-document.c
new file mode 100644
index 00000000..2bd17c65
--- /dev/null
+++ b/src/test-document.c
@@ -0,0 +1,130 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2006 Shaun McCance
+ *
+ * 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, Cambridge, MA 02139, USA.
+ *
+ * Author: Shaun McCance <shaunm@gnome.org>
+ */
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <libxml/parser.h>
+#include <libxml/xinclude.h>
+
+#include "yelp-error.h"
+#include "yelp-docbook.h"
+
+static gchar *mode = NULL;
+static gchar **files = NULL;
+static const GOptionEntry options[] = {
+ { "mode", 'm',
+ 0, G_OPTION_ARG_STRING,
+ &mode,
+ "One of man or docbook", "MODE" },
+ { G_OPTION_REMAINING,
+ 0, 0, G_OPTION_ARG_FILENAME_ARRAY,
+ &files, NULL, NULL },
+ { NULL }
+};
+
+static void document_func (YelpDocument *document,
+ YelpDocumentSignal signal,
+ gint req_id,
+ gpointer *func_data,
+ gpointer user_data);
+
+GMainLoop *loop;
+
+static void
+document_func (YelpDocument *document,
+ YelpDocumentSignal signal,
+ gint req_id,
+ gpointer *func_data,
+ gpointer user_data)
+{
+ gchar contents[60];
+ gchar *contents_;
+ gsize read;
+ YelpPage *page;
+ YelpError *error;
+ switch (signal) {
+ case YELP_DOCUMENT_SIGNAL_PAGE:
+ page = (YelpPage *) func_data;
+ printf ("PAGE: %s (%i)\n", page->id, req_id);
+ printf (" PREV: %s\n", page->prev_id);
+ printf (" NEXT: %s\n", page->next_id);
+ printf (" UP: %s\n", page->up_id);
+ printf (" ROOT: %s\n", page->root_id);
+ yelp_page_read (page, contents, 60, &read, NULL);
+ /* contents isn't \0-terminated */
+ contents_ = g_strndup (contents, read);
+ printf (" DATA: %s\n", contents_);
+ g_free (contents_);
+ yelp_page_free (page);
+ break;
+ case YELP_DOCUMENT_SIGNAL_TITLE:
+ printf ("TITLE: %s (%i)\n", (gchar *) func_data, req_id);
+ g_free (func_data);
+ break;
+ case YELP_DOCUMENT_SIGNAL_ERROR:
+ error = (YelpError *) func_data;
+ printf ("ERROR: %s\n", yelp_error_get_title (error));
+ printf (" %s\n", yelp_error_get_message (error));
+ yelp_error_free (error);
+ break;
+ }
+}
+
+gint
+main (gint argc, gchar **argv)
+{
+ GOptionContext *context;
+ YelpDocument *document;
+ gint i;
+
+ g_thread_init (NULL);
+ gdk_threads_init ();
+ gdk_threads_leave ();
+
+ gtk_init (&argc, &argv);
+
+ context = g_option_context_new ("FILE PAGE_IDS...");
+ g_option_context_add_main_entries (context, options, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
+
+ if (files == NULL || files[0] == NULL) {
+ g_printerr ("Usage: test-docbook FILE PAGE_IDS...\n");
+ return 1;
+ }
+
+ if (g_str_equal (mode, "man"))
+ document = yelp_man_new (files[0]);
+ else
+ document = yelp_docbook_new (files[0]);
+
+ if (files[1] == NULL)
+ yelp_document_get_page (document, "x-yelp-index", (YelpDocumentFunc) document_func, NULL);
+ else
+ for (i = 1; files[i]; i++)
+ yelp_document_get_page (document, files[i], (YelpDocumentFunc) document_func, NULL);
+
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+
+ gdk_threads_leave ();
+
+ return 0;
+}
diff --git a/src/test-man-parser.c b/src/test-man-parser.c
index 3b115430..38b38497 100644
--- a/src/test-man-parser.c
+++ b/src/test-man-parser.c
@@ -19,39 +19,50 @@
* Author: Shaun McCance <shaunm@gnome.org>
*/
-#include <libgnomevfs/gnome-vfs.h>
+#include <glib.h>
+#include <libxml/tree.h>
+
#include "yelp-man-parser.h"
-#include "yelp-utils.h"
+
+static gchar **files = NULL;
+static const GOptionEntry options[] = {
+ { G_OPTION_REMAINING,
+ 0, 0, G_OPTION_ARG_FILENAME_ARRAY,
+ &files, NULL, NULL },
+ { NULL }
+};
gint
main (gint argc, gchar **argv)
{
+ GOptionContext *context;
YelpManParser *parser;
- YelpDocInfo *doc_info;
- xmlDocPtr doc;
+ xmlDocPtr doc;
+ gchar *encoding;
+ gint i;
+
+ context = g_option_context_new ("FILES...");
+ g_option_context_add_main_entries (context, options, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
- if (argc < 2) {
- g_error ("Usage: test-man-parser file\n");
+ if (files == NULL || files[0] == NULL) {
+ g_printerr ("Usage: test-man-parser [OPTION...] FILES...\n");
return 1;
}
- gnome_vfs_init ();
-
parser = yelp_man_parser_new ();
- doc_info = yelp_doc_info_get (argv[1], FALSE);
- if (!doc_info) {
- printf ("Failed to load URI: %s\n", argv[1]);
- return -1;
- }
- doc = yelp_man_parser_parse_doc (parser, doc_info);
- yelp_man_parser_free (parser);
+ encoding = (gchar *) g_getenv("MAN_ENCODING");
+ if (encoding == NULL)
+ encoding = "ISO-8859-1";
- xmlDocDump (stdout, doc);
- xmlFreeDoc (doc);
+ for (i = 0; files[i]; i++) {
+ doc = yelp_man_parser_parse_file (parser, files[i], encoding);
+ xmlDocDump (stdout, doc);
+ xmlFreeDoc (doc);
+ }
- gnome_vfs_shutdown ();
+ yelp_man_parser_free (parser);
return 0;
}
-
diff --git a/src/test-page.c b/src/test-page.c
new file mode 100644
index 00000000..c7d2c701
--- /dev/null
+++ b/src/test-page.c
@@ -0,0 +1,83 @@
+/* -*- 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, Cambridge, MA 02139, USA.
+ *
+ * Author: Shaun McCance <shaunm@gnome.org>
+ */
+
+#include <glib.h>
+
+#include "yelp-page.h"
+#include "yelp-error.h"
+
+#define READ_SIZE 1024
+
+static gchar *file = NULL;
+static const GOptionEntry options[] = {
+ { G_OPTION_REMAINING,
+ 0, 0, G_OPTION_ARG_FILENAME,
+ &file, NULL, NULL },
+ { NULL }
+};
+
+gint
+main (gint argc, gchar **argv)
+{
+ GOptionContext *context;
+ YelpPage *page;
+ YelpError *error;
+ gchar buffer[READ_SIZE];
+ gsize read;
+ gint num_reads;
+
+ context = g_option_context_new ("[FILE]");
+ g_option_context_add_main_entries (context, options, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
+
+ if (file == NULL) {
+ gint i;
+ gchar *str, *new;
+ str = g_strdup ("Fe fi fo fum. I smell the blood of an Englishman.\n");
+ for (i = 0; i < 5; i++) {
+ new = g_strconcat (str, str, str, str, str, str, str, NULL);
+ g_free (str);
+ str = new;
+ }
+ page = yelp_page_new_string (NULL, NULL, str);
+ } else {
+ g_error ("File pages not yet supported.\n");
+ return 1;
+ }
+
+ num_reads = 0;
+ while (yelp_page_read (page, buffer, READ_SIZE, &read, &error)
+ == G_IO_STATUS_NORMAL) {
+ gchar *str;
+ num_reads++;
+ /* buffer isn't NULL-terminated */
+ str = g_strndup (buffer, READ_SIZE);
+ printf ("%s", str);
+ g_free (str);
+ }
+
+ printf ("\n%i total reads\n", num_reads);
+ printf ("%i bytes in last read\n", read);
+
+ yelp_page_free (page);
+
+ return 0;
+}
diff --git a/src/test-transform.c b/src/test-transform.c
new file mode 100644
index 00000000..ae659be0
--- /dev/null
+++ b/src/test-transform.c
@@ -0,0 +1,163 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2006 Shaun McCance
+ *
+ * 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, Cambridge, MA 02139, USA.
+ *
+ * Author: Shaun McCance <shaunm@gnome.org>
+ */
+
+#include <glib.h>
+#include <libxml/parser.h>
+#include <libxml/xinclude.h>
+
+#include "yelp-error.h"
+#include "yelp-transform.h"
+
+static gint timeout = -1;
+static gboolean random_timeout = FALSE;
+static gchar **files = NULL;
+static const GOptionEntry options[] = {
+ { "random-timeout", 'r',
+ 0, G_OPTION_ARG_NONE,
+ &random_timeout,
+ "Time out after a random amount of time", NULL },
+ { "timeout", 't',
+ 0, G_OPTION_ARG_INT,
+ &timeout,
+ "Time out after N milliseconds", "N" },
+ { G_OPTION_REMAINING,
+ 0, 0, G_OPTION_ARG_FILENAME_ARRAY,
+ &files, NULL, NULL },
+ { NULL }
+};
+
+GMainLoop *loop;
+
+static void
+transform_release (YelpTransform *transform)
+{
+ printf ("\nRELEASE\n");
+ yelp_transform_release (transform);
+ /* Quit after pending things are done. This helps test
+ * whether YelpTransform takes its release seriously.
+ */
+ g_idle_add ((GSourceFunc) g_main_loop_quit, loop);
+}
+
+static void
+transform_func (YelpTransform *transform,
+ YelpTransformSignal signal,
+ gpointer *func_data,
+ gpointer user_data)
+{
+ gchar *chunk_id;
+ gchar *chunk_data;
+ gchar *chunk_short;
+ YelpError *error;
+
+ switch (signal) {
+ case YELP_TRANSFORM_CHUNK:
+ chunk_id = (gchar *) func_data;
+ printf ("\nCHUNK: %s\n", chunk_id);
+
+ chunk_data = yelp_transform_eat_chunk (transform, chunk_id);
+ chunk_short = g_strndup (chunk_data, 300);
+ printf ("%s\n", chunk_short);
+
+ g_free (chunk_short);
+ g_free (chunk_data);
+ g_free (chunk_id);
+ break;
+ case YELP_TRANSFORM_ERROR:
+ error = (YelpError *) func_data;
+ printf ("\nERROR: %s\n", yelp_error_get_title (error));
+ yelp_error_free (error);
+ break;
+ case YELP_TRANSFORM_FINAL:
+ printf ("\nFINAL\n");
+ transform_release (transform);
+ break;
+ }
+}
+
+gint
+main (gint argc, gchar **argv)
+{
+ GOptionContext *context;
+ xmlParserCtxtPtr parser;
+ xmlDocPtr doc;
+ YelpTransform *transform;
+ gchar **params;
+ gchar *stylesheet;
+ gchar *file;
+
+ g_thread_init (NULL);
+
+ context = g_option_context_new ("[STYLESHEET] FILE");
+ g_option_context_add_main_entries (context, options, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
+
+ if (files == NULL || files[0] == NULL) {
+ g_printerr ("Usage: test-transform [OPTION...] [STYLESHEET] FILE\n");
+ return 1;
+ }
+
+ if (files[1] == NULL) {
+ stylesheet = DATADIR"/yelp/xslt/db2html.xsl";
+ file = files[0];
+ } else {
+ stylesheet = files[0];
+ file = files[1];
+ }
+
+ params = g_new0 (gchar *, 7);
+ params[0] = "db.chunk.extension";
+ params[1] = "\"\"";
+ params[2] = "db.chunk.info_basename";
+ params[3] = "\"x-yelp-titlepage\"";
+ params[4] = "db.chunk.max_depth";
+ params[5] = "2";
+ params[6] = NULL;
+
+ transform = yelp_transform_new (stylesheet,
+ (YelpTransformFunc) transform_func,
+ NULL);
+ parser = xmlNewParserCtxt ();
+ doc = xmlCtxtReadFile (parser,
+ file,
+ NULL,
+ XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
+ XML_PARSE_NOENT | XML_PARSE_NONET );
+ xmlFreeParserCtxt (parser);
+ xmlXIncludeProcessFlags (doc,
+ XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
+ XML_PARSE_NOENT | XML_PARSE_NONET );
+ yelp_transform_start (transform, doc, params);
+
+ if (random_timeout) {
+ GRand *rand = g_rand_new ();
+ timeout = g_rand_int_range (rand, 80, 280);
+ g_rand_free (rand);
+ }
+
+ if (timeout >= 0)
+ g_timeout_add (timeout, (GSourceFunc) transform_release, transform);
+
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+
+ return 0;
+}
diff --git a/src/yelp-debug.h b/src/yelp-debug.h
index 657859f9..846ad750 100644
--- a/src/yelp-debug.h
+++ b/src/yelp-debug.h
@@ -23,7 +23,10 @@
G_BEGIN_DECLS
+#ifdef HAVE_CONFIG_H
#include <config.h>
+#endif
+
#include <glib.h>
typedef enum {
diff --git a/src/yelp-docbook.c b/src/yelp-docbook.c
new file mode 100644
index 00000000..384c6a40
--- /dev/null
+++ b/src/yelp-docbook.c
@@ -0,0 +1,763 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-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/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xinclude.h>
+
+#include "yelp-error.h"
+#include "yelp-docbook.h"
+#include "yelp-settings.h"
+#include "yelp-transform.h"
+#include "yelp-debug.h"
+
+#define STYLESHEET DATADIR"/yelp/xslt/db2html.xsl"
+
+#define YELP_DOCBOOK_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DOCBOOK, YelpDocbookPriv))
+
+typedef enum {
+ DOCBOOK_STATE_BLANK, /* Brand new, run transform as needed */
+ DOCBOOK_STATE_PARSING, /* Parsing/transforming document, please wait */
+ DOCBOOK_STATE_PARSED, /* All done, if we ain't got it, it ain't here */
+ DOCBOOK_STATE_STOP /* Stop everything now, object to be disposed */
+} DocbookState;
+
+struct _YelpDocbookPriv {
+ gchar *filename;
+ DocbookState state;
+
+ GMutex *mutex;
+ GThread *thread;
+
+ gboolean process_running;
+ gboolean transform_running;
+
+ YelpTransform *transform;
+
+ GtkTreeModel *sections;
+ GtkTreeIter *sections_iter; /* On the stack, do not free */
+
+ xmlDocPtr xmldoc;
+ xmlNodePtr xmlcur;
+ gint max_depth;
+ gint cur_depth;
+ gchar *cur_page_id;
+ gchar *cur_prev_id;
+};
+
+
+static void docbook_class_init (YelpDocbookClass *klass);
+static void docbook_init (YelpDocbook *docbook);
+static void docbook_try_dispose (GObject *object);
+static void docbook_dispose (GObject *object);
+
+/* YelpDocument */
+static void docbook_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,
+ YelpDocbook *docbook);
+static void transform_page_func (YelpTransform *transform,
+ gchar *page_id,
+ YelpDocbook *docbook);
+static void transform_final_func (YelpTransform *transform,
+ YelpDocbook *docbook);
+
+/* Threaded */
+static void docbook_process (YelpDocbook *docbook);
+
+/* Walker */
+static void docbook_walk (YelpDocbook *docbook);
+static gboolean docbook_walk_chunkQ (YelpDocbook *docbook);
+static gboolean docbook_walk_divisionQ (YelpDocbook *docbook);
+static gchar * docbook_walk_get_title (YelpDocbook *docbook);
+
+
+static YelpDocumentClass *parent_class;
+
+GType
+yelp_docbook_get_type (void)
+{
+ static GType type = 0;
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (YelpDocbookClass),
+ NULL, NULL,
+ (GClassInitFunc) docbook_class_init,
+ NULL, NULL,
+ sizeof (YelpDocbook),
+ 0,
+ (GInstanceInitFunc) docbook_init,
+ };
+ type = g_type_register_static (YELP_TYPE_DOCUMENT,
+ "YelpDocbook",
+ &info, 0);
+ }
+ return type;
+}
+
+static void
+docbook_class_init (YelpDocbookClass *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 = docbook_try_dispose;
+
+ document_class->request = docbook_request;
+ document_class->cancel = NULL;
+
+ g_type_class_add_private (klass, sizeof (YelpDocbookPriv));
+}
+
+static void
+docbook_init (YelpDocbook *docbook)
+{
+ YelpDocbookPriv *priv;
+ priv = docbook->priv = YELP_DOCBOOK_GET_PRIVATE (docbook);
+
+ priv->sections = NULL;
+
+ priv->state = DOCBOOK_STATE_BLANK;
+
+ priv->mutex = g_mutex_new ();
+}
+
+static void
+docbook_try_dispose (GObject *object)
+{
+ YelpDocbookPriv *priv;
+
+ g_assert (object != NULL && YELP_IS_DOCBOOK (object));
+
+ priv = YELP_DOCBOOK (object)->priv;
+
+ g_mutex_lock (priv->mutex);
+ if (priv->process_running || priv->transform_running) {
+ priv->state = DOCBOOK_STATE_STOP;
+ g_idle_add ((GSourceFunc) docbook_try_dispose, object);
+ g_mutex_unlock (priv->mutex);
+ } else {
+ g_mutex_unlock (priv->mutex);
+ docbook_dispose (object);
+ }
+}
+
+static void
+docbook_dispose (GObject *object)
+{
+ YelpDocbook *docbook = YELP_DOCBOOK (object);
+
+ g_free (docbook->priv->filename);
+
+ g_object_unref (docbook->priv->sections);
+
+ if (docbook->priv->xmldoc)
+ xmlFreeDoc (docbook->priv->xmldoc);
+
+ g_free (docbook->priv->cur_page_id);
+ g_free (docbook->priv->cur_prev_id);
+
+ g_mutex_free (docbook->priv->mutex);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+/******************************************************************************/
+
+YelpDocument *
+yelp_docbook_new (gchar *filename)
+{
+ YelpDocbook *docbook;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ debug_print (DB_FUNCTION, "entering\n");
+ debug_print (DB_ARG, " filename = \"%s\"\n", filename);
+
+ docbook = (YelpDocbook *) g_object_new (YELP_TYPE_DOCBOOK, NULL);
+ docbook->priv->filename = g_strdup (filename);
+
+ yelp_document_add_page_id (YELP_DOCUMENT (docbook), "x-yelp-titlepage", "x-yelp-titlepage");
+
+ docbook->priv->sections =
+ GTK_TREE_MODEL (gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_STRING));
+
+ return (YelpDocument *) docbook;
+}
+
+
+/******************************************************************************/
+/** YelpDocument **************************************************************/
+
+static void
+docbook_request (YelpDocument *document,
+ gint req_id,
+ gboolean handled,
+ gchar *page_id,
+ YelpDocumentFunc func,
+ gpointer user_data)
+{
+ YelpDocbook *docbook;
+ YelpDocbookPriv *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_DOCBOOK (document));
+
+ if (handled)
+ return;
+
+ docbook = YELP_DOCBOOK (document);
+ priv = docbook->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ switch (priv->state) {
+ case DOCBOOK_STATE_BLANK:
+ priv->state = DOCBOOK_STATE_PARSING;
+ priv->process_running = TRUE;
+ priv->thread = g_thread_create ((GThreadFunc) docbook_process,
+ docbook, FALSE, NULL);
+ break;
+ case DOCBOOK_STATE_PARSING:
+ break;
+ case DOCBOOK_STATE_PARSED:
+ case DOCBOOK_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,
+ YelpDocbook *docbook)
+{
+ YelpDocbookPriv *priv;
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ g_assert (docbook != NULL && YELP_IS_DOCBOOK (docbook));
+
+ priv = docbook->priv;
+
+ g_assert (transform == priv->transform);
+
+ if (priv->state == DOCBOOK_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, docbook);
+ break;
+ case YELP_TRANSFORM_ERROR:
+ yelp_document_error_pending (YELP_DOCUMENT (docbook), (YelpError *) func_data);
+ yelp_transform_release (transform);
+ priv->transform = NULL;
+ priv->transform_running = FALSE;
+ break;
+ case YELP_TRANSFORM_FINAL:
+ transform_final_func (transform, docbook);
+ break;
+ }
+}
+
+static void
+transform_page_func (YelpTransform *transform,
+ gchar *page_id,
+ YelpDocbook *docbook)
+{
+ YelpDocbookPriv *priv;
+ gchar *content;
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ priv = docbook->priv;
+ g_mutex_lock (priv->mutex);
+
+ content = yelp_transform_eat_chunk (transform, page_id);
+
+ yelp_document_add_page (YELP_DOCUMENT (docbook), page_id, content);
+
+ g_free (page_id);
+
+ g_mutex_unlock (priv->mutex);
+}
+
+static void
+transform_final_func (YelpTransform *transform, YelpDocbook *docbook)
+{
+ YelpError *error;
+ YelpDocbookPriv *priv = docbook->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 (docbook), 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
+docbook_process (YelpDocbook *docbook)
+{
+ YelpDocbookPriv *priv;
+ xmlDocPtr xmldoc = NULL;
+ xmlChar *id = NULL;
+ YelpError *error = NULL;
+ xmlParserCtxtPtr parserCtxt = NULL;
+ YelpDocument *document;
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ g_assert (docbook != NULL && YELP_IS_DOCBOOK (docbook));
+ g_object_ref (docbook);
+ priv = docbook->priv;
+ document = YELP_DOCUMENT (docbook);
+
+ 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;
+ }
+
+ parserCtxt = xmlNewParserCtxt ();
+ xmldoc = xmlCtxtReadFile (parserCtxt,
+ (const char *) priv->filename, NULL,
+ XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
+ XML_PARSE_NOENT | XML_PARSE_NONET );
+
+ if (xmldoc == NULL) {
+ error = yelp_error_new (_("Could not parse file"),
+ _("The file ‘%s’ could not be parsed because it is"
+ " not a well-formed XML document."),
+ priv->filename);
+ yelp_document_error_pending (document, error);
+ goto done;
+ }
+
+ if (xmlXIncludeProcessFlags (xmldoc,
+ XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
+ XML_PARSE_NOENT | XML_PARSE_NONET )
+ < 0) {
+ error = yelp_error_new (_("Could not parse file"),
+ _("The file ‘%s’ could not be parsed because"
+ " one or more of its included files is not"
+ " a well-formed XML document."),
+ priv->filename);
+ yelp_document_error_pending (document, error);
+ goto done;
+ }
+
+ g_mutex_lock (priv->mutex);
+ if (!xmlStrcmp (xmlDocGetRootElement (xmldoc)->name, BAD_CAST "book"))
+ priv->max_depth = 2;
+ else
+ priv->max_depth = 1;
+
+ priv->xmldoc = xmldoc;
+ priv->xmlcur = xmlDocGetRootElement (xmldoc);
+
+ id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
+ if (id) {
+ yelp_document_set_root_id (document, (gchar *) id);
+ yelp_document_add_page_id (document, "x-yelp-index", (gchar *) id);
+ yelp_document_add_prev_id (document, (gchar *) id, "x-yelp-titlepage");
+ yelp_document_add_next_id (document, "x-yelp-titlepage", (gchar *) id);
+ }
+ else {
+ yelp_document_set_root_id (document, "x-yelp-index");
+ yelp_document_add_page_id (document, "x-yelp-index", "x-yelp-index");
+ yelp_document_add_prev_id (document, "x-yelp-index", "x-yelp-titlepage");
+ yelp_document_add_next_id (document, "x-yelp-titlepage", "x-yelp-index");
+ /* add the id attribute to the root element with value "index"
+ * so when we try to load the document later, it doesn't fail */
+ xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST "x-yelp-index");
+ }
+ g_mutex_unlock (priv->mutex);
+
+ g_mutex_lock (priv->mutex);
+ if (priv->state == DOCBOOK_STATE_STOP) {
+ g_mutex_unlock (priv->mutex);
+ goto done;
+ }
+
+ docbook_walk (docbook);
+
+ g_mutex_lock (priv->mutex);
+ if (priv->state == DOCBOOK_STATE_STOP) {
+ g_mutex_unlock (priv->mutex);
+ goto done;
+ }
+
+ priv->transform = yelp_transform_new (STYLESHEET,
+ (YelpTransformFunc) transform_func,
+ docbook);
+ 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:
+ if (id)
+ xmlFree (id);
+ if (parserCtxt)
+ xmlFreeParserCtxt (parserCtxt);
+
+ priv->process_running = FALSE;
+ g_object_unref (docbook);
+}
+
+
+/******************************************************************************/
+/** Walker ********************************************************************/
+
+static void
+docbook_walk (YelpDocbook *docbook)
+{
+ static gint autoid = 0;
+ gchar autoidstr[20];
+ xmlChar *id = NULL;
+ xmlChar *title = NULL;
+ gchar *old_page_id = NULL;
+ xmlNodePtr cur, old_cur;
+ GtkTreeIter iter;
+ GtkTreeIter *old_iter = NULL;
+ YelpDocbookPriv *priv = docbook->priv;
+ YelpDocument *document = YELP_DOCUMENT (docbook);
+
+ debug_print (DB_FUNCTION, "entering\n");
+ debug_print (DB_DEBUG, " priv->xmlcur->name: %s\n", priv->xmlcur->name);
+
+ /* check for the yelp:chunk-depth or db.chunk.max_depth processing
+ * instruction and set the max chunk depth accordingly.
+ */
+ if (priv->cur_depth == 0)
+ for (cur = priv->xmlcur; cur; cur = cur->prev)
+ if (cur->type == XML_PI_NODE)
+ if (!xmlStrcmp (cur->name, (const xmlChar *) "yelp:chunk-depth") ||
+ !xmlStrcmp (cur->name, (const xmlChar *) "db.chunk.max_depth")) {
+ gint max = atoi ((gchar *) cur->content);
+ if (max)
+ priv->max_depth = max;
+ break;
+ }
+
+ id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
+
+ if (docbook_walk_chunkQ (docbook)) {
+ title = BAD_CAST docbook_walk_get_title (docbook);
+
+ /* if id attribute is not present, autogenerate a
+ * unique value, and insert it into the in-memory tree */
+ if (!id) {
+ g_snprintf (autoidstr, 20, "_auto-gen-id-%d", ++autoid);
+ xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST autoidstr);
+ id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
+ }
+
+ debug_print (DB_DEBUG, " id: \"%s\"\n", id);
+ debug_print (DB_DEBUG, " title: \"%s\"\n", title);
+
+ yelp_document_add_title (document, (gchar *) id, (gchar *) title);
+
+ gdk_threads_enter ();
+ gtk_tree_store_append (GTK_TREE_STORE (priv->sections),
+ &iter,
+ priv->sections_iter);
+ gtk_tree_store_set (GTK_TREE_STORE (priv->sections),
+ &iter,
+ YELP_DOCUMENT_COLUMN_ID, id,
+ YELP_DOCUMENT_COLUMN_TITLE, title,
+ -1);
+ gdk_threads_leave ();
+
+ if (priv->cur_prev_id) {
+ yelp_document_add_prev_id (document, (gchar *) id, priv->cur_prev_id);
+ yelp_document_add_next_id (document, priv->cur_prev_id, (gchar *) id);
+ g_free (priv->cur_prev_id);
+ }
+ priv->cur_prev_id = g_strdup ((gchar *) id);
+
+ if (priv->cur_page_id)
+ yelp_document_add_up_id (document, (gchar *) id, priv->cur_page_id);
+ old_page_id = priv->cur_page_id;
+ priv->cur_page_id = g_strdup ((gchar *) id);
+
+ old_iter = priv->sections_iter;
+ if (priv->xmlcur->parent->type != XML_DOCUMENT_NODE)
+ priv->sections_iter = &iter;
+ }
+
+ old_cur = priv->xmlcur;
+ priv->cur_depth++;
+
+ if (id)
+ yelp_document_add_page_id (document, (gchar *) id, priv->cur_page_id);
+
+ for (cur = priv->xmlcur->children; cur; cur = cur->next) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ priv->xmlcur = cur;
+ docbook_walk (docbook);
+ }
+ }
+
+ priv->cur_depth--;
+ priv->xmlcur = old_cur;
+
+ if (docbook_walk_chunkQ (docbook)) {
+ priv->sections_iter = old_iter;
+ g_free (priv->cur_page_id);
+ priv->cur_page_id = old_page_id;
+ }
+
+ if (priv->cur_depth == 0) {
+ g_free (priv->cur_prev_id);
+ priv->cur_prev_id = NULL;
+
+ g_free (priv->cur_page_id);
+ priv->cur_page_id = NULL;
+ }
+
+ if (id != NULL)
+ xmlFree (id);
+ if (title != NULL)
+ xmlFree (title);
+}
+
+static gboolean
+docbook_walk_chunkQ (YelpDocbook *docbook)
+{
+ if (docbook->priv->cur_depth <= docbook->priv->max_depth
+ && docbook_walk_divisionQ (docbook))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static gboolean
+docbook_walk_divisionQ (YelpDocbook *docbook)
+{
+ xmlNodePtr node = docbook->priv->xmlcur;
+ return (!xmlStrcmp (node->name, (const xmlChar *) "appendix") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "article") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "book") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "bibliography") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "chapter") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "colophon") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "glossary") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "index") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "part") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "preface") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "reference") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "refentry") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "refsect1") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "refsect2") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "refsect3") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "refsection") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "sect1") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "sect2") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "sect3") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "sect4") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "sect5") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "section") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "set") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "setindex") ||
+ !xmlStrcmp (node->name, (const xmlChar *) "simplesect") );
+}
+
+static gchar *
+docbook_walk_get_title (YelpDocbook *docbook)
+{
+ gchar *infoname = NULL;
+ xmlNodePtr child = NULL;
+ xmlNodePtr title = NULL;
+ xmlNodePtr title_tmp = NULL;
+ YelpDocbookPriv *priv = docbook->priv;
+
+ if (!xmlStrcmp (priv->xmlcur->name, BAD_CAST "refentry")) {
+ /* The title for a refentry element can come from the following:
+ * refmeta/refentrytitle
+ * refentryinfo/title[abbrev]
+ * refnamediv/refname
+ * We take the first one we find.
+ */
+ for (child = priv->xmlcur->children; child; child = child->next) {
+ if (!xmlStrcmp (child->name, BAD_CAST "refmeta")) {
+ for (title = child->children; title; title = title->next) {
+ if (!xmlStrcmp (title->name, BAD_CAST "refentrytitle"))
+ break;
+ }
+ if (title)
+ goto done;
+ }
+ else if (!xmlStrcmp (child->name, BAD_CAST "refentryinfo")) {
+ for (title = child->children; title; title = title->next) {
+ if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev"))
+ break;
+ else if (!xmlStrcmp (title->name, BAD_CAST "title"))
+ title_tmp = title;
+ }
+ if (title)
+ goto done;
+ else if (title_tmp) {
+ title = title_tmp;
+ goto done;
+ }
+ }
+ else if (!xmlStrcmp (child->name, BAD_CAST "refnamediv")) {
+ for (title = child->children; title; title = title->next) {
+ if (!xmlStrcmp (title->name, BAD_CAST "refname"))
+ break;
+ else if (!xmlStrcmp (title->name, BAD_CAST "refpurpose")) {
+ title = NULL;
+ break;
+ }
+ }
+ if (title)
+ goto done;
+ }
+ else if (!xmlStrncmp (child->name, BAD_CAST "refsect", 7))
+ break;
+ }
+ }
+ else {
+ /* The title for other elements appears in the following:
+ * title[abbrev]
+ * *info/title[abbrev]
+ * blockinfo/title[abbrev]
+ * objectinfo/title[abbrev]
+ * We take them in that order.
+ */
+ xmlNodePtr infos[3] = {NULL, NULL, NULL};
+ int i;
+
+ infoname = g_strdup_printf ("%sinfo", priv->xmlcur->name);
+
+ for (child = priv->xmlcur->children; child; child = child->next) {
+ if (!xmlStrcmp (child->name, BAD_CAST "titleabbrev")) {
+ title = child;
+ goto done;
+ }
+ else if (!xmlStrcmp (child->name, BAD_CAST "title"))
+ title_tmp = child;
+ else if (!xmlStrcmp (child->name, BAD_CAST infoname))
+ infos[0] = child;
+ else if (!xmlStrcmp (child->name, BAD_CAST "blockinfo"))
+ infos[1] = child;
+ else if (!xmlStrcmp (child->name, BAD_CAST "objectinfo"))
+ infos[2] = child;
+ }
+
+ if (title_tmp) {
+ title = title_tmp;
+ goto done;
+ }
+
+ for (i = 0; i < 3; i++) {
+ child = infos[i];
+ if (child) {
+ for (title = child->children; title; title = title->next) {
+ if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev"))
+ goto done;
+ else if (!xmlStrcmp (title->name, BAD_CAST "title"))
+ title_tmp = title;
+ }
+ if (title_tmp) {
+ title = title_tmp;
+ goto done;
+ }
+ }
+ }
+ }
+
+ done:
+ g_free (infoname);
+
+ if (title)
+ return (gchar *) xmlNodeGetContent (title);
+ else
+ return g_strdup (_("Unknown"));
+}
diff --git a/src/yelp-docbook.h b/src/yelp-docbook.h
new file mode 100644
index 00000000..9690a718
--- /dev/null
+++ b/src/yelp-docbook.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-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>
+ */
+
+#ifndef __YELP_DOCBOOK_H__
+#define __YELP_DOCBOOK_H__
+
+#include <glib-object.h>
+
+#include "yelp-document.h"
+
+#define YELP_TYPE_DOCBOOK (yelp_docbook_get_type ())
+#define YELP_DOCBOOK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_DOCBOOK, YelpDocbook))
+#define YELP_DOCBOOK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_DOCBOOK, YelpDocbookClass))
+#define YELP_IS_DOCBOOK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_DOCBOOK))
+#define YELP_IS_DOCBOOK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_DOCBOOK))
+#define YELP_DOCBOOK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_DOCBOOK, YelpDocbookClass))
+
+typedef struct _YelpDocbook YelpDocbook;
+typedef struct _YelpDocbookClass YelpDocbookClass;
+typedef struct _YelpDocbookPriv YelpDocbookPriv;
+
+struct _YelpDocbook {
+ YelpDocument parent;
+ YelpDocbookPriv *priv;
+};
+
+struct _YelpDocbookClass {
+ YelpDocumentClass parent_class;
+};
+
+GType yelp_docbook_get_type (void);
+YelpDocument * yelp_docbook_new (gchar *uri);
+
+#endif /* __YELP_DOCBOOK_H__ */
diff --git a/src/yelp-document.c b/src/yelp-document.c
new file mode 100644
index 00000000..bce10d05
--- /dev/null
+++ b/src/yelp-document.c
@@ -0,0 +1,782 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-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 "yelp-document.h"
+#include "yelp-debug.h"
+
+#define YELP_DOCUMENT_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DOCUMENT, YelpDocumentPriv))
+
+typedef struct _Request Request;
+struct _Request {
+ YelpDocument *document;
+ YelpDocumentFunc func;
+ gpointer user_data;
+
+ YelpError *error;
+
+ gint req_id;
+ gchar *page_id;
+
+ gint idle_funcs;
+ gboolean cancel;
+};
+
+struct _YelpDocumentPriv {
+ GMutex *mutex;
+
+ gchar *root_id;
+
+ GHashTable *reqs_by_req_id; /* Indexed by the request ID */
+ GHashTable *reqs_by_page_id; /* Indexed by page ID, contains GSList */
+ GSList *reqs_pending; /* List of requests that need a page */
+
+ /* Real page IDs map to themselves, so this list doubles
+ * as a list of all valid page IDs.
+ */
+ GHashTable *page_ids; /* Mapping of fragment IDs to real page IDs */
+ GHashTable *titles; /* Mapping of page IDs to titles */
+ GHashTable *contents; /* Mapping of page IDs to string content */
+ GHashTable *pages; /* Mapping of page IDs to open YelpPages */
+
+ GHashTable *prev_ids; /* Mapping of page IDs to "previous page" IDs */
+ GHashTable *next_ids; /* Mapping of page IDs to "next page" IDs */
+ GHashTable *up_ids; /* Mapping of page IDs to "up page" IDs */
+};
+
+static void document_class_init (YelpDocumentClass *klass);
+static void document_init (YelpDocument *document);
+static void document_dispose (GObject *object);
+
+static gboolean request_idle_title (Request *request);
+static gboolean request_idle_page (Request *request);
+static gboolean request_idle_error (Request *request);
+static void request_try_free (Request *request);
+static void request_free (Request *request);
+
+static void hash_slist_insert (GHashTable *hash,
+ const gchar *key,
+ gpointer value);
+static void hash_slist_remove (GHashTable *hash,
+ const gchar *key,
+ gpointer value);
+
+GStaticMutex str_mutex = G_STATIC_MUTEX_INIT;
+GHashTable *str_refs = NULL;
+static gchar * str_ref (gchar *str);
+static void str_unref (gchar *str);
+
+static GObjectClass *parent_class;
+
+GType
+yelp_document_get_type (void)
+{
+ static GType type = 0;
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (YelpDocumentClass),
+ NULL, NULL,
+ (GClassInitFunc) document_class_init,
+ NULL, NULL,
+ sizeof (YelpDocument),
+ 0,
+ (GInstanceInitFunc) document_init,
+ };
+ type = g_type_register_static (G_TYPE_OBJECT,
+ "YelpDocument",
+ &info, 0);
+ }
+ return type;
+}
+
+static void
+document_class_init (YelpDocumentClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->dispose = document_dispose;
+
+ g_type_class_add_private (klass, sizeof (YelpDocumentPriv));
+}
+
+static void
+document_init (YelpDocument *document)
+{
+ YelpDocumentPriv *priv;
+
+ document->priv = priv = YELP_DOCUMENT_GET_PRIVATE (document);
+
+ priv->mutex = g_mutex_new ();
+
+ priv->reqs_by_req_id =
+ g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL,
+ (GDestroyNotify) request_try_free);
+ priv->reqs_by_page_id =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) g_slist_free);
+ priv->reqs_pending = NULL;
+
+ priv->page_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->titles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->contents = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) str_unref);
+ priv->pages = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) g_slist_free);
+ priv->prev_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->next_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->up_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+}
+
+static void
+document_dispose (GObject *object)
+{
+ YelpDocument *document = YELP_DOCUMENT (object);
+
+ g_free (document->priv->root_id);
+
+ g_slist_free (document->priv->reqs_pending);
+ g_hash_table_destroy (document->priv->reqs_by_page_id);
+ g_hash_table_destroy (document->priv->reqs_by_req_id);
+
+ g_hash_table_destroy (document->priv->page_ids);
+ g_hash_table_destroy (document->priv->titles);
+
+ g_hash_table_destroy (document->priv->contents);
+ g_hash_table_destroy (document->priv->pages);
+
+ g_hash_table_destroy (document->priv->prev_ids);
+ g_hash_table_destroy (document->priv->next_ids);
+ g_hash_table_destroy (document->priv->up_ids);
+
+ g_mutex_free (document->priv->mutex);
+
+ parent_class->dispose (object);
+}
+
+/******************************************************************************/
+
+gint
+yelp_document_get_page (YelpDocument *document,
+ gchar *page_id,
+ YelpDocumentFunc func,
+ gpointer user_data)
+{
+ YelpDocumentPriv *priv;
+ gchar *real_id;
+ gboolean handled = FALSE;
+ Request *request;
+ gint req_id;
+ static gint request_id = 0;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ debug_print (DB_FUNCTION, "entering\n");
+ debug_print (DB_ARG, " page_id = \"%s\"\n", page_id);
+
+ priv = document->priv;
+
+ request = g_slice_new0 (Request);
+ request->document = document;
+ request->func = func;
+ request->user_data = user_data;
+ request->req_id = req_id = ++request_id;
+
+ real_id = g_hash_table_lookup (priv->page_ids, page_id);
+ if (real_id)
+ request->page_id = g_strdup (real_id);
+ else
+ request->page_id = g_strdup (page_id);
+
+ g_mutex_lock (priv->mutex);
+
+ g_hash_table_insert (priv->reqs_by_req_id,
+ GINT_TO_POINTER (req_id),
+ request);
+ hash_slist_insert (priv->reqs_by_page_id,
+ request->page_id,
+ request);
+ priv->reqs_pending = g_slist_prepend (priv->reqs_pending, request);
+
+ if (g_hash_table_lookup (priv->titles, request->page_id)) {
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_title, request);
+ }
+
+ if (g_hash_table_lookup (priv->contents, request->page_id)) {
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_page, request);
+ handled = TRUE;
+ }
+
+ g_mutex_unlock (priv->mutex);
+
+ YELP_DOCUMENT_GET_CLASS (document)->request (document,
+ req_id,
+ handled,
+ request->page_id,
+ func,
+ user_data);
+
+ return req_id;
+}
+
+void
+yelp_document_cancel_page (YelpDocument *document, gint req_id)
+{
+ YelpDocumentPriv *priv;
+ Request *request;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ debug_print (DB_FUNCTION, "entering\n");
+ debug_print (DB_ARG, " req_id = %i\n", req_id);
+
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+ request = g_hash_table_lookup (priv->reqs_by_req_id,
+ GINT_TO_POINTER (req_id));
+ if (request) {
+ priv->reqs_pending = g_slist_remove (priv->reqs_pending,
+ (gconstpointer) request);
+ hash_slist_remove (priv->reqs_by_page_id,
+ request->page_id,
+ request);
+ g_hash_table_remove (priv->reqs_by_req_id,
+ GINT_TO_POINTER (req_id));
+ } else {
+ g_warning ("YelpDocument: Attempted to remove request %i,"
+ " but no such request exists.",
+ req_id);
+ }
+ g_mutex_unlock (priv->mutex);
+
+ if (YELP_DOCUMENT_GET_CLASS (document)->cancel)
+ YELP_DOCUMENT_GET_CLASS (document)->cancel (document, req_id);
+}
+
+/******************************************************************************/
+
+void
+yelp_document_release_page (YelpDocument *document, YelpPage *page)
+{
+ YelpDocumentPriv *priv;
+
+ g_return_if_fail (YELP_IS_DOCUMENT (document));
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ hash_slist_remove (priv->pages, page->id, page);
+ if (page->content)
+ str_unref (page->content);
+
+ g_mutex_unlock (priv->mutex);
+}
+
+/******************************************************************************/
+
+void
+yelp_document_set_root_id (YelpDocument *document, gchar *root_id)
+{
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ if (document->priv->root_id)
+ g_free (document->priv->root_id);
+
+ document->priv->root_id = g_strdup (root_id);
+}
+
+void
+yelp_document_add_page_id (YelpDocument *document, gchar *id, gchar *page_id)
+{
+ GSList *reqs, *cur;
+ Request *request;
+ gchar *title, *contents;
+ YelpDocumentPriv *priv;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+ if (g_hash_table_lookup (priv->page_ids, id)) {
+ g_warning ("YelpDocument: Attempted to add an ID mapping from"
+ " %s to %s, but %s is already mapped.",
+ id, page_id, id);
+ g_mutex_unlock (priv->mutex);
+ return;
+ }
+
+ g_hash_table_insert (priv->page_ids, g_strdup (id), g_strdup (page_id));
+
+ if (!g_str_equal (id, page_id)) {
+ title = g_hash_table_lookup (priv->titles, page_id);
+ contents = g_hash_table_lookup (priv->contents, page_id);
+ reqs = g_hash_table_lookup (priv->reqs_by_page_id, id);
+ for (cur = reqs; cur != NULL; cur = cur->next) {
+ if (cur->data) {
+ request = (Request *) cur->data;
+ g_free (request->page_id);
+ request->page_id = g_strdup (page_id);
+ hash_slist_insert (priv->reqs_by_page_id, page_id, request);
+ if (title) {
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_title, request);
+ }
+ if (contents) {
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_page, request);
+ }
+ }
+ }
+ if (reqs)
+ g_hash_table_remove (priv->reqs_by_page_id, id);
+ }
+
+ g_mutex_unlock (priv->mutex);
+}
+
+void
+yelp_document_add_prev_id (YelpDocument *document, gchar *page_id, gchar *prev_id)
+{
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ g_hash_table_replace (document->priv->prev_ids, g_strdup (page_id), g_strdup (prev_id));
+ g_mutex_unlock (document->priv->mutex);
+}
+
+void
+yelp_document_add_next_id (YelpDocument *document, gchar *page_id, gchar *next_id)
+{
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ g_hash_table_replace (document->priv->next_ids, g_strdup (page_id), g_strdup (next_id));
+ g_mutex_unlock (document->priv->mutex);
+}
+
+void
+yelp_document_add_up_id (YelpDocument *document, gchar *page_id, gchar *up_id)
+{
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ g_hash_table_replace (document->priv->up_ids, g_strdup (page_id), g_strdup (up_id));
+ g_mutex_unlock (document->priv->mutex);
+}
+
+void
+yelp_document_add_title (YelpDocument *document, gchar *page_id, gchar *title)
+{
+ GSList *reqs, *cur;
+ Request *request;
+ YelpDocumentPriv *priv;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ debug_print (DB_FUNCTION, "entering\n");
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ g_hash_table_replace (priv->titles, g_strdup (page_id), g_strdup (title));
+
+ reqs = g_hash_table_lookup (priv->reqs_by_page_id, page_id);
+ for (cur = reqs; cur != NULL; cur = cur->next) {
+ if (cur->data) {
+ request = (Request *) cur->data;
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_title, request);
+ }
+ }
+
+ g_mutex_unlock (priv->mutex);
+}
+
+void
+yelp_document_add_page (YelpDocument *document, gchar *page_id, const gchar *contents)
+{
+ GSList *reqs, *cur;
+ Request *request;
+ YelpDocumentPriv *priv;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ debug_print (DB_FUNCTION, "entering\n");
+ debug_print (DB_ARG, " page_id = \"%s\"\n", page_id);
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ g_hash_table_replace (priv->contents,
+ g_strdup (page_id),
+ str_ref ((gchar *) contents));
+
+ reqs = g_hash_table_lookup (priv->reqs_by_page_id, page_id);
+ for (cur = reqs; cur != NULL; cur = cur->next) {
+ if (cur->data) {
+ request = (Request *) cur->data;
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_page, request);
+ }
+ }
+
+ g_mutex_unlock (priv->mutex);
+}
+
+void
+yelp_document_error_request (YelpDocument *document, gint req_id, YelpError *error)
+{
+ Request *request;
+ YelpDocumentPriv *priv;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ debug_print (DB_FUNCTION, "entering\n");
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ request = g_hash_table_lookup (priv->reqs_by_req_id,
+ GINT_TO_POINTER (req_id));
+ if (request) {
+ request->error = error;
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_error, request);
+ } else {
+ yelp_error_free (error);
+ }
+
+ g_mutex_unlock (priv->mutex);
+}
+
+void
+yelp_document_error_pending (YelpDocument *document, YelpError *error)
+{
+ GSList *cur;
+ Request *request;
+ YelpDocumentPriv *priv;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ debug_print (DB_FUNCTION, "entering\n");
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ if (priv->reqs_pending) {
+ for (cur = priv->reqs_pending; cur; cur = cur->next) {
+ request = cur->data;
+ if (cur->next)
+ request->error = yelp_error_copy (error);
+ else
+ request->error = error;
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_error, request);
+ }
+
+ g_slist_free (priv->reqs_pending);
+ priv->reqs_pending = NULL;
+ } else {
+ yelp_error_free (error);
+ }
+
+ g_mutex_unlock (priv->mutex);
+}
+
+/******************************************************************************/
+
+static gboolean
+request_idle_title (Request *request)
+{
+ YelpDocument *document;
+ YelpDocumentPriv *priv;
+ YelpDocumentFunc func = NULL;
+ gchar *title;
+ gint req_id = 0;
+ gpointer user_data = user_data;
+
+ g_assert (request != NULL && YELP_IS_DOCUMENT (request->document));
+
+ if (request->cancel) {
+ request->idle_funcs--;
+ return FALSE;
+ }
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ document = g_object_ref (request->document);
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ title = g_hash_table_lookup (priv->titles, request->page_id);
+ if (title) {
+ func = request->func;
+ req_id = request->req_id;
+ title = g_strdup (title);
+ user_data = request->user_data;
+ }
+
+ request->idle_funcs--;
+ g_mutex_unlock (priv->mutex);
+
+ if (func)
+ func (document,
+ YELP_DOCUMENT_SIGNAL_TITLE,
+ req_id,
+ title,
+ user_data);
+
+ g_object_unref (document);
+ return FALSE;
+}
+
+static gboolean
+request_idle_page (Request *request)
+{
+ YelpDocument *document;
+ YelpDocumentPriv *priv;
+ YelpDocumentFunc func = NULL;
+ YelpPage *page = NULL;
+ gchar *contents, *tmp;
+ gint req_id = 0;
+ gpointer user_data = user_data;
+
+ g_assert (request != NULL && YELP_IS_DOCUMENT (request->document));
+
+ if (request->cancel) {
+ request->idle_funcs--;
+ return FALSE;
+ }
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ document = g_object_ref (request->document);
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ contents = g_hash_table_lookup (priv->contents, request->page_id);
+ if (contents) {
+ func = request->func;
+ req_id = request->req_id;
+ user_data = request->user_data;
+
+ /* FIXME: there will come a day when we can't just assume XHTML */
+ page = yelp_page_new_string (YELP_DOCUMENT (request->document),
+ request->page_id,
+ str_ref (contents),
+ YELP_PAGE_MIME_XHTML);
+ tmp = g_hash_table_lookup (priv->prev_ids, request->page_id);
+ if (tmp)
+ page->prev_id = g_strdup (tmp);
+ tmp = g_hash_table_lookup (priv->next_ids, request->page_id);
+ if (tmp)
+ page->next_id = g_strdup (tmp);
+ tmp = g_hash_table_lookup (priv->up_ids, request->page_id);
+ if (tmp)
+ page->up_id = g_strdup (tmp);
+ if (priv->root_id)
+ page->root_id = g_strdup (priv->root_id);
+ priv->reqs_pending = g_slist_remove (priv->reqs_pending, request);
+ }
+
+ request->idle_funcs--;
+ g_mutex_unlock (priv->mutex);
+
+ if (func) {
+ func (document,
+ YELP_DOCUMENT_SIGNAL_PAGE,
+ req_id,
+ page,
+ user_data);
+ }
+
+ g_object_unref (document);
+
+ return FALSE;
+}
+
+static gboolean
+request_idle_error (Request *request)
+{
+ YelpDocument *document;
+ YelpDocumentPriv *priv;
+ YelpDocumentFunc func = NULL;
+ YelpError *error = NULL;
+ gint req_id = 0;
+ gpointer user_data = user_data;
+
+ g_assert (request != NULL && YELP_IS_DOCUMENT (request->document));
+
+ if (request->cancel) {
+ request->idle_funcs--;
+ return FALSE;
+ }
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ document = g_object_ref (request->document);
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ if (request->error) {
+ func = request->func;
+ req_id = request->req_id;
+ user_data = request->user_data;
+ error = request->error;
+ request->error = NULL;
+
+ priv->reqs_pending = g_slist_remove (priv->reqs_pending, request);
+ }
+
+ request->idle_funcs--;
+ g_mutex_unlock (priv->mutex);
+
+ if (func)
+ func (document,
+ YELP_DOCUMENT_SIGNAL_ERROR,
+ req_id,
+ error,
+ user_data);
+
+ g_object_unref (document);
+ return FALSE;
+}
+
+static void
+request_try_free (Request *request) {
+ debug_print (DB_FUNCTION, "entering\n");
+ request->cancel = TRUE;
+
+ if (request->idle_funcs == 0)
+ request_free (request);
+ else
+ g_idle_add ((GSourceFunc) request_try_free, request);
+}
+
+static void
+request_free (Request *request)
+{
+ debug_print (DB_FUNCTION, "entering\n");
+ g_free (request->page_id);
+ g_free (request);
+}
+
+/******************************************************************************/
+
+static void
+hash_slist_insert (GHashTable *hash,
+ const gchar *key,
+ gpointer value)
+{
+ GSList *list;
+ list = g_hash_table_lookup (hash, key);
+ if (list) {
+ list->next = g_slist_prepend (list->next, value);
+ } else {
+ list = g_slist_prepend (NULL, value);
+ list = g_slist_prepend (list, NULL);
+ g_hash_table_insert (hash, g_strdup (key), list);
+ }
+}
+
+static void
+hash_slist_remove (GHashTable *hash,
+ const gchar *key,
+ gpointer value)
+{
+ GSList *list;
+ list = g_hash_table_lookup (hash, key);
+ if (list) {
+ list = g_slist_remove (list, value);
+ if (list->next == NULL)
+ g_hash_table_remove (hash, key);
+ }
+}
+
+static gchar *
+str_ref (gchar *str)
+{
+ gpointer p;
+ guint i;
+
+ g_static_mutex_lock (&str_mutex);
+
+ if (str_refs == NULL)
+ str_refs = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ p = g_hash_table_lookup (str_refs, str);
+
+ i = GPOINTER_TO_UINT (p);
+ i++;
+ p = GUINT_TO_POINTER (i);
+
+ g_hash_table_insert (str_refs, str, p);
+
+ g_static_mutex_unlock (&str_mutex);
+
+ return str;
+}
+
+static void
+str_unref (gchar *str)
+{
+ gpointer p;
+ guint i;
+
+ g_static_mutex_lock (&str_mutex);
+
+ p = g_hash_table_lookup (str_refs, str);
+
+ i = GPOINTER_TO_UINT (p);
+ i--;
+ p = GUINT_TO_POINTER (i);
+
+ if (i > 0)
+ g_hash_table_insert (str_refs, str, p);
+ else {
+ g_hash_table_remove (str_refs, str);
+ g_free (str);
+ }
+
+ g_static_mutex_unlock (&str_mutex);
+}
diff --git a/src/yelp-document.h b/src/yelp-document.h
new file mode 100644
index 00000000..c550c764
--- /dev/null
+++ b/src/yelp-document.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-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>
+ */
+
+#ifndef __YELP_DOCUMENT_H__
+#define __YELP_DOCUMENT_H__
+
+#include <glib-object.h>
+
+#define YELP_TYPE_DOCUMENT (yelp_document_get_type ())
+#define YELP_DOCUMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_DOCUMENT, YelpDocument))
+#define YELP_DOCUMENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_DOCUMENT, YelpDocumentClass))
+#define YELP_IS_DOCUMENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_DOCUMENT))
+#define YELP_IS_DOCUMENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_DOCUMENT))
+#define YELP_DOCUMENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_DOCUMENT, YelpDocumentClass))
+
+typedef struct _YelpDocument YelpDocument;
+typedef struct _YelpDocumentClass YelpDocumentClass;
+typedef struct _YelpDocumentPriv YelpDocumentPriv;
+
+/* This needs to be after the typedefs. */
+#include "yelp-page.h"
+
+typedef enum {
+ YELP_DOCUMENT_SIGNAL_PAGE,
+ YELP_DOCUMENT_SIGNAL_TITLE,
+ YELP_DOCUMENT_SIGNAL_ERROR
+} YelpDocumentSignal;
+
+enum {
+ YELP_DOCUMENT_COLUMN_ID = 0,
+ YELP_DOCUMENT_COLUMN_TITLE,
+ YELP_DOCUMENT_NUM_COLUMNS
+};
+
+typedef void (*YelpDocumentFunc) (YelpDocument *document,
+ YelpDocumentSignal signal,
+ gint req_id,
+ gpointer func_data,
+ gpointer user_data);
+
+struct _YelpDocument {
+ GObject parent;
+ YelpDocumentPriv *priv;
+};
+
+struct _YelpDocumentClass {
+ GObjectClass parent_class;
+
+ /* Virtual Functions */
+ void (*request) (YelpDocument *document,
+ gint req_id,
+ gboolean handled,
+ gchar *page_id,
+ YelpDocumentFunc func,
+ gpointer user_data);
+ void (*cancel) (YelpDocument *document,
+ gint req_id);
+
+ gint (*get_page) (YelpDocument *document,
+ gchar *page_id,
+ gpointer user_data);
+ void (*release_page) (YelpDocument *document,
+ YelpPage *page);
+};
+
+
+GType yelp_document_get_type (void);
+
+gint yelp_document_get_page (YelpDocument *document,
+ gchar *page_id,
+ YelpDocumentFunc func,
+ gpointer user_data);
+void yelp_document_cancel_page (YelpDocument *document,
+ gint req_id);
+
+/* Only called by yelp_page_free */
+void yelp_document_release_page (YelpDocument *document,
+ YelpPage *page);
+
+/* Only called by subclasses */
+void yelp_document_set_root_id (YelpDocument *document,
+ gchar *root_id);
+void yelp_document_add_page_id (YelpDocument *document,
+ gchar *id,
+ gchar *page_id);
+void yelp_document_add_prev_id (YelpDocument *document,
+ gchar *page_id,
+ gchar *prev_id);
+void yelp_document_add_next_id (YelpDocument *document,
+ gchar *page_id,
+ gchar *next_id);
+void yelp_document_add_up_id (YelpDocument *document,
+ gchar *page_id,
+ gchar *up_id);
+void yelp_document_add_title (YelpDocument *document,
+ gchar *page_id,
+ gchar *title);
+void yelp_document_add_page (YelpDocument *document,
+ gchar *page_id,
+ const gchar *contents);
+void yelp_document_error_request (YelpDocument *document,
+ gint req_id,
+ YelpError *error);
+void yelp_document_error_pending (YelpDocument *document,
+ YelpError *error);
+
+#endif /* __YELP_DOCUMENT_H__ */
diff --git a/src/yelp-error.c b/src/yelp-error.c
index b32bd5e5..fcb21425 100644
--- a/src/yelp-error.c
+++ b/src/yelp-error.c
@@ -1,6 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* Copyright (C) 2002 Mikael Hallendal <micke@imendio.com>
+ * 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
@@ -20,12 +21,101 @@
#include <config.h>
+#include <glib.h>
#include <glib/gi18n.h>
#include "yelp-error.h"
+struct _YelpError {
+ gchar *title;
+ gchar *message;
+};
+
+static YelpError *
+yelp_error_new_valist (gchar *title, gchar *format, va_list args)
+{
+ YelpError *error;
+
+ error = g_slice_new (YelpError);
+ error->title = g_strdup (title);
+ error->message = g_strdup_vprintf (format, args);
+
+ return error;
+}
+
+YelpError *
+yelp_error_new (gchar *title, gchar *format, ...)
+{
+ YelpError *error;
+ va_list args;
+
+ va_start (args, format);
+ error = yelp_error_new_valist (title, format, args);
+ va_end (args);
+
+ return error;
+}
+
+void
+yelp_error_set (YelpError **error, gchar *title, gchar *format, ...)
+{
+ YelpError *new;
+ va_list args;
+
+ va_start (args, format);
+ new = yelp_error_new_valist (title, format, args);
+ va_end (args);
+
+ if (*error == NULL)
+ *error = new;
+ else
+ g_warning
+ ("YelpError set over the top of a previous YelpError or uninitialized\n"
+ "memory. This indicates a bug in someone's code. You must ensure an\n"
+ "error is NULL before it's set. The overwriting error message was:\n"
+ "%s",
+ new->message);
+}
+
+YelpError *
+yelp_error_copy (YelpError *error)
+{
+ YelpError *new;
+
+ new = g_slice_new (YelpError);
+ new->title = g_strdup (error->title);
+ new->message = g_strdup (error->message);
+
+ return error;
+}
+
+const gchar *
+yelp_error_get_title (YelpError *error)
+{
+ g_return_val_if_fail (error != NULL, NULL);
+ return error->title;
+}
+
+const gchar *
+yelp_error_get_message (YelpError *error)
+{
+ g_return_val_if_fail (error != NULL, NULL);
+ return error->message;
+}
+
+void
+yelp_error_free (YelpError *error)
+{
+ g_return_if_fail (error != NULL);
+ g_free (error->title);
+ g_free (error->message);
+ g_slice_free (YelpError, error);
+}
+
+/******************************************************************************/
+
GQuark
-yelp_error_quark (void)
+yelp_gerror_quark (void)
{
static GQuark q = 0;
@@ -36,34 +126,24 @@ yelp_error_quark (void)
}
const gchar *
-yelp_error_get_primary (GError *error)
+yelp_gerror_get_title (GError *error)
{
- if (!error || error->domain != YELP_ERROR)
- return _("An unknown error occured");
+ if (!error || error->domain != YELP_GERROR)
+ return _("Unknown Error");
switch (error->code) {
- case YELP_ERROR_NO_DOC:
- return _("Could not load document");
- case YELP_ERROR_NO_PAGE:
- return _("Could not load section");
- case YELP_ERROR_NO_TOC:
- return _("Could not read the table of contents");
- case YELP_ERROR_FORMAT:
- return _("Unsupported Format");
- case YELP_ERROR_IO:
- return _("Could not read document");
- case YELP_ERROR_PROC:
- return _("Could not process document");
- default:
- return _("An unknown error occured");
+ case YELP_GERROR_IO:
+ return _("Could Not Read File");
}
+
+ return _("Unknown Error");
}
const gchar *
-yelp_error_get_secondary (GError *error)
+yelp_gerror_get_message (GError *error)
{
if (!error || !error->message)
- return _("No information is available about the error.");
+ return _("No information is available about this error.");
else
return error->message;
}
diff --git a/src/yelp-error.h b/src/yelp-error.h
index 02a1dc46..91a5bda2 100644
--- a/src/yelp-error.h
+++ b/src/yelp-error.h
@@ -1,6 +1,7 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* Copyright (C) 2002 Mikael Hallendal <micke@imendio.com>
+ * 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
@@ -23,20 +24,31 @@
#include <glib.h>
-#define YELP_ERROR yelp_error_quark ()
+typedef struct _YelpError YelpError;
-typedef enum {
- YELP_ERROR_NO_DOC, /* Selected document not found */
- YELP_ERROR_NO_PAGE, /* Selected page not found */
- YELP_ERROR_NO_TOC, /* Could not read the TOC */
- YELP_ERROR_FORMAT, /* Format is not supported */
- YELP_ERROR_IO, /* Error in IO */
- YELP_ERROR_PROC /* Error processing the document */
-} YelpError;
+YelpError * yelp_error_new (gchar *title,
+ gchar *format,
+ ...);
+void yelp_error_set (YelpError **error,
+ gchar *title,
+ gchar *format,
+ ...);
+YelpError * yelp_error_copy (YelpError *error);
-GQuark yelp_error_quark (void) G_GNUC_CONST;
+const gchar * yelp_error_get_title (YelpError *error);
+const gchar * yelp_error_get_message (YelpError *error);
-const gchar * yelp_error_get_primary (GError *error);
-const gchar * yelp_error_get_secondary (GError *error);
+void yelp_error_free (YelpError *error);
+
+
+#define YELP_GERROR yelp_gerror_quark ()
+
+enum {
+ YELP_GERROR_IO
+};
+
+GQuark yelp_gerror_quark (void) G_GNUC_CONST;
+const gchar * yelp_gerror_get_title (GError *error);
+const gchar * yelp_gerror_get_message (GError *error);
#endif /* __YELP_ERROR_H__ */
diff --git a/src/yelp-io-channel.c b/src/yelp-io-channel.c
index 85f26d60..16c57509 100644
--- a/src/yelp-io-channel.c
+++ b/src/yelp-io-channel.c
@@ -103,7 +103,7 @@ yelp_io_channel_new_file (gchar *file,
if (!channel) {
if (error) {
- g_set_error (error, YELP_ERROR, YELP_ERROR_IO,
+ g_set_error (error, YELP_GERROR, YELP_GERROR_IO,
_("The file ‘%s’ could not be read and decoded. "
"The file may be compressed in an unsupported "
"format."),
diff --git a/src/yelp-man-parser.c b/src/yelp-man-parser.c
index 0b7e97cd..edba95c5 100644
--- a/src/yelp-man-parser.c
+++ b/src/yelp-man-parser.c
@@ -26,15 +26,12 @@
#include <glib.h>
#include <glib/gi18n.h>
-#include <glib/gprintf.h>
#include <libxml/tree.h>
#include <string.h>
+#include "yelp-debug.h"
#include "yelp-io-channel.h"
#include "yelp-man-parser.h"
-#include "yelp-utils.h"
-
-#define d(x)
#define PARSER_CUR (g_utf8_get_char (parser->cur) != '\0' \
&& (parser->cur - parser->buffer < parser->length))
@@ -172,34 +169,6 @@ yelp_man_parser_parse_file (YelpManParser *parser,
return parser->doc;
}
-xmlDocPtr
-yelp_man_parser_parse_doc (YelpManParser *parser,
- YelpDocInfo *doc_info)
-{
- gchar *file;
- gchar *encoding = NULL;
- xmlDocPtr doc = NULL;
-
- g_return_val_if_fail (parser != NULL, NULL);
- g_return_val_if_fail (doc_info != NULL, NULL);
- g_return_val_if_fail (yelp_doc_info_get_type (doc_info) != YELP_DOC_TYPE_MAN, NULL);
-
- file = yelp_doc_info_get_filename (doc_info);
-
- if (!file)
- return NULL;
-
- encoding = (gchar *)g_getenv("MAN_ENCODING");
- if (encoding == NULL)
- encoding = "ISO-8859-1";
-
- doc = yelp_man_parser_parse_file (parser, file, encoding);
-
- g_free (file);
-
- return doc;
-}
-
void
yelp_man_parser_free (YelpManParser *parser)
{
@@ -605,7 +574,7 @@ macro_url_handler (YelpManParser *parser, gchar *macro, GSList *args)
tmpNode = parser_stack_pop_node (parser, "UR");
if (tmpNode == NULL)
- d (g_warning ("Found unexpected tag: '%s'\n", macro));
+ debug_print (DB_WARN, "Found unexpected tag: '%s'\n", macro);
else
parser->ins = tmpNode->parent;
} else
@@ -702,7 +671,7 @@ macro_mandoc_list_handler (YelpManParser *parser, gchar *macro, GSList *args)
tmpNode = parser_stack_pop_node (parser, "Bl");
if (tmpNode == NULL)
- d (g_warning ("Found unexpected tag: '%s'\n", macro));
+ debug_print (DB_WARN, "Found unexpected tag: '%s'\n", macro);
else
parser->ins = tmpNode->parent;
}
@@ -721,7 +690,7 @@ macro_verbatim_handler (YelpManParser *parser, gchar *macro, GSList *args)
tmpNode = parser_stack_pop_node (parser, "Verbatim");
if (tmpNode == NULL)
- d (g_warning ("Found unexpected tag: '%s'\n", macro));
+ debug_print (DB_WARN, "Found unexpected tag: '%s'\n", macro);
else
parser->ins = tmpNode->parent;
}
@@ -1266,7 +1235,7 @@ get_argument:
}
else if (g_str_equal (str, "TE")) {
/* We should only see this from within parser_parse_table */
- d (g_warning ("Found unexpected tag: '%s'\n", str));
+ debug_print (DB_WARN, "Found unexpected tag: '%s'\n", str);
g_free (str);
}
/* "ie" and "if" are conditional macros in groff
@@ -1451,7 +1420,7 @@ parser_append_given_text_handle_escapes (YelpManParser *parser, gchar *text, gbo
if (g_str_equal (str, "fI") || g_str_equal (str, "fB"))
parser->ins = parser_append_node (parser, str);
else if (!g_str_equal (str, "fR") && !g_str_equal (str, "fP"))
- d (g_warning ("No rule matching the tag '%s'\n", str));
+ debug_print (DB_WARN, "No rule matching the tag '%s'\n", str);
g_free (str);
anc = ptr;
@@ -1798,7 +1767,7 @@ parser_parse_table (YelpManParser *parser)
if (*(parser->buffer + 1) == 'T'
&& *(parser->buffer + 2) == 'E') {
if (parser_stack_pop_node (parser, "TABLE") == NULL)
- d (g_warning ("Found unexpected tag: 'TE'\n"));
+ debug_print (DB_WARN, "Found unexpected tag: 'TE'\n");
else {
parser->ins = table_start;
diff --git a/src/yelp-man-parser.h b/src/yelp-man-parser.h
index 26553e0a..26976d2a 100644
--- a/src/yelp-man-parser.h
+++ b/src/yelp-man-parser.h
@@ -26,16 +26,12 @@
#include <glib.h>
#include <libxml/tree.h>
-#include "yelp-utils.h"
-
typedef struct _YelpManParser YelpManParser;
YelpManParser * yelp_man_parser_new (void);
xmlDocPtr yelp_man_parser_parse_file (YelpManParser *parser,
gchar *file,
const gchar *encoding);
-xmlDocPtr yelp_man_parser_parse_doc (YelpManParser *parser,
- YelpDocInfo *doc);
void yelp_man_parser_free (YelpManParser *parser);
#endif /* __YELP_MAN_PARSER_H__ */
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);
+}
diff --git a/src/yelp-man.h b/src/yelp-man.h
new file mode 100644
index 00000000..844c44ab
--- /dev/null
+++ b/src/yelp-man.h
@@ -0,0 +1,53 @@
+/* -*- 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>
+ */
+
+#ifndef __YELP_MAN_H__
+#define __YELP_MAN_H__
+
+#include <glib-object.h>
+
+#include "yelp-document.h"
+
+#define YELP_TYPE_MAN (yelp_man_get_type ())
+#define YELP_MAN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_MAN, YelpMan))
+#define YELP_MAN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_MAN, YelpManClass))
+#define YELP_IS_MAN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_MAN))
+#define YELP_IS_MAN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_MAN))
+#define YELP_MAN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_MAN, YelpManClass))
+
+typedef struct _YelpMan YelpMan;
+typedef struct _YelpManClass YelpManClass;
+typedef struct _YelpManPriv YelpManPriv;
+
+struct _YelpMan {
+ YelpDocument parent;
+ YelpManPriv *priv;
+};
+
+struct _YelpManClass {
+ YelpDocumentClass parent_class;
+};
+
+GType yelp_man_get_type (void);
+YelpDocument * yelp_man_new (gchar *uri);
+
+#endif /* __YELP_MAN_H__ */
diff --git a/src/yelp-page.c b/src/yelp-page.c
new file mode 100644
index 00000000..1a3e6b9b
--- /dev/null
+++ b/src/yelp-page.c
@@ -0,0 +1,147 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2006 Brent Smith <gnome@nextreality.net>
+ * 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.
+ *
+ * Authors: Brent Smith <gnome@nextreality.net>
+ * Shaun McCance <shaunm@gnome.org>
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "yelp-page.h"
+
+static GIOStatus page_read_string (YelpPage *page,
+ gchar *buffer,
+ gsize count,
+ gsize *bytes_read,
+ YelpError **error);
+static GIOStatus page_read_file (YelpPage *page,
+ gchar *buffer,
+ gsize count,
+ gsize *bytes_read,
+ YelpError **error);
+
+YelpPage *
+yelp_page_new_string (YelpDocument *document,
+ gchar *id,
+ const gchar *content,
+ YelpPageMime mime)
+{
+ YelpPage *page;
+
+ page = g_slice_new0 (YelpPage);
+
+ page->mime = mime;
+
+ if (document)
+ page->document = g_object_ref (document);
+ page->source = YELP_PAGE_SOURCE_STRING;
+ page->id = g_strdup (id);
+
+ page->content = (gchar *) content;
+ page->content_len = strlen (content);
+
+ return page;
+}
+
+GIOStatus
+yelp_page_read (YelpPage *page,
+ gchar *buffer,
+ gsize count,
+ gsize *bytes_read,
+ YelpError **error)
+{
+ /* FIXME: set error */
+ g_return_val_if_fail (page != NULL, G_IO_STATUS_ERROR);
+
+ if (page->source == YELP_PAGE_SOURCE_STRING)
+ return page_read_string (page, buffer, count, bytes_read, error);
+ else
+ return page_read_file (page, buffer, count, bytes_read, error);
+}
+
+static GIOStatus
+page_read_string (YelpPage *page,
+ gchar *buffer,
+ gsize count,
+ gsize *bytes_read,
+ YelpError **error)
+{
+ g_return_val_if_fail (page != NULL, G_IO_STATUS_ERROR);
+
+ if (page->content_offset == page->content_len) {
+ return G_IO_STATUS_EOF;
+ }
+ else if (page->content_offset > page->content_len) {
+ /* FIXME: set the error */
+ return G_IO_STATUS_ERROR;
+ }
+ else if (page->content_offset + count <= page->content_len) {
+ strncpy (buffer, page->content + page->content_offset, count);
+ page->content_offset += count;
+ *bytes_read = count;
+ return G_IO_STATUS_NORMAL;
+ }
+ else {
+ strcpy (buffer, page->content + page->content_offset);
+ *bytes_read = strlen (buffer);
+ page->content_offset += *bytes_read;
+ return G_IO_STATUS_NORMAL;
+ }
+}
+
+static GIOStatus
+page_read_file (YelpPage *page,
+ gchar *buffer,
+ gsize count,
+ gsize *bytes_read,
+ YelpError **error)
+{
+ g_return_val_if_fail (page != NULL, G_IO_STATUS_ERROR);
+ /* FIXME: just use yelp-io-channel? */
+ return G_IO_STATUS_ERROR;
+}
+
+void
+yelp_page_free (YelpPage *page)
+{
+ g_return_if_fail (page != NULL);
+
+ if (page->document) {
+ yelp_document_release_page (page->document, page);
+ g_object_unref (page->document);
+ }
+
+ if (page->title)
+ g_free (page->title);
+
+ if (page->id)
+ g_free (page->id);
+ if (page->prev_id)
+ g_free (page->prev_id);
+ if (page->next_id)
+ g_free (page->next_id);
+ if (page->up_id)
+ g_free (page->up_id);
+ if (page->root_id)
+ g_free (page->root_id);
+
+ g_slice_free (YelpPage, page);
+}
diff --git a/src/yelp-page.h b/src/yelp-page.h
new file mode 100644
index 00000000..f4428fad
--- /dev/null
+++ b/src/yelp-page.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2006 Brent Smith <gnome@nextreality.net>
+ * 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.
+ *
+ * Authors: Brent Smith <gnome@nextreality.net>
+ * Shaun McCance <shaunm@gnome.org>
+ */
+
+#ifndef __YELP_PAGE_H__
+#define __YELP_PAGE_H__
+
+#include <glib.h>
+
+#include "yelp-error.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ YELP_PAGE_MIME_HTML,
+ YELP_PAGE_MIME_XHTML
+} YelpPageMime;
+
+typedef enum {
+ YELP_PAGE_SOURCE_STRING,
+ YELP_PAGE_SOURCE_FILE
+} YelpPageSource;
+
+typedef struct _YelpPage YelpPage;
+
+/* This needs to be right here to compile. */
+#include "yelp-document.h"
+
+struct _YelpPage {
+ YelpDocument *document;
+ YelpPageSource source;
+ YelpPageMime mime;
+
+ gchar *title;
+
+ /* Do not free content. The string is owned by the YelpDocument,
+ * and it does some internal reference counting to make sure it's
+ * only reed when it's no longer referenced. These strings are
+ * just too big to strdup all over the place.
+ */
+ gchar *content;
+ gsize content_len;
+ gsize content_offset;
+
+ gchar *id;
+ gchar *prev_id;
+ gchar *next_id;
+ gchar *up_id;
+ gchar *root_id;
+};
+
+YelpPage * yelp_page_new_string (YelpDocument *document,
+ gchar *id,
+ const gchar *content,
+ YelpPageMime mime);
+
+GIOStatus yelp_page_read (YelpPage *page,
+ gchar *buffer,
+ gsize count,
+ gsize *bytes_read,
+ YelpError **error);
+
+void yelp_page_free (YelpPage *page);
+
+G_END_DECLS
+
+#endif
diff --git a/src/yelp-transform.c b/src/yelp-transform.c
new file mode 100644
index 00000000..1e6f8f5b
--- /dev/null
+++ b/src/yelp-transform.c
@@ -0,0 +1,416 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-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 <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xinclude.h>
+#include <libxslt/xslt.h>
+#include <libxslt/templates.h>
+#include <libxslt/transform.h>
+#include <libxslt/extensions.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/xsltutils.h>
+
+#include "yelp-debug.h"
+#include "yelp-error.h"
+#include "yelp-transform.h"
+
+#define YELP_NAMESPACE "http://www.gnome.org/yelp/ns"
+
+static void transform_run (YelpTransform *transform);
+static gboolean transform_free (YelpTransform *transform);
+static void transform_set_error (YelpTransform *transform,
+ YelpError *error);
+
+static gboolean transform_chunk (YelpTransform *transform);
+static gboolean transform_error (YelpTransform *transform);
+static gboolean transform_final (YelpTransform *transform);
+
+static void xslt_yelp_document (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+static void xslt_yelp_cache (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+
+/******************************************************************************/
+
+YelpTransform
+*yelp_transform_new (gchar *stylesheet,
+ YelpTransformFunc func,
+ gpointer user_data)
+{
+ YelpTransform *transform;
+
+ transform = g_new0 (YelpTransform, 1);
+
+ transform->stylesheet = xsltParseStylesheetFile (BAD_CAST stylesheet);
+ if (!transform->stylesheet) {
+ transform->error =
+ yelp_error_new (_("Invalid Stylesheet"),
+ _("The XSLT stylesheet ‘%s’ is either missing, or it is "
+ "not valid."),
+ stylesheet);
+ transform_error (transform);
+ return NULL;
+ }
+
+ transform->func = func;
+
+ transform->queue = g_async_queue_new ();
+ transform->chunks = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+
+ transform->user_data = user_data;
+
+ return transform;
+}
+
+void
+yelp_transform_start (YelpTransform *transform,
+ xmlDocPtr document,
+ gchar **params)
+{
+ transform->inputDoc = document;
+
+ transform->context = xsltNewTransformContext (transform->stylesheet,
+ transform->inputDoc);
+ if (!transform->context) {
+ YelpError *error =
+ yelp_error_new (_("Broken Transformation"),
+ _("An unknown error occurred while attempting to "
+ "transform the document."));
+ transform_set_error (transform, error);
+ return;
+ }
+
+ transform->params = g_strdupv (params);
+
+ transform->context->_private = transform;
+ xsltRegisterExtElement (transform->context,
+ BAD_CAST "document",
+ BAD_CAST YELP_NAMESPACE,
+ (xsltTransformFunction) xslt_yelp_document);
+ xsltRegisterExtElement (transform->context,
+ BAD_CAST "cache",
+ BAD_CAST YELP_NAMESPACE,
+ (xsltTransformFunction) xslt_yelp_cache);
+
+ transform->mutex = g_mutex_new ();
+ g_mutex_lock (transform->mutex);
+ transform->running = TRUE;
+ transform->thread = g_thread_create ((GThreadFunc) transform_run,
+ transform, FALSE, NULL);
+ g_mutex_unlock (transform->mutex);
+}
+
+gchar *
+yelp_transform_eat_chunk (YelpTransform *transform, gchar *chunk_id)
+{
+ gchar *buf;
+
+ g_mutex_lock (transform->mutex);
+
+ buf = g_hash_table_lookup (transform->chunks, chunk_id);
+ if (buf)
+ g_hash_table_remove (transform->chunks, chunk_id);
+
+ g_mutex_unlock (transform->mutex);
+
+ /* The caller assumes ownership of this memory. */
+ return buf;
+}
+
+void
+yelp_transform_release (YelpTransform *transform)
+{
+ g_mutex_lock (transform->mutex);
+ if (transform->running) {
+ /* We can't free it just now, because the thread is running.
+ * Instead, we'll tell libxslt to stop and mark the transform
+ * as released.
+ */
+ transform->released = TRUE;
+ transform->context->state = XSLT_STATE_STOPPED;
+ } else {
+ /* We might still have pending pops from the queue, so just
+ * schedule transform_free.
+ */
+ g_idle_add ((GSourceFunc) transform_free, transform);
+ }
+ g_mutex_unlock (transform->mutex);
+}
+
+/******************************************************************************/
+
+static void
+transform_run (YelpTransform *transform)
+{
+ transform->outputDoc = xsltApplyStylesheetUser (transform->stylesheet,
+ transform->inputDoc,
+ (const char **) transform->params,
+ NULL, NULL,
+ transform->context);
+
+ /* FIXME: do something with outputDoc? */
+ transform->idle_funcs++;
+ g_idle_add ((GSourceFunc) transform_final, transform);
+
+ g_mutex_lock (transform->mutex);
+ transform->running = FALSE;
+ if (transform->released) {
+ /* The transform was released by its owner, but it couldn't
+ * be freed because this thread was running. But we're in
+ * a thread, and the main thread might still be popping stuff
+ * off the asynchronous queue. Schedule this for freeing.
+ */
+ g_idle_add ((GSourceFunc) transform_free, transform);
+ }
+ g_mutex_unlock (transform->mutex);
+}
+
+static gboolean
+transform_free (YelpTransform *transform)
+{
+ gchar *chunk_id;
+
+ /* If the queue isn't empty yet, try again later. But because
+ * threads scare me and I don't want runaway code, stop trying
+ * after an insane number of attempts and just leak.
+ */
+ if (transform->idle_funcs > 0) {
+ transform->free_attempts++;
+ if (transform->free_attempts < 1000) {
+ return TRUE;
+ } else {
+ g_warning ("Runaway free attempt detected. Memory is about to leak.\n");
+ return FALSE;
+ }
+ }
+
+ g_mutex_lock (transform->mutex);
+ if (transform->outputDoc)
+ xmlFreeDoc (transform->outputDoc);
+ if (transform->stylesheet)
+ xsltFreeStylesheet (transform->stylesheet);
+ if (transform->context)
+ xsltFreeTransformContext (transform->context);
+
+ g_strfreev (transform->params);
+
+ /* FIXME: destroy data */
+ while ((chunk_id = (gchar *) g_async_queue_try_pop (transform->queue)))
+ g_free (chunk_id);
+ g_async_queue_unref (transform->queue);
+ g_mutex_unlock (transform->mutex);
+ g_mutex_free (transform->mutex);
+
+ if (transform->error)
+ yelp_error_free (transform->error);
+
+ g_free (transform);
+ return FALSE;
+}
+
+static void
+transform_set_error (YelpTransform *transform,
+ YelpError *error)
+{
+ g_mutex_lock (transform->mutex);
+ if (transform->released) {
+ yelp_error_free (error);
+ g_mutex_unlock (transform->mutex);
+ return;
+ }
+ if (transform->error)
+ yelp_error_free (transform->error);
+ transform->error = error;
+ transform->idle_funcs++;
+ g_idle_add ((GSourceFunc) transform_error, transform);
+ g_mutex_unlock (transform->mutex);
+}
+
+static gboolean
+transform_chunk (YelpTransform *transform)
+{
+ gchar *chunk_id;
+
+ transform->idle_funcs--;
+ if (transform->released)
+ return FALSE;
+
+ chunk_id = (gchar *) g_async_queue_try_pop (transform->queue);
+
+ if (chunk_id) {
+ if (transform->func)
+ transform->func (transform,
+ YELP_TRANSFORM_CHUNK,
+ chunk_id,
+ transform->user_data);
+ else
+ g_free (chunk_id);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+transform_error (YelpTransform *transform)
+{
+ YelpError *error;
+
+ transform->idle_funcs--;
+ if (transform->released)
+ return FALSE;
+
+ g_mutex_lock (transform->mutex);
+
+ error = transform->error;
+ transform->error = NULL;
+
+ g_mutex_unlock (transform->mutex);
+
+ if (transform->func)
+ transform->func (transform,
+ YELP_TRANSFORM_ERROR,
+ error, transform->user_data);
+ else
+ yelp_error_free (error);
+
+ return FALSE;
+}
+
+static gboolean
+transform_final (YelpTransform *transform)
+{
+ transform->idle_funcs--;
+ if (transform->released)
+ return FALSE;
+
+ /* FIXME: check for anything remaining on the queue */
+ if (transform->func)
+ transform->func (transform,
+ YELP_TRANSFORM_FINAL,
+ NULL, transform->user_data);
+
+ return FALSE;
+}
+
+/******************************************************************************/
+
+static void
+xslt_yelp_document (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp)
+{
+ YelpTransform *transform;
+ xmlChar *page_id = NULL;
+ xmlChar *page_buf;
+ gint buf_size;
+ xsltStylesheetPtr style = NULL;
+ const char *old_outfile;
+ xmlDocPtr new_doc = NULL;
+ xmlDocPtr old_doc;
+ xmlNodePtr old_insert;
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ if (ctxt->state == XSLT_STATE_STOPPED)
+ return;
+
+ if (!ctxt || !node || !inst || !comp)
+ return;
+
+ transform = (YelpTransform *) ctxt->_private;
+
+ page_id = xsltEvalAttrValueTemplate (ctxt, inst,
+ (const xmlChar *) "href",
+ NULL);
+ if (page_id == NULL) {
+ xsltTransformError (ctxt, NULL, inst,
+ _("No href attribute found on yelp:document"));
+ /* FIXME: put a real error here */
+ goto done;
+ }
+ debug_print (DB_ARG, " page_id = \"%s\"\n", page_id);
+
+ old_outfile = ctxt->outputFile;
+ old_doc = ctxt->output;
+ old_insert = ctxt->insert;
+ ctxt->outputFile = (const char *) page_id;
+
+ style = xsltNewStylesheet ();
+ if (style == NULL) {
+ xsltTransformError (ctxt, NULL, inst,
+ _("Out of memory"));
+ goto done;
+ }
+
+ style->omitXmlDeclaration = TRUE;
+
+ new_doc = xmlNewDoc (BAD_CAST "1.0");
+ new_doc->charset = XML_CHAR_ENCODING_UTF8;
+ new_doc->dict = ctxt->dict;
+ xmlDictReference (new_doc->dict);
+
+ ctxt->output = new_doc;
+ ctxt->insert = (xmlNodePtr) new_doc;
+
+ xsltApplyOneTemplate (ctxt, node, inst->children, NULL, NULL);
+ xsltSaveResultToString (&page_buf, &buf_size, new_doc, style);
+
+ ctxt->outputFile = old_outfile;
+ ctxt->output = old_doc;
+ ctxt->insert = old_insert;
+
+ g_mutex_lock (transform->mutex);
+ g_hash_table_insert (transform->chunks, page_id, page_buf);
+ g_async_queue_push (transform->queue, g_strdup ((gchar *) page_id));
+ transform->idle_funcs++;
+ g_idle_add ((GSourceFunc) transform_chunk, transform);
+ g_mutex_unlock (transform->mutex);
+
+ done:
+ if (new_doc)
+ xmlFreeDoc (new_doc);
+ if (style)
+ xsltFreeStylesheet (style);
+}
+
+static void
+xslt_yelp_cache (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp)
+{
+}
diff --git a/src/yelp-transform.h b/src/yelp-transform.h
new file mode 100644
index 00000000..e1262e39
--- /dev/null
+++ b/src/yelp-transform.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-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>
+ */
+
+#ifndef __YELP_TRANSFORM_H__
+#define __YELP_TRANSFORM_H__
+
+#include <glib.h>
+#include <libxml/tree.h>
+#include <libxslt/xslt.h>
+#include <libxslt/transform.h>
+
+#include "yelp-error.h"
+
+typedef struct _YelpTransform YelpTransform;
+
+typedef enum {
+ YELP_TRANSFORM_CHUNK,
+ YELP_TRANSFORM_ERROR,
+ YELP_TRANSFORM_FINAL
+} YelpTransformSignal;
+
+typedef void (*YelpTransformFunc) (YelpTransform *transform,
+ YelpTransformSignal signal,
+ gpointer func_data,
+ gpointer user_data);
+
+struct _YelpTransform {
+ xmlDocPtr inputDoc;
+ xmlDocPtr outputDoc;
+ xsltStylesheetPtr stylesheet;
+ xsltTransformContextPtr context;
+
+ YelpTransformFunc func;
+
+ gchar **params;
+
+ GThread *thread;
+ GMutex *mutex;
+ GAsyncQueue *queue;
+ GHashTable *chunks;
+
+ gboolean running;
+ gboolean released;
+ gint idle_funcs;
+ gint free_attempts;
+
+ gpointer user_data;
+
+ YelpError *error;
+};
+
+YelpTransform *yelp_transform_new (gchar *stylesheet,
+ YelpTransformFunc func,
+ gpointer user_data);
+void yelp_transform_start (YelpTransform *transform,
+ xmlDocPtr document,
+ gchar **params);
+gchar * yelp_transform_eat_chunk (YelpTransform *transform,
+ gchar *chunk_id);
+void yelp_transform_release (YelpTransform *transform);
+
+#endif /* __YELP_TRANSFORM_H__ */
diff --git a/src/yelp-utils.c b/src/yelp-utils.c
index fb8cd37d..351a4117 100644
--- a/src/yelp-utils.c
+++ b/src/yelp-utils.c
@@ -499,23 +499,6 @@ yelp_doc_info_equal (YelpDocInfo *doc1, YelpDocInfo *doc2)
return equal;
}
-void
-yelp_doc_page_free (YelpDocPage *page)
-{
- if (!page)
- return;
-
- g_free (page->page_id);
- g_free (page->title);
- g_free (page->contents);
-
- g_free (page->prev_id);
- g_free (page->next_id);
- g_free (page->toc_id);
-
- g_free (page);
-}
-
gchar *
yelp_uri_get_fragment (const gchar *uri)
{
diff --git a/src/yelp-utils.h b/src/yelp-utils.h
index a2fede29..8794d364 100644
--- a/src/yelp-utils.h
+++ b/src/yelp-utils.h
@@ -26,7 +26,6 @@
#include <glib/gi18n.h>
typedef struct _YelpDocInfo YelpDocInfo;
-typedef struct _YelpDocPage YelpDocPage;
typedef enum {
YELP_DOC_TYPE_ERROR = 0,
@@ -65,17 +64,6 @@ typedef enum {
#include "yelp-pager.h"
-struct _YelpDocPage {
- YelpDocInfo *document;
- gchar *page_id;
- gchar *title;
- gchar *contents;
-
- gchar *prev_id;
- gchar *next_id;
- gchar *toc_id;
-};
-
const char * yelp_dot_dir (void);
YelpDocInfo * yelp_doc_info_new (const gchar *uri,
gboolean trust_uri);
@@ -118,8 +106,6 @@ gchar * yelp_doc_info_get_filename (YelpDocInfo *doc);
gboolean yelp_doc_info_equal (YelpDocInfo *doc1,
YelpDocInfo *doc2);
-void yelp_doc_page_free (YelpDocPage *page);
-
gchar * yelp_uri_get_fragment (const gchar *uri);
gchar * yelp_uri_get_relative (gchar *base,
gchar *ref);