diff --git a/Makefile.lib b/Makefile.lib
index 7e102c3d4..4cedf9124 100644
--- a/Makefile.lib
+++ b/Makefile.lib
@@ -33,8 +33,8 @@ mcmd = @$(if $($(mquiet)cmd_$(1)),echo $($(mquiet)cmd_$(1)) &&) $(cmd_$(1))
ecmd = @$(if $($(mquiet)cmd_$(1)),printf "%-38s " $($(mquiet)cmd_$(1)) &&) $(cmd_$(1))
quiet_cmd_compile = ' [$(CC_COLOR)CC$(END_COLOR)] $(RELPATH)$@'
- masq_cmd_compile = $(COMPILE) -c $<
- cmd_compile = $(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+ masq_cmd_compile = $(COMPILE) -o $(@) -c $< $(2)
+ cmd_compile = $(COMPILE) -o $(@) -Wp,-MD,.deps/$(*F).pp -c $< $(2)
# Rule to compile a set of .o files into one .o file
quiet_cmd_ld_objs = " [$(LD_COLOR)LD$(END_COLOR)] $(RELPATH)$@"
@@ -153,6 +153,7 @@ clean-test:
test-default:
ifdef TEST_PROGS
+TESTDEPS-$(CONFIG_DEBUG) += $(top_builddir)/src/util/memdebug.o
TESTDEPS += $(TESTDEPS-yes)
TESTS = $(wildcard $(srcdir)test-*)
diff --git a/doc/Makefile b/doc/Makefile
index 08641d124..121961c23 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -14,6 +14,7 @@ TXT_DIR = $(top_srcdir)/doc/txt
DOC_DIRS = \
$(HTML_DIR) \
+ $(HTML_DIR)/api \
$(MAN_DIR)/man1 \
$(MAN_DIR)/man5 \
$(TXT_DIR) \
@@ -51,74 +52,43 @@ FEATURES = $(top_srcdir)/features.conf
### Scripts
#
+CODE2DOC = $(top_srcdir)/doc/tools/code2doc
HELP2DOC = $(top_srcdir)/doc/tools/help2doc
IMPORT_FEATURES_CONF = $(top_srcdir)/doc/tools/import-features.conf
MAKE_ELINKS_MANPAGE = $(top_srcdir)/doc/tools/make-elinks-manpage
MAKE_ELINKSKEYS_MANPAGE = $(top_srcdir)/doc/tools/make-elinkskeys-manpage
-ifeq ($(CONFIG_ASCIIDOC),yes)
-HTML_DOCS_WITH_ASCIIDOC = \
- $(HTML_DIR)/elinks.1.html \
- $(HTML_DIR)/elinkskeys.5.html \
- $(HTML_DIR)/hacking.html \
- $(HTML_DIR)/manual.html
-endif
+HTML_DOCS-$(CONFIG_ASCIIDOC) += \
+ api/dom-sgml-parser.html \
+ elinks.1.html \
+ elinkskeys.5.html \
+ hacking.html \
+ manual.html
+HTML_DOCS-$(CONFIG_XMLTO) += \
+ manual.html-chunked
+
+HTML_DOCS-$(CONFIG_POD2HTML) += \
+ perl.html \
+ perl-hooks.html
+
+MAN_DOCS-$(CONFIG_XMLTO) += \
+ man1/elinks.1.in \
+ man5/elinkskeys.5
# Only jw is used for generating PDF.
-ifeq ($(CONFIG_XMLTO),yes)
-HTML_DOCS_WITH_XMLTO = \
- $(HTML_DIR)/manual.html-chunked
+PDF_DOCS-$(CONFIG_JW) += \
+ manual.pdf
-MAN_DOCS_WITH_XMLTO = \
- $(MAN_DIR)/man1/elinks.1.in \
- $(MAN_DIR)/man5/elinkskeys.5
-endif
+MAN_DOCS += man5/elinks.conf.5
-# Only jw is used for generating PDF.
-ifeq ($(CONFIG_JW),yes)
-PDF_DOCS_WITH_JW = \
- $(PDF_DIR)/manual.pdf
-endif
+MAN_DOCS += $(MAN_DOCS-yes)
+HTML_DOCS += $(HTML_DOCS-yes)
+PDF_DOCS += $(PDF_DOCS-yes)
-ifeq ($(CONFIG_POD2HTML),yes)
-HTML_DOCS_WITH_POD2HTML = \
- $(HTML_DIR)/perl.html \
- $(HTML_DIR)/perl-hooks.html
-endif
-
-MAN_DOCS_WITH_SHELL = $(MAN_DIR)/man5/elinks.conf.5
-
-MAN_DOCS = \
- $(MAN_DOCS_WITH_SHELL)
- $(MAN_DOCS_WITH_ASCIIDOC)
-
-HTML_DOCS = \
- $(HTML_DOCS_WITH_ASCIIDOC) \
- $(HTML_DOCS_WITH_POD2HTML) \
- $(HTML_DOCS_WITH_JW)
-
-PDF_DOCS = \
- $(PDF_DOCS_WITH_JW)
-
-html-asciidoc-yes: doc-dirs $(HTML_DOCS_WITH_ASCIIDOC)
-html-asciidoc-no:
-
-html-pod2html-yes: doc-dirs $(HTML_DOCS_WITH_POD2HTML)
-html-pod2html-no:
-
-html-xmlto-yes: doc-dirs $(HTML_DOCS_WITH_XMLTO)
-html-xmlto-no:
-
-man-xmlto-yes: doc-dirs $(MAN_DOCS_WITH_XMLTO)
-man-xmlto-no:
-
-pdf-jw-yes: doc-dirs $(PDF_DOCS_WITH_JW)
-pdf-jw-no:
-
-man-docs: man-xmlto-$(CONFIG_XMLTO) $(MAN_DOCS_WITH_SHELL)
-html-docs: html-asciidoc-$(CONFIG_ASCIIDOC) html-xmlto-$(CONFIG_XMLTO) html-pod2html-$(CONFIG_POD2HTML)
-pdf-docs: pdf-jw-$(CONFIG_JW)
+man-docs: doc-dirs $(addprefix $(MAN_DIR)/,$(MAN_DOCS))
+html-docs: doc-dirs $(addprefix $(HTML_DIR)/,$(HTML_DOCS))
+pdf-docs: doc-dirs $(addprefix $(PDF_DIR)/,$(PDF_DOCS))
all-docs: man-docs html-docs pdf-docs
@@ -135,36 +105,36 @@ clean-local:
# Autogenerated asciidoc files.
-$(TXT_DIR)/import-features.conf.txt: $(FEATURES) doc-dirs $(IMPORT_FEATURES_CONF)
+$(TXT_DIR)/import-features.conf.txt: $(FEATURES) $(IMPORT_FEATURES_CONF)
$(IMPORT_FEATURES_CONF) > $@
-$(TXT_DIR)/elinks.1.%.txt: $(MAKE_ELINKS_MANPAGE) doc-dirs $(ELINKS)
+$(TXT_DIR)/elinks.1.%.txt: $(MAKE_ELINKS_MANPAGE) $(ELINKS)
$(MAKE_ELINKS_MANPAGE) $@ $(ELINKS) $(HELP2DOC) > $@
-$(TXT_DIR)/elinkskeys.5.%.txt: $(MAKE_ELINKSKEYS_MANPAGE) doc-dirs $(KBDBIND)
+$(TXT_DIR)/elinkskeys.5.%.txt: $(MAKE_ELINKSKEYS_MANPAGE) $(KBDBIND)
$(MAKE_ELINKSKEYS_MANPAGE) $@ $(KBDBIND) > $@
# Man Pages
-$(XML_DIR)/%.man.xml: $(TXT_DIR)/%.man.txt doc-dirs
+$(XML_DIR)/%.man.xml: $(TXT_DIR)/%.man.txt
$(ASCIIDOC) -b docbook -d manpage -o $@ $<
-$(MAN_DIR)/man1/elinks.1.in: $(XML_DIR)/elinks.1.man.xml doc-dirs
+$(MAN_DIR)/man1/elinks.1.in: $(XML_DIR)/elinks.1.man.xml
$(XMLTO) -o $(MAN_DIR)/man1 man $<
mv $(MAN_DIR)/man1/elinks.1 $@
-$(MAN_DIR)/man5/elinkskeys.5: $(XML_DIR)/elinkskeys.5.man.xml doc-dirs
+$(MAN_DIR)/man5/elinkskeys.5: $(XML_DIR)/elinkskeys.5.man.xml
$(XMLTO) -o $(MAN_DIR)/man5 man $<
sed -e 's/\\fI\\fR'\''/\\fI\\'\''\\fR/' < $@ > $@.tmp
mv $@.tmp $@
-$(MAN_DIR)/man5/elinks.conf.5: doc-dirs $(ELINKS)
+$(MAN_DIR)/man5/elinks.conf.5: $(ELINKS)
$(HELP2DOC) --elinks=$(ELINKS) --elinksconf > $@
# XHTML/CSS Man Pages
-$(HTML_DIR)/%.html: $(TXT_DIR)/%.html.txt doc-dirs
+$(HTML_DIR)/%.html: $(TXT_DIR)/%.html.txt
$(ASCIIDOC) -b xhtml11 -d manpage -o $@ $<
# The Manual
@@ -174,28 +144,34 @@ MANUAL_EXTRA_FILES = \
$(TXT_DIR)/elinks.1.html.txt \
$(TXT_DIR)/elinkskeys.5.html.txt
-$(HTML_DIR)/manual.html: $(MANUAL_FILES) doc-dirs $(MANUAL_EXTRA_FILES)
+$(HTML_DIR)/manual.html: $(MANUAL_FILES) $(MANUAL_EXTRA_FILES)
$(ASCIIDOC) -b xhtml11 -d book -o $@ -n $<
-$(HTML_DIR)/hacking.html: $(top_srcdir)/doc/hacking.txt doc-dirs
+$(HTML_DIR)/hacking.html: $(top_srcdir)/doc/hacking.txt
$(ASCIIDOC) -b xhtml11 -d book -o $@ -n $<
-$(HTML_DIR)/dev-intro.html: $(top_srcdir)/doc/dev-intro.txt doc-dirs
+$(HTML_DIR)/dev-intro.html: $(top_srcdir)/doc/dev-intro.txt
$(ASCIIDOC) -b xhtml11 -d book -o $@ -n $<
-$(XML_DIR)/manual.xml: $(MANUAL_FILES) doc-dirs $(MANUAL_EXTRA_FILES)
+$(XML_DIR)/manual.xml: $(MANUAL_FILES) $(MANUAL_EXTRA_FILES)
$(ASCIIDOC) -b docbook -d book -o $@ $<
-$(HTML_DIR)/manual.html-chunked: $(XML_DIR)/manual.xml doc-dirs
+$(HTML_DIR)/manual.html-chunked: $(XML_DIR)/manual.xml
$(XMLTO) -o $@ html $<
-$(PDF_DIR)/manual.pdf: $(XML_DIR)/manual.xml doc-dirs
+$(PDF_DIR)/manual.pdf: $(XML_DIR)/manual.xml
$(JW) -o $(PDF_DIR) -b pdf $<
-$(HTML_DIR)/perl.html: $(top_srcdir)/doc/perl.pod doc-dirs
+$(HTML_DIR)/perl.html: $(top_srcdir)/doc/perl.pod
$(POD2HTML) --outfile=$@ < $<
-$(HTML_DIR)/perl-hooks.html: $(top_srcdir)/contrib/perl/hooks.pl doc-dirs
+$(HTML_DIR)/perl-hooks.html: $(top_srcdir)/contrib/perl/hooks.pl
$(POD2HTML) --outfile=$@ < $<
+## API Docs
+#
+
+$(HTML_DIR)/api/dom-sgml-parser.html: $(top_srcdir)/src/dom/sgml/parser.h
+ $(CODE2DOC) $< | $(ASCIIDOC) -f code2doc.conf -b xhtml11 -d book -o $@ -n -
+
include $(top_srcdir)/Makefile.lib
diff --git a/doc/code2doc.conf b/doc/code2doc.conf
new file mode 100644
index 000000000..039f5a022
--- /dev/null
+++ b/doc/code2doc.conf
@@ -0,0 +1,52 @@
+[specialwords]
+emphasizedwords=\bAsciiDoc\b
+monospacedwords=\basciidoc\(1\)
+
+[id-inlinemacro]
+{0}
+
+[enum-inlinemacro]
+enum {0}
+
+[func-inlinemacro]
+{0}()
+
+[struct-inlinemacro]
+struct {0}
+
+[callback-inlinemacro]
+callback {0}
+
+[ref-inlinemacro]
+{0}
+
+[replacements]
+(^|[^-])--($|[^-])=\1--\2
+
+[tags]
+ilisttext=|
+olisttext=|
+vlisttext=|
+qlisttext=|
+colisttext=|
+
+[tags]
+title1=
|
+title2=|
+title3=|
+
+[literalparagraph]
+
+
+[listingblock]
+{title}
+
+
+[noteblock]
+
diff --git a/doc/tools/code2doc b/doc/tools/code2doc
new file mode 100755
index 000000000..d6b03202b
--- /dev/null
+++ b/doc/tools/code2doc
@@ -0,0 +1,46 @@
+#!/usr/bin/perl -w
+use strict;
+use warnings;
+use diagnostics;
+
+print "Usage: $0 [FILE]\n\tParses [FILE], outputing the result to stdout.\n"
+ and exit if not @ARGV;
+
+my ($input) = @ARGV;
+my ($found, $start, $first, $gotone, $idpath);
+print "CopyleftŠ 2006, Russ Rowan (See `COPYING')\n" and exit if $input eq '-v';
+open FILEIN, "<$input" or print "File `$input' was not found.\n" and exit;
+$idpath = '';
+while ()
+{
+ if ($found)
+ {
+ if ($_ =~ /^\s+\*\s$/) { next if $first; $_ =~ s/\s\*// if not $first; }
+ if ($_ =~ /^\s\*+\/$/ or $_ !~ /^\s/) { $found = undef; next; }
+ $_ =~ s/^(\s*)\s\*\s/$1/;
+ $found = 'sorta' if $_ =~ s/\s*\*\/$/\n/; $first = undef;
+ }
+ elsif ($_ =~ /^\s*\/\*\*\s(.*)/)
+ {
+ $_ = $1; $first = 1;
+ print STDOUT "\n\n" if $start;
+ if ($_ =~ s/\s*\*\/$//) { $found = 'sorta'; } else { $found = $.; }
+ if ($_ =~ /struct:[[]([^\]]+)[\]]/) { $idpath = "$1."; } else { $idpath = ''; }
+ if ($_ =~ /::/) { $_ = "$_\n\n"; }
+ else
+ {
+ my $dash; for (my $x = 0; $x < length($_); $x++) { $dash .= '-'; }
+ $_ = "$_\n$dash\n\n";
+ }
+ }
+ elsif ($_ =~ /^(\s|[^\s=]+)*[\s*]([A-Za-z0-9_]+)(\s+=\s+[^,;]+)?[,;]\s*\/\*::\s*(.*)\s+\*\/$/)
+ {
+ print STDOUT "\n" if $gotone;
+ $_ = "\nid:[$idpath$2]::\n\t$4\n";
+ $found = 'sorta'; $gotone = $.;
+ }
+ print STDOUT "\n" and $gotone = undef if $gotone and $gotone < $.;
+ next if not $found; $found = undef if $found eq 'sorta';
+ print STDOUT $_ and $start = 1;
+}
+close FILEIN;
diff --git a/src/bfu/hotkey.c b/src/bfu/hotkey.c
index 42222c449..0c3078dec 100644
--- a/src/bfu/hotkey.c
+++ b/src/bfu/hotkey.c
@@ -121,52 +121,9 @@ refresh_hotkeys(struct terminal *term, struct menu *menu)
#endif
}
-/* Returns true if key (upcased) matches one of the hotkeys in menu */
-static int
-is_hotkey(struct menu_item *item, unsigned char key, struct terminal *term)
-{
- unsigned char *text;
- int key_pos;
-
- assert(item);
- if_assert_failed return 0;
-
- if (!mi_has_left_text(item)) return 0;
-
- text = item->text;
- if (mi_text_translate(item)) text = _(text, term);
- if (!text || !*text) return 0;
-
- key_pos = item->hotkey_pos;
-
-#ifdef CONFIG_DEBUG
- if (key_pos < 0) key_pos = -key_pos;
-#endif
-
- return (key_pos && (toupper(text[key_pos]) == key));
-}
-
-/* Returns true if key (upcased) matches first letter of menu item left text. */
-static int
-is_not_so_hotkey(struct menu_item *item, unsigned char key, struct terminal *term)
-{
- unsigned char *text;
-
- assert(item);
- if_assert_failed return 0;
-
- if (!mi_has_left_text(item)) return 0;
-
- text = item->text;
- if (mi_text_translate(item)) text = _(text, term);
- if (!text || !*text) return 0;
-
- return (toupper(*text) == key);
-}
-
static int
check_hotkeys_common(struct menu *menu, unsigned char hotkey, struct terminal *term,
- int (*func)(struct menu_item *, unsigned char, struct terminal *))
+ int check_mode)
{
unsigned char key = toupper(hotkey);
int i = menu->selected;
@@ -179,9 +136,37 @@ check_hotkeys_common(struct menu *menu, unsigned char hotkey, struct terminal *t
start = i;
do {
+ struct menu_item *item;
+ unsigned char *text;
+ int found;
+
if (++i == menu->size) i = 0;
- if (func(&menu->items[i], key, term)) {
+ item = &menu->items[i];
+
+ if (!mi_has_left_text(item)) continue;
+
+ text = item->text;
+ if (mi_text_translate(item)) text = _(text, term);
+ if (!text || !*text) continue;
+
+ if (check_mode == 0) {
+ /* Does the key (upcased) matches one of the
+ * hotkeys in menu ? */
+ int key_pos = item->hotkey_pos;
+
+#ifdef CONFIG_DEBUG
+ if (key_pos < 0) key_pos = -key_pos;
+#endif
+ found = (key_pos && (toupper(text[key_pos]) == key));
+
+ } else {
+ /* Does the key (upcased) matches first letter
+ * of menu item left text ? */
+ found = (toupper(*text) == key);
+ }
+
+ if (found) {
menu->selected = i;
return 1;
}
@@ -195,7 +180,7 @@ check_hotkeys_common(struct menu *menu, unsigned char hotkey, struct terminal *t
int
check_hotkeys(struct menu *menu, unsigned char key, struct terminal *term)
{
- return check_hotkeys_common(menu, key, term, is_hotkey);
+ return check_hotkeys_common(menu, key, term, 0);
}
/* Search if first letter of an entry in menu matches the key (caseless comp.).
@@ -205,5 +190,5 @@ check_hotkeys(struct menu *menu, unsigned char key, struct terminal *term)
int
check_not_so_hot_keys(struct menu *menu, unsigned char key, struct terminal *term)
{
- return check_hotkeys_common(menu, key, term, is_not_so_hotkey);
+ return check_hotkeys_common(menu, key, term, 1);
}
diff --git a/src/document/dom/renderer.c b/src/document/dom/renderer.c
index 0415d3516..70b022b1d 100644
--- a/src/document/dom/renderer.c
+++ b/src/document/dom/renderer.c
@@ -24,6 +24,7 @@
#include "document/renderer.h"
#include "dom/scanner.h"
#include "dom/sgml/parser.h"
+#include "dom/sgml/rss/rss.h"
#include "dom/node.h"
#include "dom/stack.h"
#include "intl/charsets.h"
@@ -54,6 +55,13 @@ struct dom_renderer {
unsigned int find_url:1;
#endif
struct screen_char styles[DOM_NODES];
+
+ /* RSS renderer variables */
+ struct dom_node *channel;
+ struct dom_node_list *items;
+ struct dom_node *item;
+ struct dom_node *node;
+ struct dom_string text;
};
#define URL_REGEX "(file://|((f|ht|nt)tp(s)?|smb)://[[:alnum:]]+([-@:.]?[[:alnum:]])*\\.[[:alpha:]]{2,4}(:[[:digit:]]+)?)(/(%[[:xdigit:]]{2}|[-_~&=;?.a-z0-9])*)*"
@@ -334,7 +342,8 @@ render_dom_text(struct dom_renderer *renderer, struct screen_char *template,
ALIGN_LINK(&(doc)->links, (doc)->nlinks, size)
static inline struct link *
-add_dom_link(struct dom_renderer *renderer, unsigned char *string, int length)
+add_dom_link(struct dom_renderer *renderer, unsigned char *string, int length,
+ unsigned char *uristring, int urilength)
{
struct document *document = renderer->document;
int x = renderer->canvas_x;
@@ -343,7 +352,6 @@ add_dom_link(struct dom_renderer *renderer, unsigned char *string, int length)
struct link *link;
struct point *point;
struct screen_char template;
- unsigned char *uristring;
color_T fgcolor;
if (!realloc_document_links(document, document->nlinks + 1))
@@ -355,7 +363,7 @@ add_dom_link(struct dom_renderer *renderer, unsigned char *string, int length)
return NULL;
uristring = convert_string(renderer->convert_table,
- string, length, document->options.cp,
+ uristring, urilength, document->options.cp,
CSM_DEFAULT, NULL, NULL, NULL);
if (!uristring) return NULL;
@@ -479,7 +487,7 @@ render_dom_node_enhanced_text(struct dom_renderer *renderer, struct dom_node *no
string += offset;
length -= offset;
- add_dom_link(renderer, string, matchlen);
+ add_dom_link(renderer, string, matchlen, string, matchlen);
length -= matchlen;
string += matchlen;
@@ -601,7 +609,8 @@ render_dom_attribute_source(struct dom_stack *stack, struct dom_node *node, void
break;
}
- add_dom_link(renderer, value, valuelen - skips);
+ add_dom_link(renderer, value, valuelen - skips,
+ value, valuelen - skips);
if (skips > 0) {
value += valuelen - skips;
@@ -682,6 +691,272 @@ static struct dom_stack_context_info dom_source_renderer_context_info = {
};
+/* DOM RSS Renderer */
+
+static void
+dom_rss_push_element(struct dom_stack *stack, struct dom_node *node, void *data)
+{
+ struct dom_renderer *renderer = stack->current->data;
+
+ assert(node && renderer && renderer->document);
+
+ switch (node->data.element.type) {
+ case RSS_ELEMENT_CHANNEL:
+ /* The stack should have: #document * channel */
+ if (stack->depth != 3)
+ break;
+
+ if (!renderer->channel) {
+ renderer->channel = node;
+ }
+ break;
+
+ case RSS_ELEMENT_ITEM:
+ /* The stack should have: #document * channel item */
+#if 0
+ /* Don't be so strict ... */
+ if (stack->depth != 4)
+ break;
+#endif
+ /* ... but be exclusive. */
+ if (renderer->item)
+ break;
+ add_to_dom_node_list(&renderer->items, node, -1);
+ renderer->item = node;
+ break;
+
+ case RSS_ELEMENT_LINK:
+ case RSS_ELEMENT_DESCRIPTION:
+ case RSS_ELEMENT_TITLE:
+ case RSS_ELEMENT_AUTHOR:
+ case RSS_ELEMENT_PUBDATE:
+ if (!node->parent || renderer->node != node->parent)
+ break;
+
+ renderer->node = node;
+ }
+}
+
+static void
+dom_rss_pop_element(struct dom_stack *stack, struct dom_node *node, void *data)
+{
+ struct dom_renderer *renderer = stack->current->data;
+ struct dom_node_list **list;
+
+ assert(node && renderer && renderer->document);
+
+ switch (node->data.element.type) {
+ case RSS_ELEMENT_ITEM:
+ if (is_dom_string_set(&renderer->text))
+ done_dom_string(&renderer->text);
+ renderer->item = NULL;
+ break;
+
+ case RSS_ELEMENT_LINK:
+ case RSS_ELEMENT_DESCRIPTION:
+ case RSS_ELEMENT_TITLE:
+ case RSS_ELEMENT_AUTHOR:
+ case RSS_ELEMENT_PUBDATE:
+ if (!is_dom_string_set(&renderer->text)
+ || !node->parent
+ || renderer->item != node->parent
+ || renderer->node != node)
+ break;
+
+ /* Replace any child nodes with the normalized text node. */
+ list = get_dom_node_list(node->parent, node);
+ done_dom_node_list(*list);
+ if (is_dom_string_set(&renderer->text)) {
+ if (!add_dom_node(node, DOM_NODE_TEXT, &renderer->text))
+ done_dom_string(&renderer->text);
+ }
+ renderer->node = NULL;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+dom_rss_push_content(struct dom_stack *stack, struct dom_node *node, void *data)
+{
+ struct dom_renderer *renderer = stack->current->data;
+ unsigned char *string = node->string.string;
+ int length = node->string.length;
+
+ assert(node && renderer && renderer->document);
+
+ if (!renderer->node)
+ return;
+
+ if (node->type == DOM_NODE_ENTITY_REFERENCE) {
+ string -= 1;
+ length += 2;
+ }
+
+ if (!is_dom_string_set(&renderer->text)) {
+ init_dom_string(&renderer->text, string, length);
+ } else {
+ add_to_dom_string(&renderer->text, string, length);
+ }
+}
+
+static struct dom_string *
+get_rss_node_text(struct dom_node *node)
+{
+ struct dom_node *child;
+ int index;
+
+ if (!node->data.element.children)
+ return NULL;
+
+ foreach_dom_node (node->data.element.children, child, index) {
+ if (child->type == DOM_NODE_TEXT)
+ return &child->string;
+ }
+
+ return NULL;
+}
+
+static struct dom_node *
+get_rss_child(struct dom_node *parent, enum rss_element_type type)
+{
+ struct dom_node *node;
+ int index;
+
+ if (!parent->data.element.children)
+ return NULL;
+
+ foreach_dom_node (parent->data.element.children, node, index) {
+ if (node->type == DOM_NODE_ELEMENT
+ && type == node->data.element.type)
+ return node;
+ }
+
+ return NULL;
+}
+
+
+static struct dom_string *
+get_rss_text(struct dom_node *node, enum rss_element_type type)
+{
+ node = get_rss_child(node, type);
+
+ return node ? get_rss_node_text(node) : NULL;
+}
+
+static void
+render_rss_item(struct dom_renderer *renderer, struct dom_node *item)
+{
+ struct dom_string *title = get_rss_text(item, RSS_ELEMENT_TITLE);
+ struct dom_string *link = get_rss_text(item, RSS_ELEMENT_LINK);
+ struct dom_string *author = get_rss_text(item, RSS_ELEMENT_AUTHOR);
+ struct dom_string *date = get_rss_text(item, RSS_ELEMENT_PUBDATE);
+
+ if (title && is_dom_string_set(title)) {
+ render_dom_text(renderer, &renderer->styles[DOM_NODE_ELEMENT],
+ title->string, title->length);
+ }
+
+ if (link && is_dom_string_set(link)) {
+ X(renderer)++;
+ add_dom_link(renderer, "[link]", 6, link->string, link->length);
+ }
+
+ /* New line, and indent */
+ Y(renderer)++;
+ X(renderer) = 0;
+
+ if (author && is_dom_string_set(author)) {
+ render_dom_text(renderer, &renderer->styles[DOM_NODE_COMMENT],
+ author->string, author->length);
+ }
+
+ if (date && is_dom_string_set(date)) {
+ if (author && is_dom_string_set(author)) {
+ render_dom_text(renderer, &renderer->styles[DOM_NODE_COMMENT],
+ " - ", 3);
+ }
+
+ render_dom_text(renderer, &renderer->styles[DOM_NODE_COMMENT],
+ date->string, date->length);
+ }
+
+ if ((author && is_dom_string_set(author))
+ || (date && is_dom_string_set(date))) {
+ /* New line, and indent */
+ Y(renderer)++;
+ X(renderer) = 0;
+ }
+}
+
+static void
+dom_rss_pop_document(struct dom_stack *stack, struct dom_node *root, void *data)
+{
+ struct dom_renderer *renderer = stack->current->data;
+
+ if (!renderer->channel)
+ return;
+
+ render_rss_item(renderer, renderer->channel);
+
+ if (renderer->items) {
+ struct dom_node *node;
+ int index;
+
+ foreach_dom_node (renderer->items, node, index) {
+ Y(renderer)++;
+ X(renderer) = 0;
+ render_rss_item(renderer, node);
+ }
+ }
+
+ if (is_dom_string_set(&renderer->text))
+ done_dom_string(&renderer->text);
+ mem_free_if(renderer->items);
+
+ done_dom_node(root);
+}
+
+
+static struct dom_stack_context_info dom_rss_renderer_context_info = {
+ /* Object size: */ 0,
+ /* Push: */
+ {
+ /* */ NULL,
+ /* DOM_NODE_ELEMENT */ dom_rss_push_element,
+ /* DOM_NODE_ATTRIBUTE */ NULL,
+ /* DOM_NODE_TEXT */ dom_rss_push_content,
+ /* DOM_NODE_CDATA_SECTION */ dom_rss_push_content,
+ /* DOM_NODE_ENTITY_REFERENCE */ dom_rss_push_content,
+ /* DOM_NODE_ENTITY */ NULL,
+ /* DOM_NODE_PROC_INSTRUCTION */ NULL,
+ /* DOM_NODE_COMMENT */ NULL,
+ /* DOM_NODE_DOCUMENT */ NULL,
+ /* DOM_NODE_DOCUMENT_TYPE */ NULL,
+ /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL,
+ /* DOM_NODE_NOTATION */ NULL,
+ },
+ /* Pop: */
+ {
+ /* */ NULL,
+ /* DOM_NODE_ELEMENT */ dom_rss_pop_element,
+ /* DOM_NODE_ATTRIBUTE */ NULL,
+ /* DOM_NODE_TEXT */ NULL,
+ /* DOM_NODE_CDATA_SECTION */ NULL,
+ /* DOM_NODE_ENTITY_REFERENCE */ NULL,
+ /* DOM_NODE_ENTITY */ NULL,
+ /* DOM_NODE_PROC_INSTRUCTION */ NULL,
+ /* DOM_NODE_COMMENT */ NULL,
+ /* DOM_NODE_DOCUMENT */ dom_rss_pop_document,
+ /* DOM_NODE_DOCUMENT_TYPE */ NULL,
+ /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL,
+ /* DOM_NODE_NOTATION */ NULL,
+ }
+};
+
+
/* Shared multiplexor between renderers */
void
render_dom_document(struct cache_entry *cached, struct document *document,
@@ -692,14 +967,13 @@ render_dom_document(struct cache_entry *cached, struct document *document,
struct conv_table *convert_table;
struct sgml_parser *parser;
enum sgml_document_type doctype;
+ enum sgml_parser_type parser_type;
unsigned char *string = struri(cached->uri);
size_t length = strlen(string);
struct dom_string uri = INIT_DOM_STRING(string, length);
struct dom_string source = INIT_DOM_STRING(buffer->source, buffer->length);
enum sgml_parser_code code;
- assert(document->options.plain);
-
convert_table = get_convert_table(head, document->options.cp,
document->options.assume_cp,
&document->cp,
@@ -710,6 +984,11 @@ render_dom_document(struct cache_entry *cached, struct document *document,
document->bgcolor = document->options.default_bg;
+ if (document->options.plain)
+ parser_type = SGML_PARSER_STREAM;
+ else
+ parser_type = SGML_PARSER_TREE;
+
/* FIXME: Refactor the doctype lookup. */
if (!strcasecmp("application/rss+xml", cached->content_type)) {
doctype = SGML_DOCTYPE_RSS;
@@ -730,11 +1009,17 @@ render_dom_document(struct cache_entry *cached, struct document *document,
doctype = SGML_DOCTYPE_HTML;
}
- parser = init_sgml_parser(SGML_PARSER_STREAM, doctype, &uri, 0);
- if (!parser) return;
+ parser = init_sgml_parser(parser_type, doctype, &uri, 0);
+ if (!parser) return;
+
+ if (document->options.plain) {
+ add_dom_stack_context(&parser->stack, &renderer,
+ &dom_source_renderer_context_info);
- add_dom_stack_context(&parser->stack, &renderer,
- &dom_source_renderer_context_info);
+ } else if (doctype == SGML_DOCTYPE_RSS) {
+ add_dom_stack_context(&parser->stack, &renderer,
+ &dom_rss_renderer_context_info);
+ }
/* FIXME: When rendering this way we don't really care about the code.
* However, it will be useful when we will be able to also
diff --git a/src/document/renderer.c b/src/document/renderer.c
index 6c3a1d81a..ad458caa4 100644
--- a/src/document/renderer.c
+++ b/src/document/renderer.c
@@ -254,7 +254,13 @@ render_encoded_document(struct cache_entry *cached, struct document *document)
render_plain_document(cached, document, &buffer);
} else {
- render_html_document(cached, document, &buffer);
+#ifdef CONFIG_DOM
+ if (cached->content_type
+ && (!strlcasecmp("application/rss+xml", 19, cached->content_type, -1)))
+ render_dom_document(cached, document, &buffer);
+ else
+#endif
+ render_html_document(cached, document, &buffer);
}
if (encoding != ENCODING_NONE) {
diff --git a/src/dom/scanner.c b/src/dom/scanner.c
index aea8e2d18..c3cd1baa4 100644
--- a/src/dom/scanner.c
+++ b/src/dom/scanner.c
@@ -155,7 +155,7 @@ init_dom_scanner_info(struct dom_scanner_info *scanner_info)
void
init_dom_scanner(struct dom_scanner *scanner, struct dom_scanner_info *scanner_info,
struct dom_string *string, int state, int count_lines, int complete,
- int check_complete)
+ int check_complete, int detect_errors)
{
if (!scanner_info->initialized) {
init_dom_scanner_info(scanner_info);
@@ -173,6 +173,7 @@ init_dom_scanner(struct dom_scanner *scanner, struct dom_scanner_info *scanner_i
scanner->count_lines = !!count_lines;
scanner->incomplete = !complete;
scanner->check_complete = !!check_complete;
+ scanner->detect_errors = !!detect_errors;
scanner->lineno = scanner->count_lines;
scanner->info->scan(scanner);
}
diff --git a/src/dom/scanner.h b/src/dom/scanner.h
index cb9906d44..eabe95746 100644
--- a/src/dom/scanner.h
+++ b/src/dom/scanner.h
@@ -20,6 +20,9 @@ struct dom_scanner_token {
/* Some precedence value */
int precedence;
+ /* The line number; used for error tokens */
+ unsigned int lineno;
+
/* The start of the token string and the token length */
struct dom_string string;
};
@@ -93,7 +96,7 @@ struct dom_scanner_info {
/* Initializes the scanner. */
void init_dom_scanner(struct dom_scanner *scanner, struct dom_scanner_info *scanner_info,
struct dom_string *string, int state, int count_lines, int complete,
- int check_complete);
+ int check_complete, int detect_error);
/* The number of tokens in the scanners token table:
* At best it should be big enough to contain properties with space separated
@@ -130,6 +133,9 @@ struct dom_scanner {
unsigned int check_complete:1; /* Only generate complete tokens */
unsigned int incomplete:1; /* The scanned string is incomplete */
+ unsigned int detect_errors:1; /* Check for markup errors */
+ unsigned int found_error; /* Did we already report this error? */
+
unsigned int count_lines:1; /* Is line counting enbaled? */
unsigned int lineno; /* Line # of the last scanned token */
diff --git a/src/dom/select.c b/src/dom/select.c
index 1c53f61c3..a0e5d7644 100644
--- a/src/dom/select.c
+++ b/src/dom/select.c
@@ -391,7 +391,7 @@ parse_dom_select(struct dom_select *select, struct dom_stack *stack,
struct dom_scanner scanner;
struct dom_select_node sel;
- init_dom_scanner(&scanner, &dom_css_scanner_info, string, 0, 0, 1, 0);
+ init_dom_scanner(&scanner, &dom_css_scanner_info, string, 0, 0, 1, 0, 0);
memset(&sel, 0, sizeof(sel));
diff --git a/src/dom/sgml/parser.c b/src/dom/sgml/parser.c
index 0c5b4a3b4..565177e00 100644
--- a/src/dom/sgml/parser.c
+++ b/src/dom/sgml/parser.c
@@ -154,6 +154,17 @@ add_sgml_node(struct dom_stack *stack, enum dom_node_type type, struct dom_scann
/* SGML parser main handling: */
+static enum sgml_parser_code
+call_sgml_error_function(struct dom_stack *stack, struct dom_scanner_token *token)
+{
+ struct sgml_parser *parser = get_sgml_parser(stack);
+ unsigned int line = get_sgml_parser_line_number(parser);
+
+ assert(parser->error_func);
+
+ return parser->error_func(parser, &token->string, line);
+}
+
static inline enum sgml_parser_code
parse_sgml_attributes(struct dom_stack *stack, struct dom_scanner *scanner)
{
@@ -217,6 +228,17 @@ parse_sgml_attributes(struct dom_stack *stack, struct dom_scanner *scanner)
case SGML_TOKEN_INCOMPLETE:
return SGML_PARSER_CODE_INCOMPLETE;
+ case SGML_TOKEN_ERROR:
+ {
+ enum sgml_parser_code code;
+
+ code = call_sgml_error_function(stack, token);
+ if (code != SGML_PARSER_CODE_OK)
+ return code;
+
+ skip_dom_scanner_token(scanner);
+ break;
+ }
default:
skip_dom_scanner_token(scanner);
}
@@ -314,8 +336,13 @@ parse_sgml_plain(struct dom_stack *stack, struct dom_scanner *scanner)
if (!token || token->type == SGML_TOKEN_INCOMPLETE)
return SGML_PARSER_CODE_INCOMPLETE;
- assert(token->type == SGML_TOKEN_PROCESS_DATA);
+ if (token->type == SGML_TOKEN_ERROR)
+ break;
+ assert(token->type == SGML_TOKEN_PROCESS_DATA);
+ /* Fall-through */
+
+ case SGML_TOKEN_PROCESS_DATA:
if (add_sgml_proc_instruction(stack, &target, token)
&& (target.type == SGML_TOKEN_PROCESS_XML
|| target.type == SGML_TOKEN_PROCESS_XML_STYLESHEET)
@@ -326,7 +353,7 @@ parse_sgml_plain(struct dom_stack *stack, struct dom_scanner *scanner)
/* The attribute souce is complete. */
init_dom_scanner(&attr_scanner, &sgml_scanner_info,
&token->string, SGML_STATE_ELEMENT,
- scanner->count_lines, 1, 0);
+ scanner->count_lines, 1, 0, 0);
if (dom_scanner_has_tokens(&attr_scanner)) {
/* Ignore parser codes from this
@@ -350,6 +377,17 @@ parse_sgml_plain(struct dom_stack *stack, struct dom_scanner *scanner)
case SGML_TOKEN_INCOMPLETE:
return SGML_PARSER_CODE_INCOMPLETE;
+ case SGML_TOKEN_ERROR:
+ {
+ enum sgml_parser_code code;
+
+ code = call_sgml_error_function(stack, token);
+ if (code != SGML_PARSER_CODE_OK)
+ return code;
+
+ skip_dom_scanner_token(scanner);
+ break;
+ }
case SGML_TOKEN_SPACE:
case SGML_TOKEN_TEXT:
default:
@@ -403,11 +441,13 @@ sgml_parsing_push(struct dom_stack *stack, struct dom_node *node, void *data)
int count_lines = !!(parser->flags & SGML_PARSER_COUNT_LINES);
int complete = !!(parser->flags & SGML_PARSER_COMPLETE);
int incremental = !!(parser->flags & SGML_PARSER_INCREMENTAL);
+ int detect_errors = !!(parser->flags & SGML_PARSER_DETECT_ERRORS);
parsing->depth = parser->stack.depth;
get_dom_stack_top(&parser->stack)->immutable = 1;
init_dom_scanner(&parsing->scanner, &sgml_scanner_info, &node->string,
- SGML_STATE_TEXT, count_lines, complete, incremental);
+ SGML_STATE_TEXT, count_lines, complete, incremental,
+ detect_errors);
}
static void
@@ -494,6 +534,11 @@ get_sgml_parser_line_number(struct sgml_parser *parser)
assert(pstate->scanner.count_lines && pstate->scanner.lineno);
+ if (pstate->scanner.current
+ && pstate->scanner.current < pstate->scanner.table + DOM_SCANNER_TOKENS
+ && pstate->scanner.current->type == SGML_TOKEN_ERROR)
+ return pstate->scanner.current->lineno;
+
return pstate->scanner.lineno;
}
@@ -553,6 +598,9 @@ init_sgml_parser(enum sgml_parser_type type, enum sgml_document_type doctype,
return NULL;
}
+ if (flags & SGML_PARSER_DETECT_ERRORS)
+ flags |= SGML_PARSER_COUNT_LINES;
+
parser->type = type;
parser->flags = flags;
parser->info = get_sgml_info(doctype);
diff --git a/src/dom/sgml/parser.h b/src/dom/sgml/parser.h
index de836a9f8..dd3542632 100644
--- a/src/dom/sgml/parser.h
+++ b/src/dom/sgml/parser.h
@@ -7,69 +7,150 @@
#include "dom/sgml/sgml.h"
#include "dom/scanner.h"
+struct sgml_parser;
struct string;
struct uri;
+/** enum:[sgml_parser_type]: SGML parser type
+ *
+ * There are two kinds of parser types: One that optimises one-time access to
+ * the DOM tree and one that creates a persistent DOM tree. */
enum sgml_parser_type {
- /* The first one is a DOM tree builder. */
- SGML_PARSER_TREE,
- /* The second one will simply push nodes on the stack, not building a
+ /** id:[SGML_PARSER_STREAM]::
+ * The first one will simply push nodes on the stack, not building a
* DOM tree. This interface is similar to that of SAX (Simple API for
* XML) where events are fired when nodes are entered and exited. It is
* useful when you are not actually interested in the DOM tree, but can
* do all processing in a stream-like manner, such as when highlighting
* HTML code. */
SGML_PARSER_STREAM,
+ /** id:[SGML_PARSER_TREE]::
+ * The second one is a DOM tree builder, that builds a persistent DOM
+ * tree. When using this type, it is possible to do even more
+ * (pre)processing than for parser streams. For example you can sort
+ * element child nodes, or purge various node such as text nodes that
+ * only contain space characters. */
+ SGML_PARSER_TREE,
};
+/** enum:[sgml_parser_flag]: SGML parser flags
+ *
+ * These flags control how the parser behaves.
+ */
enum sgml_parser_flag {
- SGML_PARSER_COUNT_LINES = 1,
- SGML_PARSER_COMPLETE = 2,
- SGML_PARSER_INCREMENTAL = 4,
+ SGML_PARSER_COUNT_LINES = 1, /*:: Make line numbers available. */
+ SGML_PARSER_COMPLETE = 2, /*:: Used internally when incremental. */
+ SGML_PARSER_INCREMENTAL = 4, /*:: Parse chunks of input. */
+ SGML_PARSER_DETECT_ERRORS = 8, /*:: Report errors. */
};
+/** struct:[sgml_parser_state]: SGML parser state
+ *
+ * The SGML parser has only little state.
+ */
struct sgml_parser_state {
- /* Info about the properties of the node contained by state.
+ /** id:[sgml_parser_state.info]::
+ * Info about the properties of the node contained by state.
* This is only meaningful to element and attribute nodes. For
* unknown nodes it points to the common 'unknown node' info. */
struct sgml_node_info *info;
- /* This is used by the DOM source renderer for highlighting the
+ /** id:[sgml_parser_state.end_token]::
+ * This is used by the DOM source renderer for highlighting the
* end-tag of an element. */
struct dom_scanner_token end_token;
};
-struct sgml_parser {
- enum sgml_parser_type type; /* Stream or tree */
- enum sgml_parser_flag flags; /* Flags that control the behaviour */
-
- struct sgml_info *info; /* Backend dependent info */
-
- struct dom_string uri; /* The URI of the DOM document */
- struct dom_node *root; /* The document root node */
-
- struct dom_stack stack; /* A stack for tracking parsed nodes */
- struct dom_stack parsing; /* Used for tracking parsing states */
-};
-
-struct sgml_parser *
-init_sgml_parser(enum sgml_parser_type type, enum sgml_document_type doctype,
- struct dom_string *uri, enum sgml_parser_flag flags);
-
-void done_sgml_parser(struct sgml_parser *parser);
-
+/** enum:[sgml_parser_code]: (Error) codes for the SGML parser
+ *
+ * These enum values are used for return codes.
+ */
enum sgml_parser_code {
- SGML_PARSER_CODE_OK, /* The parsing was successful */
- SGML_PARSER_CODE_INCOMPLETE, /* The parsing could not be completed */
- SGML_PARSER_CODE_MEM_ALLOC, /* Failed to allocate memory */
+ SGML_PARSER_CODE_OK, /*:: The parsing was successful */
+ SGML_PARSER_CODE_INCOMPLETE, /*:: The parsing could not be completed */
+ SGML_PARSER_CODE_MEM_ALLOC, /*:: Failed to allocate memory */
- /* FIXME: For when we will add support for requiring stricter parsing
+ /** id:[SGML_PARSER_CODE_ERROR]::
+ * FIXME: For when we will add support for requiring stricter parsing
* or even a validator. */
SGML_PARSER_CODE_ERROR,
};
+/** callback:[sgml_error_T]: SGML error callback
+ *
+ * Called by the SGML parser when a parsing error has occurred.
+ *
+ * If the return code is not ref:[SGML_PARSER_CODE_OK] the parsing will be
+ * ended and that code will be returned. */
+typedef enum sgml_parser_code
+(*sgml_error_T)(struct sgml_parser *, struct dom_string *, unsigned int);
+
+
+/** struct:[sgml_parser]: The SGML parser
+ *
+ * This struct hold info used while parsing SGML data.
+ *
+ * NOTE: The only variable the user should set is ref:[error_func]. */
+struct sgml_parser {
+ enum sgml_parser_type type; /*:: Stream or tree */
+ enum sgml_parser_flag flags; /*:: Flags that control the behaviour */
+
+ struct sgml_info *info; /*:: Backend dependent info */
+
+ struct dom_string uri; /*:: The URI of the DOM document */
+ struct dom_node *root; /*:: The document root node */
+
+ sgml_error_T error_func; /*:: Called for detected errors */
+
+ struct dom_stack stack; /*:: A stack for tracking parsed nodes */
+ struct dom_stack parsing; /*:: Used for tracking parsing states */
+};
+
+
+/** func:[init_sgml_parser]: Initialise an SGML parser
+ *
+ * Initialise an SGML parser with the given properties.
+ *
+ * type:: Stream or tree; one-time or persistant.
+ * doctype:: The document type, this affects what sub type nodes are given.
+ * uri:: The URI of the document root.
+ * flags:: Flags controlling the behaviour of the parser.
+ *
+ * Returns the created parser or NULL.
+ */
+struct sgml_parser *
+init_sgml_parser(enum sgml_parser_type type, enum sgml_document_type doctype,
+ struct dom_string *uri, enum sgml_parser_flag flags);
+
+/** func:[done_sgml_parser]: Release an SGML parser
+ *
+ * Deallocates all resources, _expect_ the root node.
+ *
+ * parser:: The parser being released.
+ */
+void done_sgml_parser(struct sgml_parser *parser);
+
+/** func:[parse_sgml]: Parse a chunk of SGML source
+ *
+ * Parses the given `buffer`. For incremental rendering the last buffer can be
+ * signals through the `complete` parameter.
+ *
+ * parser:: A parser created with ref:[init_sgml_parser].
+ * buffer:: A string containing the chunk to parse.
+ * complete:: Whether this is the last chunk to parse.
+ *
+ * The returned code is ref:[SGML_PARSER_CODE_OK] if the buffer was
+ * successfully parserd, else a code hinting at the error.
+ */
enum sgml_parser_code
parse_sgml(struct sgml_parser *parser, struct dom_string *buffer, int complete);
+/** func:[get_sgml_parser_line_number]: Get the line position in the source
+ *
+ * Returns what line number the parser is currently at or zero if there has
+ * been no parsing yet.
+ *
+ * NOTE: Line numbers are recoderded in the scanner tokens.
+ */
unsigned int get_sgml_parser_line_number(struct sgml_parser *parser);
#endif
diff --git a/src/dom/sgml/scanner.c b/src/dom/sgml/scanner.c
index 6399b4395..a372c9a59 100644
--- a/src/dom/sgml/scanner.c
+++ b/src/dom/sgml/scanner.c
@@ -117,6 +117,86 @@ set_sgml_incomplete(struct dom_scanner *scanner, struct dom_scanner_token *token
scanner->position = scanner->end;
}
+
+static inline int
+check_sgml_error(struct dom_scanner *scanner)
+{
+ unsigned int found_error = scanner->found_error;
+
+ /* Toggle if we found an error previously. */
+ scanner->found_error = 0;
+
+ return scanner->detect_errors && !found_error;
+}
+
+static unsigned char *
+get_sgml_error_end(struct dom_scanner *scanner, enum sgml_token_type type,
+ unsigned char *end)
+{
+ switch (type) {
+ case SGML_TOKEN_CDATA_SECTION:
+ case SGML_TOKEN_NOTATION_ATTLIST:
+ case SGML_TOKEN_NOTATION_DOCTYPE:
+ case SGML_TOKEN_NOTATION_ELEMENT:
+ if (scanner->position + 9 < end)
+ end = scanner->position + 9;
+ break;
+
+ case SGML_TOKEN_NOTATION_COMMENT:
+ /* Just include the ' number ' \
-'
-1' \
---print-lines
-
-test_output_equals \
-'Check line numbers. (II)' \
-'<
-line:2
-line:3
-=
-"line:5"
->' \
-'
-10' \
---print-lines
-
-test_output_equals \
-'Check line numbers. (III)' \
-'1
-2
-3
-4
-5
-6
-7
-8' \
-'
-8' \
---print-lines
-
-test_expect_incomplete \
-'Check incomplete comment. (I)' \
-''
-
-test_expect_incomplete \
-'Check incomplete attribute.' \
-' output
+ echo "$out" | sed -n '2,$p' > expected
+
+ test_expect_success "$desc" 'cmp output expected'
+}
+
+
+################################################################
+# Check parsing errors
+
+test_output_error \
+'Check an element error.' \
+'' \
+'
+error on line 1: '
+
+test_output_error \
+'Check incomplete attribute.' \
+'/dev/null
+ test_expect_success "$desc" \
+ "test $? = 1"
+}
+
+
+################################################################
+# Check for incompleteness
+
+test_expect_incomplete \
+'Check incomplete comment. (I)' \
+''
+
+test_expect_incomplete \
+'Check incomplete attribute.' \
+' number ' \
+1
+
+test_output_line_numbers \
+'Check line numbers. (II)' \
+'<
+line:2
+line:3
+=
+"line:5"
+>' \
+10
+
+test_output_line_numbers \
+'Check line numbers. (III)' \
+'1
+2
+3
+4
+5
+6
+7
+8' \
+8
+
+test_done
diff --git a/src/mime/backend/Makefile b/src/mime/backend/Makefile
index b1e61a0c0..eb9300cc5 100644
--- a/src/mime/backend/Makefile
+++ b/src/mime/backend/Makefile
@@ -6,4 +6,26 @@ OBJS-$(CONFIG_MIMETYPES) += mimetypes.o
OBJS = common.o default.o
+TEST_PROGS = \
+ mailcap-cache
+
+# The dependencies are a bit funny here! I don't know why. Just remember to
+# make clean before making the test. --jonas
+mailcap-cache.o: mailcap.c
+ $(call cmd,compile,-DTEST_MAILCAP)
+
+TESTDEPS = \
+ common.o \
+ $(top_builddir)/src/osdep/osdep.o \
+ $(top_builddir)/src/osdep/stub.o \
+ $(top_builddir)/src/util/conv.o \
+ $(top_builddir)/src/util/error.o \
+ $(top_builddir)/src/util/file.o \
+ $(top_builddir)/src/util/hash.o \
+ $(top_builddir)/src/util/memory.o \
+ $(top_builddir)/src/util/string.o \
+ $(top_builddir)/src/util/time.o
+
+TESTDEPS-$(CONFIG_DEBUG) += $(top_builddir)/src/util/memdebug.o
+
include $(top_srcdir)/Makefile.lib
diff --git a/src/mime/backend/mailcap.c b/src/mime/backend/mailcap.c
index bf9daec38..e3eccec55 100644
--- a/src/mime/backend/mailcap.c
+++ b/src/mime/backend/mailcap.c
@@ -443,6 +443,8 @@ done_mailcap(struct module *module)
mailcap_map_size = 0;
}
+#ifndef TEST_MAILCAP
+
static int
change_hook_mailcap(struct session *ses, struct option *current, struct option *changed)
{
@@ -469,6 +471,10 @@ init_mailcap(struct module *module)
get_mailcap_enable() = 0;
}
+#else
+#define init_mailcap NULL
+#endif /* TEST_MAILCAP */
+
/* The command semantics include the following:
*
* %s is the filename that contains the mail body data
@@ -673,3 +679,108 @@ struct module mailcap_mime_module = struct_module(
/* init: */ init_mailcap,
/* done: */ done_mailcap
);
+
+#ifdef TEST_MAILCAP
+/* Some ugly shortcuts for getting defined symbols to work. */
+int default_mime_backend,
+ install_signal_handler,
+ mimetypes_mime_backend;
+struct list_head terminals;
+
+void die(const char *msg, ...)
+{
+ va_list args;
+
+ if (msg) {
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ fputs("\n", stderr);
+ va_end(args);
+ }
+
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ unsigned char *format = "description,ask,block,program";
+ int has_gotten = 0;
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ char *arg = argv[i];
+
+ if (strncmp(arg, "--", 2))
+ break;
+
+ arg += 2;
+
+ if (!strncmp(arg, "path", 4)) {
+ arg += 4;
+ if (*arg == '=') {
+ arg++;
+ get_mailcap_path() = arg;
+ } else {
+ i++;
+ if (i >= argc)
+ die("--path expects a parameter");
+ get_mailcap_path() = argv[i];
+ }
+ done_mailcap(NULL);
+
+ } else if (!strncmp(arg, "format", 6)) {
+ arg += 6;
+ if (*arg == '=') {
+ arg++;
+ format = arg;
+ } else {
+ i++;
+ if (i >= argc)
+ die("--format expects a parameter");
+ format = argv[i];
+ }
+
+ } else if (!strncmp(arg, "get", 3)) {
+ struct mime_handler *handler;
+
+ arg += 3;
+ if (*arg == '=') {
+ arg++;
+ } else {
+ i++;
+ if (i >= argc)
+ die("--get expects a parameter");
+ arg = argv[i];
+ }
+
+ if (has_gotten)
+ printf("\n");
+ has_gotten = 1;
+ printf("type: %s\n", arg);
+ handler = get_mime_handler_mailcap(arg, 0);
+ if (!handler) continue;
+
+ if (strstr(format, "description"))
+ printf("description: %s\n", handler->description);
+
+ if (strstr(format, "ask"))
+ printf("ask: %d\n", handler->ask);
+
+ if (strstr(format, "block"))
+ printf("block: %d\n", handler->block);
+
+ if (strstr(format, "program"))
+ printf("program: %s\n", handler->program);
+
+ } else {
+ die("Unknown argument '%s'", arg - 2);
+ }
+ }
+
+ done_mailcap(NULL);
+
+ return 0;
+}
+
+#endif /* TEST_MAILCAP */
diff --git a/src/mime/backend/test-mailcap-cache b/src/mime/backend/test-mailcap-cache
new file mode 100755
index 000000000..5440066c3
--- /dev/null
+++ b/src/mime/backend/test-mailcap-cache
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Jonas Fonseca
+#
+
+test_description='Test mailcap parsing and querying
+
+This tests the parsing of various mailcap files, if they are
+"prioritised" correctly, if the test are run correctly and
+if querying returns the expected mailcap entry.
+'
+
+. "$TEST_LIB"
+
+# Set PAGER to something recognisable since it gets appended as
+# "|copiousoutput_handler" to entries with copiousoutput.
+export PAGER=copiousoutput_handler
+
+################################################################
+# Parse a simple mailcap file
+
+cat > mailcap-basic <&1 ; copiousoutput
+EOF
+
+mailcap-cache \
+ --path "mailcap-basic" \
+ --format "block,program" \
+ --get "text/html" \
+ --get "text/x-csh" \
+ --get "application/postscript" \
+ --get "application/foo" \
+ > output
+
+cat > expected < mailcap-simple-with-test < output
+
+cat > expected <= 0) return namelen;
+ if (exists) {
+ return pos - name;
+ }
}
return -1;
@@ -147,29 +150,15 @@ check_uri_file(unsigned char *name)
{
/* Check POST_CHAR etc ... */
static const unsigned char chars[] = POST_CHAR_S "#?";
- int i;
- for (i = 0; i < sizeof(chars) - 1; i++) {
- unsigned char *pos = strchr(name, chars[i]);
- int namelen;
-
- if (!pos) continue;
-
- *pos = 0;
- namelen = strlen(name);
- *pos = chars[i];
-
- return namelen;
- }
-
- return strlen(name);
+ return strcspn(name, chars);
}
/* Encodes URIs without encoding stuff like fragments and query separators. */
static void
encode_file_uri_string(struct string *string, unsigned char *uristring)
{
- int filenamelen = check_uri_file(uristring);
+ int filenamelen = check_whether_file_exists(uristring);
encode_uri_string(string, uristring, filenamelen, 0);
}
diff --git a/src/session/download.c b/src/session/download.c
index d438011fb..7c0714dad 100644
--- a/src/session/download.c
+++ b/src/session/download.c
@@ -986,6 +986,25 @@ tp_open(struct type_query *type_query)
return;
}
+ if (type_query->uri->protocol == PROTOCOL_FILE) {
+ unsigned char *file = get_uri_string(type_query->uri, URI_PATH);
+ unsigned char *handler = NULL;
+
+ if (file) {
+ handler = subst_file(type_query->external_handler, file);
+ mem_free(file);
+ }
+
+ if (handler) {
+ exec_on_terminal(type_query->ses->tab->term,
+ handler, "", !!type_query->block);
+ mem_free(handler);
+ }
+
+ done_type_query(type_query);
+ return;
+ }
+
continue_download(type_query, "");
}
@@ -1154,7 +1173,7 @@ struct {
{ "application/xhtml+xml", 0 }, /* RFC 3236 */
#if CONFIG_DOM
{ "application/docbook+xml", 1 },
- { "application/rss+xml", 1 },
+ { "application/rss+xml", 0 },
{ "application/xbel+xml", 1 },
{ "application/xbel", 1 },
{ "application/x-xbel", 1 },
diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c
index 895e89806..51ae23790 100644
--- a/src/terminal/terminal.c
+++ b/src/terminal/terminal.c
@@ -197,91 +197,103 @@ unblock_terminal(struct terminal *term)
textarea_edit(1, NULL, NULL, NULL, NULL);
}
+
+static void
+exec_on_master_terminal(struct terminal *term,
+ unsigned char *path, int plen,
+ unsigned char *delete, int dlen,
+ int fg)
+{
+ int blockh;
+ int param_size = plen + dlen + 2 /* 2 null char */ + 1 /* fg */;
+ unsigned char *param = fmem_alloc(param_size);
+
+ if (!param) return;
+
+ param[0] = fg;
+ memcpy(param + 1, path, plen + 1);
+ memcpy(param + 1 + plen + 1, delete, dlen + 1);
+
+ if (fg == 1) block_itrm(term->fdin);
+
+ blockh = start_thread((void (*)(void *, int)) exec_thread,
+ param, param_size);
+ fmem_free(param);
+ if (blockh == -1) {
+ if (fg == 1) unblock_itrm(term->fdin);
+ return;
+ }
+
+ if (fg == 1) {
+ term->blocked = blockh;
+ set_handlers(blockh,
+ (select_handler_T) unblock_terminal,
+ NULL,
+ (select_handler_T) unblock_terminal,
+ term);
+ set_handlers(term->fdin, NULL, NULL,
+ (select_handler_T) destroy_terminal,
+ term);
+
+ } else {
+ set_handlers(blockh, close_handle, NULL,
+ close_handle, (void *) (long) blockh);
+ }
+}
+
+static void
+exec_on_slave_terminal( struct terminal *term,
+ unsigned char *path, int plen,
+ unsigned char *delete, int dlen,
+ int fg)
+{
+ int data_size = plen + dlen + 1 /* 0 */ + 1 /* fg */ + 2 /* 2 null char */;
+ unsigned char *data = fmem_alloc(data_size);
+
+ if (!data) return;
+
+ data[0] = 0;
+ data[1] = fg;
+ memcpy(data + 2, path, plen + 1);
+ memcpy(data + 2 + plen + 1, delete, dlen + 1);
+ hard_write(term->fdout, data, data_size);
+ fmem_free(data);
+}
+
void
exec_on_terminal(struct terminal *term, unsigned char *path,
unsigned char *delete, int fg)
{
- int plen;
- int dlen = strlen(delete);
-
- if (path && !*path) return;
- if (!path) {
- path = "";
- plen = 0;
+ if (path) {
+ if (!*path) return;
} else {
- plen = strlen(path);
+ path = "";
}
#ifdef NO_FG_EXEC
fg = 0;
#endif
+
if (term->master) {
- if (!*path) dispatch_special(delete);
- else {
- int blockh;
- unsigned char *param;
- int param_size;
-
- if (is_blocked() && fg) {
- unlink(delete);
- return;
- }
-
- param_size = plen + dlen + 2 /* 2 null char */ + 1 /* fg */;
- param = mem_alloc(param_size);
- if (!param) return;
-
- param[0] = fg;
- memcpy(param + 1, path, plen + 1);
- memcpy(param + 1 + plen + 1, delete, dlen + 1);
-
- if (fg == 1) block_itrm(term->fdin);
-
- blockh = start_thread((void (*)(void *, int)) exec_thread,
- param, param_size);
- if (blockh == -1) {
- if (fg == 1) unblock_itrm(term->fdin);
- mem_free(param);
- return;
- }
-
- mem_free(param);
- if (fg == 1) {
- term->blocked = blockh;
- set_handlers(blockh,
- (select_handler_T) unblock_terminal,
- NULL,
- (select_handler_T) unblock_terminal,
- term);
- set_handlers(term->fdin, NULL, NULL,
- (select_handler_T) destroy_terminal,
- term);
- /* block_itrm(term->fdin); */
- } else {
- set_handlers(blockh, close_handle, NULL,
- close_handle, (void *) (long) blockh);
- }
+ if (!*path) {
+ dispatch_special(delete);
+ return;
}
+
+ if (fg && is_blocked()) {
+ unlink(delete);
+ return;
+ }
+
+ exec_on_master_terminal(term,
+ path, strlen(path),
+ delete, strlen(delete),
+ fg);
} else {
- int data_size = plen + dlen + 1 /* 0 */ + 1 /* fg */ + 2 /* 2 null char */;
- unsigned char *data = mem_alloc(data_size);
-
- if (data) {
- data[0] = 0;
- data[1] = fg;
- memcpy(data + 2, path, plen + 1);
- memcpy(data + 2 + plen + 1, delete, dlen + 1);
- hard_write(term->fdout, data, data_size);
- mem_free(data);
- }
-#if 0
- char x = 0;
- hard_write(term->fdout, &x, 1);
- x = fg;
- hard_write(term->fdout, &x, 1);
- hard_write(term->fdout, path, strlen(path) + 1);
- hard_write(term->fdout, delete, strlen(delete) + 1);
-#endif
+ exec_on_slave_terminal( term,
+ path, strlen(path),
+ delete, strlen(delete),
+ fg);
}
}
diff --git a/src/viewer/dump/dump.c b/src/viewer/dump/dump.c
index 3e038502c..9b38ab2de 100644
--- a/src/viewer/dump/dump.c
+++ b/src/viewer/dump/dump.c
@@ -324,19 +324,20 @@ add_document_to_string(struct string *string, struct document *document)
if_assert_failed return NULL;
for (y = 0; y < document->height; y++) {
- struct screen_char *pos = document->data[y].chars;
int white = 0;
int x;
for (x = 0; x < document->data[y].length; x++) {
+ struct screen_char *pos = &document->data[y].chars[x];
unsigned char data = pos->data;
unsigned int frame = (pos->attr & SCREEN_ATTR_FRAME);
if (!isscreensafe(data)) {
white++;
continue;
- } else if (frame && data >= 176 && data < 224) {
- data = frame_dumb[data - 176];
+ } else {
+ if (frame && data >= 176 && data < 224)
+ data = frame_dumb[data - 176];
if (data <= ' ') {
/* Count spaces. */
diff --git a/test/libtest.sh b/test/libtest.sh
index 2d40a9162..12de5c920 100755
--- a/test/libtest.sh
+++ b/test/libtest.sh
@@ -162,4 +162,4 @@ test_done () {
test=trash
rm -fr "$test"
mkdir "$test"
-cd "$test"
+cd "$test" || error "Cannot setup test environment"