diff options
author | Shaun McCance <shaunm@src.gnome.org> | 2007-04-22 20:09:11 +0000 |
---|---|---|
committer | Shaun McCance <shaunm@src.gnome.org> | 2007-04-22 20:09:11 +0000 |
commit | 3390d20854154de304273e1d9e9320b8f38ec327 (patch) | |
tree | 5e066d55caac1e05030a4b2770be6c7249c369e5 | |
parent | 388b2c3e65782f31d5c760aeb160eb560f929dfc (diff) | |
download | yelp-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-- | ChangeLog | 219 | ||||
-rw-r--r-- | src/Makefile.am | 93 | ||||
-rw-r--r-- | src/test-document.c | 130 | ||||
-rw-r--r-- | src/test-man-parser.c | 49 | ||||
-rw-r--r-- | src/test-page.c | 83 | ||||
-rw-r--r-- | src/test-transform.c | 163 | ||||
-rw-r--r-- | src/yelp-debug.h | 3 | ||||
-rw-r--r-- | src/yelp-docbook.c | 763 | ||||
-rw-r--r-- | src/yelp-docbook.h | 53 | ||||
-rw-r--r-- | src/yelp-document.c | 782 | ||||
-rw-r--r-- | src/yelp-document.h | 126 | ||||
-rw-r--r-- | src/yelp-error.c | 120 | ||||
-rw-r--r-- | src/yelp-error.h | 38 | ||||
-rw-r--r-- | src/yelp-io-channel.c | 2 | ||||
-rw-r--r-- | src/yelp-man-parser.c | 45 | ||||
-rw-r--r-- | src/yelp-man-parser.h | 4 | ||||
-rw-r--r-- | src/yelp-man.c | 481 | ||||
-rw-r--r-- | src/yelp-man.h | 53 | ||||
-rw-r--r-- | src/yelp-page.c | 147 | ||||
-rw-r--r-- | src/yelp-page.h | 87 | ||||
-rw-r--r-- | src/yelp-transform.c | 416 | ||||
-rw-r--r-- | src/yelp-transform.h | 81 | ||||
-rw-r--r-- | src/yelp-utils.c | 17 | ||||
-rw-r--r-- | src/yelp-utils.h | 14 |
24 files changed, 3789 insertions, 180 deletions
@@ -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); |