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] +

{title}

+| +
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)' \ -' 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.' \ +'/dev/null + test_expect_success "$desc" \ + "test $? = 1" +} + + +################################################################ +# Check for incompleteness + +test_expect_incomplete \ +'Check incomplete comment. (I)' \ +' 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"