1
0
mirror of https://github.com/rkd77/elinks.git synced 2025-01-03 14:57:44 -05:00

Merge with git+ssh://pasky.or.cz/srv/git/elinks.git

This commit is contained in:
2006-01-28 12:11:29 +01:00
commit dacb694e33
19 changed files with 597 additions and 124 deletions

View File

@ -127,12 +127,12 @@ CLEAN += $(PROG) $(OBJS)
############################################################################# #############################################################################
# The main default rules # The main default rules
all-default: $(ALL_OBJS) $(PROGS) $(MAN1) $(MAN5) .vimrc all-default: $(ALL_OBJS) $(PROGS) $(MAN1) $(MAN5)
# Ensure that Makefiles in subdirs are created before we recursive into them # Ensure that Makefiles in subdirs are created before we recursive into them
init-recursive: init-default init-recursive: init-default
init-default: .vimrc init-default:
@$(foreach subdir,$(sort $(SUBDIRS)), \ @$(foreach subdir,$(sort $(SUBDIRS)), \
$(MKINSTALLDIRS) $(subdir) >/dev/null; \ $(MKINSTALLDIRS) $(subdir) >/dev/null; \
test -e "$(subdir)/Makefile" \ test -e "$(subdir)/Makefile" \
@ -164,11 +164,6 @@ ifdef MAN5
$(call ncmd,installdata,$(file),$(DESTDIR)$(mandir)/man5);) $(call ncmd,installdata,$(file),$(DESTDIR)$(mandir)/man5);)
endif endif
.vimrc: $(top_srcdir)/Makefile.lib
@{ echo ':set runtimepath+=.'; \
echo ':runtime $(top_srcdir)/config/vimrc'; \
} > .vimrc
############################################################################## ##############################################################################
# Auto-testing infrastructure # Auto-testing infrastructure

View File

@ -1,8 +0,0 @@
" Master vimrc file for the ELinks project
:set shiftwidth=8
:set tabstop=8
:set softtabstop=0
:set noexpandtab
au BufNewFile,BufRead *.inc setf c

View File

@ -1,7 +1,7 @@
/* Play videos at video.google.com with minimal niggling. Just follow the link /* Play videos at video.google.com with minimal niggling. Just follow the link
* from the front page or the search page, and the video will automatically * from the front page or the search page, and the video will automatically
* be loaded. */ * be loaded. */
function load_google_video(cached) { function load_google_video(cached, vs) {
if (!cached.uri.match(/^http:\/\/video.google.com\/videoplay/)) if (!cached.uri.match(/^http:\/\/video.google.com\/videoplay/))
return true; return true;

View File

@ -8,9 +8,9 @@ elinks.keymaps.main["@"] = function () {
}; };
elinks.preformat_html_hooks = new Array(); elinks.preformat_html_hooks = new Array();
elinks.preformat_html = function (cached) { elinks.preformat_html = function (cached, vs) {
for (var i in elinks.preformat_html_hooks) for (var i in elinks.preformat_html_hooks)
if (!elinks.preformat_html_hooks[i](cached)) if (!elinks.preformat_html_hooks[i](cached, vs))
return false; return false;
return true; return true;
@ -36,13 +36,13 @@ elinks.follow_url_hook = function (url) {
return url; return url;
}; };
function root_w00t(cached) { function root_w00t(cached, vs) {
cached.content = cached.content.replace(/root/g, "w00t"); cached.content = cached.content.replace(/root/g, "w00t");
return true; return true;
}; };
elinks.preformat_html_hooks.push(root_w00t); elinks.preformat_html_hooks.push(root_w00t);
function mangle_deb_bugnumbers(cached) { function mangle_deb_bugnumbers(cached, vs) {
if (!cached.uri.match(/^[a-z0-9]+:\/\/[a-z0-9A-Z.-]+debian\.org/) if (!cached.uri.match(/^[a-z0-9]+:\/\/[a-z0-9A-Z.-]+debian\.org/)
&& !cached.uri.match(/changelog\.Debian/)) && !cached.uri.match(/changelog\.Debian/))
return true; return true;
@ -55,7 +55,14 @@ function mangle_deb_bugnumbers(cached) {
/* Debian Policy Manual 4.4 footnote 16 */ /* Debian Policy Manual 4.4 footnote 16 */
var closes_re = /closes:\s*(?:bug)?\#?\s?\d+(?:,\s*(?:bug)?\#?\s?\d+)*/gi; var closes_re = /closes:\s*(?:bug)?\#?\s?\d+(?:,\s*(?:bug)?\#?\s?\d+)*/gi;
cached.content = cached.content.replace(closes_re, rewrite_closes_fn); var new_content = cached.content.replace(closes_re, rewrite_closes_fn);
if (cached.content_type == 'text/plain') {
cached.content = '<pre>' + new_content + '</pre>';
vs.plain = "0";
} else {
cached.content = new_content;
}
return true; return true;
} }

15
contrib/vim/c_elinks.vim Normal file
View File

@ -0,0 +1,15 @@
" Setting Vim to support the ELinks coding style
"
" To use this file, drop it in ~/.vim/ftplugin and set filetype plugin on.
" Finally, make sure the path to the source directory contains the word
" 'elinks', for example ~/src/elinks/.
"
" For .h files, link it as cpp_elinks.vim or define c_syntax_for_h in ~/.vimrc.
" For .inc files, let g:filetype_inc = 'c' in ~/.vimrc.
if expand('%:p:h') =~ '.*elinks.*'
setlocal shiftwidth=8
setlocal tabstop=8
setlocal softtabstop=0
setlocal noexpandtab
endif

View File

@ -44,11 +44,11 @@ normalize_text_node_whitespace(struct dom_node *node)
} }
} }
if (node->data.text.allocated) if (node->allocated)
done_dom_string(&node->string); done_dom_string(&node->string);
set_dom_string(&node->string, string.string, string.length); set_dom_string(&node->string, string.string, string.length);
node->data.text.allocated = 1; node->allocated = 1;
return DOM_STACK_CODE_OK; return DOM_STACK_CODE_OK;
@ -74,14 +74,14 @@ append_node_text(struct dom_config *config, struct dom_node *node)
set_dom_string(&dest, NULL, 0); set_dom_string(&dest, NULL, 0);
} else { } else {
if (prev->data.text.allocated) { if (prev->allocated) {
copy_struct(&dest, &prev->string); copy_struct(&dest, &prev->string);
} else { } else {
set_dom_string(&dest, NULL, 0); set_dom_string(&dest, NULL, 0);
if (!add_to_dom_string(&dest, prev->string.string, prev->string.length)) if (!add_to_dom_string(&dest, prev->string.string, prev->string.length))
return DOM_STACK_CODE_ERROR_MEM_ALLOC; return DOM_STACK_CODE_ERROR_MEM_ALLOC;
set_dom_string(&prev->string, dest.string, dest.length); set_dom_string(&prev->string, dest.string, dest.length);
prev->data.text.allocated = 1; prev->allocated = 1;
} }
} }
@ -135,7 +135,7 @@ append_node_text(struct dom_config *config, struct dom_node *node)
node->type = DOM_NODE_TEXT; node->type = DOM_NODE_TEXT;
memset(&node->data, 0, sizeof(node->data)); memset(&node->data, 0, sizeof(node->data));
node->data.text.allocated = 1; node->allocated = 1;
copy_struct(&node->string, &dest); copy_struct(&node->string, &dest);
if ((config->flags & DOM_CONFIG_NORMALIZE_WHITESPACE) if ((config->flags & DOM_CONFIG_NORMALIZE_WHITESPACE)

View File

@ -202,7 +202,7 @@ struct dom_node *
get_dom_node_map_entry(struct dom_node_list *list, enum dom_node_type type, get_dom_node_map_entry(struct dom_node_list *list, enum dom_node_type type,
uint16_t subtype, struct dom_string *name) uint16_t subtype, struct dom_string *name)
{ {
struct dom_node node = { type, INIT_DOM_STRING(name->string, name->length) }; struct dom_node node = { type, 0, INIT_DOM_STRING(name->string, name->length) };
struct dom_node_search search = INIT_DOM_NODE_SEARCH(&node, list); struct dom_node_search search = INIT_DOM_NODE_SEARCH(&node, list);
if (subtype) { if (subtype) {
@ -314,7 +314,7 @@ get_dom_node_child(struct dom_node *parent, enum dom_node_type type,
struct dom_node * struct dom_node *
init_dom_node_(unsigned char *file, int line, init_dom_node_(unsigned char *file, int line,
struct dom_node *parent, enum dom_node_type type, struct dom_node *parent, enum dom_node_type type,
struct dom_string *string) struct dom_string *string, int allocated)
{ {
#ifdef DEBUG_MEMLEAK #ifdef DEBUG_MEMLEAK
struct dom_node *node = debug_mem_calloc(file, line, 1, sizeof(*node)); struct dom_node *node = debug_mem_calloc(file, line, 1, sizeof(*node));
@ -326,7 +326,6 @@ init_dom_node_(unsigned char *file, int line,
node->type = type; node->type = type;
node->parent = parent; node->parent = parent;
copy_dom_string(&node->string, string);
if (parent) { if (parent) {
struct dom_node_list **list = get_dom_node_list(parent, node); struct dom_node_list **list = get_dom_node_list(parent, node);
@ -343,6 +342,22 @@ init_dom_node_(unsigned char *file, int line,
done_dom_node(node); done_dom_node(node);
return NULL; return NULL;
} }
/* Make it possible to add a node to a parent without
* allocating the strings. */
node->allocated = allocated < 0 ? parent->allocated : !!allocated;
} else if (allocated >= 0) {
node->allocated = !!allocated;
}
if (node->allocated) {
if (!init_dom_string(&node->string, string->string, string->length)) {
done_dom_node(node);
return NULL;
}
} else {
copy_dom_string(&node->string, string);
} }
return node; return node;
@ -359,8 +374,8 @@ done_dom_node_data(struct dom_node *node)
switch (node->type) { switch (node->type) {
case DOM_NODE_ATTRIBUTE: case DOM_NODE_ATTRIBUTE:
if (data->attribute.allocated) if (node->allocated)
done_dom_string(&node->string); done_dom_string(&data->attribute.value);
break; break;
case DOM_NODE_DOCUMENT: case DOM_NODE_DOCUMENT:
@ -382,20 +397,19 @@ done_dom_node_data(struct dom_node *node)
done_dom_node_list(data->element.map); done_dom_node_list(data->element.map);
break; break;
case DOM_NODE_TEXT:
if (data->text.allocated)
done_dom_string(&node->string);
break;
case DOM_NODE_PROCESSING_INSTRUCTION: case DOM_NODE_PROCESSING_INSTRUCTION:
if (data->proc_instruction.map) if (data->proc_instruction.map)
done_dom_node_list(data->proc_instruction.map); done_dom_node_list(data->proc_instruction.map);
if (node->allocated)
done_dom_string(&data->proc_instruction.instruction);
break; break;
default: default:
break; break;
} }
if (node->allocated)
done_dom_string(&node->string);
mem_free(node); mem_free(node);
} }

View File

@ -115,9 +115,6 @@ struct dom_attribute_node {
* it added from the document source. */ * it added from the document source. */
unsigned int specified:1; unsigned int specified:1;
/* Was the node->string allocated */
unsigned int allocated:1;
/* Has the node->string been converted to internal charset. */ /* Has the node->string been converted to internal charset. */
unsigned int converted:1; unsigned int converted:1;
@ -140,9 +137,6 @@ struct dom_text_node {
* In order to quickly identify such nodes this member is used. */ * In order to quickly identify such nodes this member is used. */
unsigned int only_space:1; unsigned int only_space:1;
/* Was the node->string allocated */
unsigned int allocated:1;
/* Has the node->string been converted to internal charset. */ /* Has the node->string been converted to internal charset. */
unsigned int converted:1; unsigned int converted:1;
}; };
@ -151,9 +145,8 @@ enum dom_proc_instruction_type {
DOM_PROC_INSTRUCTION, DOM_PROC_INSTRUCTION,
/* Keep this group sorted */ /* Keep this group sorted */
DOM_PROC_INSTRUCTION_DBHTML, /* DocBook toolchain instruction */ DOM_PROC_INSTRUCTION_XML, /* XML header */
DOM_PROC_INSTRUCTION_ELINKS, /* Internal instruction hook */ DOM_PROC_INSTRUCTION_XML_STYLESHEET, /* XML stylesheet link */
DOM_PROC_INSTRUCTION_XML, /* XML instructions */
DOM_PROC_INSTRUCTION_TYPES DOM_PROC_INSTRUCTION_TYPES
}; };
@ -198,6 +191,9 @@ struct dom_node {
/* The type of the node */ /* The type of the node */
uint16_t type; /* -> enum dom_node_type */ uint16_t type; /* -> enum dom_node_type */
/* Was the node string allocated? */
unsigned int allocated:1;
/* Can contain either stuff like element name or for attributes the /* Can contain either stuff like element name or for attributes the
* attribute name. */ * attribute name. */
struct dom_string string; struct dom_string string;
@ -260,12 +256,21 @@ get_dom_node_map_entry(struct dom_node_list *node_map,
enum dom_node_type type, uint16_t subtype, enum dom_node_type type, uint16_t subtype,
struct dom_string *name); struct dom_string *name);
/* Removes the node and all its children and free()s itself */
void done_dom_node(struct dom_node *node);
/* The allocated argument is used as the value of node->allocated if >= 0.
* Use -1 to default node->allocated to the value of parent->allocated. */
struct dom_node * struct dom_node *
init_dom_node_(unsigned char *file, int line, init_dom_node_(unsigned char *file, int line,
struct dom_node *parent, enum dom_node_type type, struct dom_node *parent, enum dom_node_type type,
struct dom_string *string); struct dom_string *string, int allocated);
#define init_dom_node(type, string) init_dom_node_(__FILE__, __LINE__, NULL, type, string)
#define add_dom_node(parent, type, string) init_dom_node_(__FILE__, __LINE__, parent, type, string) #define init_dom_node(type, string, allocated) \
init_dom_node_(__FILE__, __LINE__, NULL, type, string, allocated)
#define add_dom_node(parent, type, string) \
init_dom_node_(__FILE__, __LINE__, parent, type, string, -1)
#define add_dom_element(parent, string) \ #define add_dom_element(parent, string) \
add_dom_node(parent, DOM_NODE_ELEMENT, string) add_dom_node(parent, DOM_NODE_ELEMENT, string)
@ -277,7 +282,16 @@ add_dom_attribute(struct dom_node *parent, struct dom_string *name,
struct dom_node *node = add_dom_node(parent, DOM_NODE_ATTRIBUTE, name); struct dom_node *node = add_dom_node(parent, DOM_NODE_ATTRIBUTE, name);
if (node && value) { if (node && value) {
copy_dom_string(&node->data.attribute.value, value); struct dom_string *str = &node->data.attribute.value;
if (node->allocated) {
if (!init_dom_string(str, value->string, value->length)) {
done_dom_node(node);
return NULL;
}
} else {
copy_dom_string(str, value);
}
} }
return node; return node;
@ -290,15 +304,21 @@ add_dom_proc_instruction(struct dom_node *parent, struct dom_string *string,
struct dom_node *node = add_dom_node(parent, DOM_NODE_PROCESSING_INSTRUCTION, string); struct dom_node *node = add_dom_node(parent, DOM_NODE_PROCESSING_INSTRUCTION, string);
if (node && instruction) { if (node && instruction) {
copy_dom_string(&node->data.proc_instruction.instruction, instruction); struct dom_string *str = &node->data.proc_instruction.instruction;
if (node->allocated) {
if (!init_dom_string(str, instruction->string, instruction->length)) {
done_dom_node(node);
return NULL;
}
} else {
copy_dom_string(str, instruction);
}
} }
return node; return node;
} }
/* Removes the node and all its children and free()s itself */
void done_dom_node(struct dom_node *node);
/* Compare two nodes returning non-zero if they differ. */ /* Compare two nodes returning non-zero if they differ. */
int dom_node_casecmp(struct dom_node *node1, struct dom_node *node2); int dom_node_casecmp(struct dom_node *node1, struct dom_node *node2);

View File

@ -35,11 +35,13 @@
* information like node subtypes and SGML parser state information. */ * information like node subtypes and SGML parser state information. */
static inline struct dom_node * static inline struct dom_node *
add_sgml_document(struct dom_stack *stack, struct dom_string *string) add_sgml_document(struct sgml_parser *parser)
{ {
struct dom_node *node = init_dom_node(DOM_NODE_DOCUMENT, string); int allocated = parser->flags & SGML_PARSER_INCREMENTAL;
struct dom_node *node;
if (node && push_dom_node(stack, node) == DOM_STACK_CODE_OK) node = init_dom_node(DOM_NODE_DOCUMENT, &parser->uri, allocated);
if (node && push_dom_node(&parser->stack, node) == DOM_STACK_CODE_OK)
return node; return node;
return NULL; return NULL;
@ -74,7 +76,7 @@ add_sgml_element(struct dom_stack *stack, struct dom_scanner_token *token)
} }
static inline void static inline struct dom_node *
add_sgml_attribute(struct dom_stack *stack, add_sgml_attribute(struct dom_stack *stack,
struct dom_scanner_token *token, struct dom_scanner_token *valtoken) struct dom_scanner_token *token, struct dom_scanner_token *valtoken)
{ {
@ -96,9 +98,11 @@ add_sgml_attribute(struct dom_stack *stack,
node->data.attribute.quoted = 1; node->data.attribute.quoted = 1;
if (!node || push_dom_node(stack, node) != DOM_STACK_CODE_OK) if (!node || push_dom_node(stack, node) != DOM_STACK_CODE_OK)
return; return NULL;
pop_dom_node(stack); pop_dom_node(stack);
return node;
} }
static inline struct dom_node * static inline struct dom_node *
@ -117,6 +121,10 @@ add_sgml_proc_instruction(struct dom_stack *stack, struct dom_scanner_token *tar
node->data.proc_instruction.type = DOM_PROC_INSTRUCTION_XML; node->data.proc_instruction.type = DOM_PROC_INSTRUCTION_XML;
break; break;
case SGML_TOKEN_PROCESS_XML_STYLESHEET:
node->data.proc_instruction.type = DOM_PROC_INSTRUCTION_XML_STYLESHEET;
break;
case SGML_TOKEN_PROCESS: case SGML_TOKEN_PROCESS:
default: default:
node->data.proc_instruction.type = DOM_PROC_INSTRUCTION; node->data.proc_instruction.type = DOM_PROC_INSTRUCTION;
@ -128,19 +136,21 @@ add_sgml_proc_instruction(struct dom_stack *stack, struct dom_scanner_token *tar
return NULL; return NULL;
} }
static inline void static inline struct dom_node *
add_sgml_node(struct dom_stack *stack, enum dom_node_type type, struct dom_scanner_token *token) add_sgml_node(struct dom_stack *stack, enum dom_node_type type, struct dom_scanner_token *token)
{ {
struct dom_node *parent = get_dom_stack_top(stack)->node; struct dom_node *parent = get_dom_stack_top(stack)->node;
struct dom_node *node = add_dom_node(parent, type, &token->string); struct dom_node *node = add_dom_node(parent, type, &token->string);
if (!node) return; if (!node) return NULL;
if (token->type == SGML_TOKEN_SPACE) if (token->type == SGML_TOKEN_SPACE)
node->data.text.only_space = 1; node->data.text.only_space = 1;
if (push_dom_node(stack, node) == DOM_STACK_CODE_OK) if (push_dom_node(stack, node) == DOM_STACK_CODE_OK)
pop_dom_node(stack); pop_dom_node(stack);
return node;
} }
@ -157,18 +167,42 @@ call_sgml_error_function(struct dom_stack *stack, struct dom_scanner_token *toke
return parser->error_func(parser, &token->string, line); return parser->error_func(parser, &token->string, line);
} }
/* Appends to or 'creates' an incomplete token. This can be used to
* force tokens back into the 'stream' if they require that later tokens
* are available.
*
* NOTE: You can only do this for tokens that are not stripped of markup such
* as identifiers. */
static enum sgml_parser_code
check_sgml_incomplete(struct dom_scanner *scanner,
struct dom_scanner_token *start,
struct dom_scanner_token *token)
{
if (token && token->type == SGML_TOKEN_INCOMPLETE) {
token->string.length += token->string.string - start->string.string;
token->string.string = start->string.string;
return 1;
} else if (!token && scanner->check_complete && scanner->incomplete) {
size_t left = scanner->end - start->string.string;
assert(left > 0);
token = scanner->current = scanner->table;
scanner->tokens = 1;
token->type = SGML_TOKEN_INCOMPLETE;
set_dom_string(&token->string, start->string.string, left);
return 1;
}
return 0;
}
static inline enum sgml_parser_code static inline enum sgml_parser_code
parse_sgml_attributes(struct dom_stack *stack, struct dom_scanner *scanner) parse_sgml_attributes(struct dom_stack *stack, struct dom_scanner *scanner)
{ {
struct dom_scanner_token name; struct dom_scanner_token name;
assert(dom_scanner_has_tokens(scanner)
&& (get_dom_scanner_token(scanner)->type == SGML_TOKEN_ELEMENT_BEGIN
|| (get_dom_stack_top(stack)->node->type == DOM_NODE_PROCESSING_INSTRUCTION)));
if (get_dom_scanner_token(scanner)->type == SGML_TOKEN_ELEMENT_BEGIN)
skip_dom_scanner_token(scanner);
while (dom_scanner_has_tokens(scanner)) { while (dom_scanner_has_tokens(scanner)) {
struct dom_scanner_token *token = get_dom_scanner_token(scanner); struct dom_scanner_token *token = get_dom_scanner_token(scanner);
@ -194,7 +228,7 @@ parse_sgml_attributes(struct dom_stack *stack, struct dom_scanner *scanner)
/* If the token is not a valid value token /* If the token is not a valid value token
* ignore it. */ * ignore it. */
token = get_next_dom_scanner_token(scanner); token = get_next_dom_scanner_token(scanner);
if (token && token->type == SGML_TOKEN_INCOMPLETE) if (check_sgml_incomplete(scanner, &name, token))
return SGML_PARSER_CODE_INCOMPLETE; return SGML_PARSER_CODE_INCOMPLETE;
if (token if (token
@ -203,14 +237,15 @@ parse_sgml_attributes(struct dom_stack *stack, struct dom_scanner *scanner)
&& token->type != SGML_TOKEN_STRING) && token->type != SGML_TOKEN_STRING)
token = NULL; token = NULL;
} else if (token && token->type == SGML_TOKEN_INCOMPLETE) { } else if (check_sgml_incomplete(scanner, &name, token)) {
return SGML_PARSER_CODE_INCOMPLETE; return SGML_PARSER_CODE_INCOMPLETE;
} else { } else {
token = NULL; token = NULL;
} }
add_sgml_attribute(stack, &name, token); if (!add_sgml_attribute(stack, &name, token))
return SGML_PARSER_CODE_MEM_ALLOC;
/* Skip the value token */ /* Skip the value token */
if (token) if (token)
@ -250,19 +285,14 @@ parse_sgml_plain(struct dom_stack *stack, struct dom_scanner *scanner)
switch (token->type) { switch (token->type) {
case SGML_TOKEN_ELEMENT: case SGML_TOKEN_ELEMENT:
case SGML_TOKEN_ELEMENT_BEGIN: case SGML_TOKEN_ELEMENT_BEGIN:
if (!add_sgml_element(stack, token)) { if (!add_sgml_element(stack, token))
if (token->type == SGML_TOKEN_ELEMENT) { return SGML_PARSER_CODE_MEM_ALLOC;
skip_dom_scanner_token(scanner);
break;
}
skip_sgml_tokens(scanner, SGML_TOKEN_TAG_END);
break;
}
if (token->type == SGML_TOKEN_ELEMENT_BEGIN) { if (token->type == SGML_TOKEN_ELEMENT_BEGIN) {
enum sgml_parser_code code; enum sgml_parser_code code;
skip_dom_scanner_token(scanner);
code = parse_sgml_attributes(stack, scanner); code = parse_sgml_attributes(stack, scanner);
if (code != SGML_PARSER_CODE_OK) if (code != SGML_PARSER_CODE_OK)
return code; return code;
@ -301,7 +331,8 @@ parse_sgml_plain(struct dom_stack *stack, struct dom_scanner *scanner)
break; break;
case SGML_TOKEN_NOTATION_COMMENT: case SGML_TOKEN_NOTATION_COMMENT:
add_sgml_node(stack, DOM_NODE_COMMENT, token); if (!add_sgml_node(stack, DOM_NODE_COMMENT, token))
return SGML_PARSER_CODE_MEM_ALLOC;
skip_dom_scanner_token(scanner); skip_dom_scanner_token(scanner);
break; break;
@ -314,7 +345,8 @@ parse_sgml_plain(struct dom_stack *stack, struct dom_scanner *scanner)
break; break;
case SGML_TOKEN_CDATA_SECTION: case SGML_TOKEN_CDATA_SECTION:
add_sgml_node(stack, DOM_NODE_CDATA_SECTION, token); if (!add_sgml_node(stack, DOM_NODE_CDATA_SECTION, token))
return SGML_PARSER_CODE_MEM_ALLOC;
skip_dom_scanner_token(scanner); skip_dom_scanner_token(scanner);
break; break;
@ -334,10 +366,10 @@ parse_sgml_plain(struct dom_stack *stack, struct dom_scanner *scanner)
assert(token->type == SGML_TOKEN_PROCESS_DATA); assert(token->type == SGML_TOKEN_PROCESS_DATA);
/* Fall-through */ /* Fall-through */
case SGML_TOKEN_PROCESS_DATA: if (!add_sgml_proc_instruction(stack, &target, token))
if (add_sgml_proc_instruction(stack, &target, token) return SGML_PARSER_CODE_MEM_ALLOC;
&& (target.type == SGML_TOKEN_PROCESS_XML if ((target.type == SGML_TOKEN_PROCESS_XML
|| target.type == SGML_TOKEN_PROCESS_XML_STYLESHEET) || target.type == SGML_TOKEN_PROCESS_XML_STYLESHEET)
&& token->string.length > 0) { && token->string.length > 0) {
/* Parse the <?xml data="attributes"?>. */ /* Parse the <?xml data="attributes"?>. */
struct dom_scanner attr_scanner; struct dom_scanner attr_scanner;
@ -402,13 +434,13 @@ parse_sgml(struct sgml_parser *parser, unsigned char *buf, size_t bufsize,
parser->flags |= SGML_PARSER_COMPLETE; parser->flags |= SGML_PARSER_COMPLETE;
if (!parser->root) { if (!parser->root) {
parser->root = add_sgml_document(&parser->stack, &parser->uri); parser->root = add_sgml_document(parser);
if (!parser->root) if (!parser->root)
return SGML_PARSER_CODE_MEM_ALLOC; return SGML_PARSER_CODE_MEM_ALLOC;
get_dom_stack_top(&parser->stack)->immutable = 1; get_dom_stack_top(&parser->stack)->immutable = 1;
} }
node = init_dom_node(DOM_NODE_TEXT, &source); node = init_dom_node(DOM_NODE_TEXT, &source, 0);
if (!node || push_dom_node(&parser->parsing, node) != DOM_STACK_CODE_OK) if (!node || push_dom_node(&parser->parsing, node) != DOM_STACK_CODE_OK)
return SGML_PARSER_CODE_MEM_ALLOC; return SGML_PARSER_CODE_MEM_ALLOC;
@ -452,15 +484,19 @@ sgml_parsing_push(struct dom_stack *stack, struct dom_node *node, void *data)
struct sgml_parsing_state *parent = &parsing[-1]; struct sgml_parsing_state *parent = &parsing[-1];
if (parent->resume) { if (parent->resume) {
assert(is_dom_string_set(&parent->incomplete)); if (is_dom_string_set(&parent->incomplete)) {
if (!add_to_dom_string(&parent->incomplete, if (!add_to_dom_string(&parent->incomplete,
string->string, string->length)) { string->string,
parser->code = SGML_PARSER_CODE_MEM_ALLOC; string->length)) {
return DOM_STACK_CODE_OK;
parser->code = SGML_PARSER_CODE_MEM_ALLOC;
return DOM_STACK_CODE_OK;
}
string = &parent->incomplete;
} }
string = &parent->incomplete;
scanner_state = parent->scanner.state; scanner_state = parent->scanner.state;
/* Pop down to the parent. */ /* Pop down to the parent. */
@ -474,19 +510,31 @@ sgml_parsing_push(struct dom_stack *stack, struct dom_node *node, void *data)
scanner_state, count_lines, complete, incremental, scanner_state, count_lines, complete, incremental,
detect_errors); detect_errors);
{ if (scanner_state == SGML_STATE_ELEMENT) {
int immutable = get_dom_stack_top(&parser->stack)->immutable; parser->code = parse_sgml_attributes(&parser->stack, &parsing->scanner);
if (parser->code == SGML_PARSER_CODE_OK)
get_dom_stack_top(&parser->stack)->immutable = 1; parser->code = parse_sgml_plain(&parser->stack, &parsing->scanner);
} else {
parser->code = parse_sgml_plain(&parser->stack, &parsing->scanner); parser->code = parse_sgml_plain(&parser->stack, &parsing->scanner);
get_dom_stack_top(&parser->stack)->immutable = !!immutable;
} }
if (complete || parser->code != SGML_PARSER_CODE_INCOMPLETE) { if (complete) {
pop_dom_node(&parser->parsing); pop_dom_node(&parser->parsing);
return DOM_STACK_CODE_OK; return DOM_STACK_CODE_OK;
} }
if (parser->code != SGML_PARSER_CODE_INCOMPLETE) {
/* No need to preserve the default scanner state. */
if (parsing->scanner.state == SGML_STATE_TEXT) {
pop_dom_node(&parser->parsing);
return DOM_STACK_CODE_OK;
}
done_dom_string(&parsing->incomplete);
parsing->resume = 1;
return DOM_STACK_CODE_OK;
}
token = get_dom_scanner_token(&parsing->scanner); token = get_dom_scanner_token(&parsing->scanner);
assert(token && token->type == SGML_TOKEN_INCOMPLETE); assert(token && token->type == SGML_TOKEN_INCOMPLETE);
@ -522,7 +570,7 @@ sgml_parsing_pop(struct dom_stack *stack, struct dom_node *node, void *data)
} }
/* It's bigger than when calling done_sgml_parser() in the middle of an /* It's bigger than when calling done_sgml_parser() in the middle of an
* incomplete parsing. */ * incomplete parsing. */
assert(parsing->depth == parser->stack.depth); assert(parsing->depth >= parser->stack.depth);
} }
done_dom_string(&parsing->incomplete); done_dom_string(&parsing->incomplete);

View File

@ -449,13 +449,16 @@ scan_sgml_element_token(struct dom_scanner *scanner, struct dom_scanner_token *t
if (scanner->state == SGML_STATE_ELEMENT) { if (scanner->state == SGML_STATE_ELEMENT) {
/* Already inside an element so insert a tag end token /* Already inside an element so insert a tag end token
* and continue scanning in next iteration. */ * and continue scanning in next iteration. */
string--;
real_length = 0;
type = SGML_TOKEN_TAG_END; type = SGML_TOKEN_TAG_END;
scanner_state = SGML_STATE_TEXT; scanner_state = SGML_STATE_TEXT;
/* We are creating a 'virtual' that has no source. */ /* We are creating a 'virtual' that has no source. */
possibly_incomplete = 0; possibly_incomplete = 0;
string = token->string.string;
real_length = 0;
} else if (string == scanner->end) {
/* It is incomplete. */
} else if (is_sgml_ident(*string)) { } else if (is_sgml_ident(*string)) {
token->string.string = string; token->string.string = string;
@ -540,6 +543,29 @@ scan_sgml_element_token(struct dom_scanner *scanner, struct dom_scanner_token *t
possibly_incomplete = 0; possibly_incomplete = 0;
} }
if (scanner->check_complete && scanner->incomplete) {
/* We need to fit both the process target token
* and the process data token into the scanner
* table. */
if (token + 1 >= scanner->table + DOM_SCANNER_TOKENS) {
possibly_incomplete = 1;
} else if (!possibly_incomplete) {
/* FIXME: We do this twice. */
for (pos = string + 1;
(pos = skip_sgml_chars(scanner, pos, '>'));
pos++) {
if (pos[-1] == '?')
break;
}
if (!pos)
possibly_incomplete = 1;
}
if (possibly_incomplete)
string = scanner->end;
}
} else if (*string == '/') { } else if (*string == '/') {
string++; string++;
skip_sgml_space(scanner, &string); skip_sgml_space(scanner, &string);
@ -641,6 +667,10 @@ scan_sgml_element_token(struct dom_scanner *scanner, struct dom_scanner_token *t
/* We found the end. */ /* We found the end. */
possibly_incomplete = 0; possibly_incomplete = 0;
} else if (scanner->check_complete && scanner->incomplete) {
/* Force an incomplete token. */
string = scanner->end;
} else if (is_sgml_attribute(*string)) { } else if (is_sgml_attribute(*string)) {
token->string.string++; token->string.string++;
scan_sgml_attribute(scanner, string); scan_sgml_attribute(scanner, string);
@ -698,9 +728,9 @@ scan_sgml_proc_inst_token(struct dom_scanner *scanner, struct dom_scanner_token
{ {
unsigned char *string = scanner->position; unsigned char *string = scanner->position;
/* The length can be empty for '<??>'. */ /* The length can be empty for '<??>'. */
size_t length = -1; ssize_t length = -1;
token->string.string = string; token->string.string = string++;
/* Figure out where the processing instruction ends. This doesn't use /* Figure out where the processing instruction ends. This doesn't use
* skip_sgml() since we MUST ignore precedence here to allow '<' inside * skip_sgml() since we MUST ignore precedence here to allow '<' inside

View File

@ -56,7 +56,7 @@ element: root
#text: a' #text: a'
test_output_equals \ test_output_equals \
'Parse tag soup elements.' \ 'Parse tag soup elements. (I)' \
'<parent attr="value" <child:1></><child:2</>a</parent>' \ '<parent attr="value" <child:1></><child:2</>a</parent>' \
' '
element: parent element: parent
@ -65,6 +65,14 @@ element: parent
element: child:2 element: child:2
#text: a' #text: a'
test_output_equals \
'Parse tag soup elements. (II)' \
'< a >< b < c / >< / >' \
'
element: a
element: b
element: c'
test_output_equals \ test_output_equals \
'Parse an enclosed comment.' \ 'Parse an enclosed comment.' \
'<root><!-- Hello World! --></root>' \ '<root><!-- Hello World! --></root>' \

View File

@ -11,9 +11,8 @@ parsing.
. "$TEST_LIB" . "$TEST_LIB"
test_output_equals () { test_incremental_parsing () {
desc="$1"; shift desc="$1"; shift
size="$1"; shift
src="$1"; shift src="$1"; shift
out="$1"; shift out="$1"; shift
@ -25,25 +24,242 @@ test_output_equals () {
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/; y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/;
s/[^a-zA-Z0-9-]//g;')" s/[^a-zA-Z0-9-]//g;')"
echo "$src" | sgml-parser --uri "$URI" --stdin "$size" \
| sed -e 's/^ //' | sed -n '$d;p' > output
echo "#document: $URI" > expected echo "#document: $URI" > expected
echo "$out" | sed -n '2,$p' >> expected echo "$out" | sed -n '2,$p' >> expected
test_expect_success "$desc" 'cmp output expected' for size in 1 2 3 4 5 6 7 8 9 10 15 20 25 50; do
echo -n "$src" | sgml-parser --uri "$URI" --stdin "$size" \
| sed -e 's/^ //' > output
test_run_ 'cmp output expected'
if [ "$?" != 0 -o "$eval_ret" != 0 ]
then
test_failure_ "$desc" "($size bytes)"
return
fi
done
test_ok_ "$desc"
} }
for i in 25 20 15 10 9 8 7 6 5 4 3 2 1; do test_incremental_parsing \
test_output_equals \ "Parse a small document." \
"Incrementally parse a small document reading $i bytes at a time." \ '<html><body><p>Hello World!</p></body></html>' \
"$i" \
'<html><body><p>Hello World!</p></body></html>' \
' '
element: html element: html
element: body element: body
element: p element: p
#text: Hello World!' #text: Hello World!'
done test_incremental_parsing \
'Parse elements.' \
'<root><child attr="value" /><child2></><child3 >a</></root>' \
'
element: root
element: child
attribute: attr -> value
element: child2
element: child3
#text: a'
test_incremental_parsing \
'Parse tag soup elements.' \
'<parent attr="value" <child:1></><child:2</>a</parent>' \
'
element: parent
attribute: attr -> value
element: child:1
element: child:2
#text: a'
test_incremental_parsing \
'Parse an enclosed comment.' \
'<root><!-- Hello World! --></root>' \
'
element: root
#comment: Hello World! '
test_incremental_parsing \
'Parse comment combinations. (I)' \
'<root><!-- <!-- -- > --><!--foo--><!----></root>' \
'
element: root
#comment: <!-- -- >
#comment: foo
#comment: '
test_incremental_parsing \
'Parse comment combinations. (II).' \
'<! -- comment -->s<!-->-->t<!----->u' \
'
#comment: comment
#text: s
#comment: >
#text: t
#comment: -
#text: u'
test_incremental_parsing \
'Parse bad comment. (I)' \
'<!--->s' \
'
#comment: ->s'
test_incremental_parsing \
'Parse bad comment. (II)' \
'<!--a--!>bad comment' \
'
#comment: a
#text: bad comment'
test_incremental_parsing \
'Parse empty notation.' \
'<!>s' \
'
#text: s'
test_incremental_parsing \
'Parse an enclosed CDATA section.' \
'<root><![CDATA[...] ]>...]]></root>' \
'
element: root
#cdata-section: ...] ]>...'
test_incremental_parsing \
'Parse non-enclosed CDATA section.' \
'<![CDATA[...]]>' \
'
#cdata-section: ...'
test_incremental_parsing \
'Parse a bad CDATA section.' \
'<![CDATA[...' \
'
#cdata-section: ...'
test_incremental_parsing \
'Parse attributes.' \
'<root lang="fr" attr name="value with &foo; <stuff"></root>' \
'
element: root
attribute: lang -> fr
attribute: attr ->
attribute: name -> value with &foo; <stuff'
test_incremental_parsing \
'Parse attributes with garbage.' \
"<root a=b c='d' e'f' g= h i = j k =></root>" \
'
element: root
attribute: a -> b
attribute: c -> d
attribute: g -> h
attribute: i -> j
attribute: k -> '
test_incremental_parsing \
'Parse attribute with non-quoted values.' \
'<root color=#abc path=/to/%61-&\one";files/>...' \
'
element: root
attribute: color -> #abc
attribute: path -> /to/%61-&\one";files
#text: ...'
test_incremental_parsing \
'Parse entity references.' \
'&amp;-&#42;' \
'
entity-reference: amp
#text: -
entity-reference: #42'
# Just how these should be gracefully handled is not clear to me.
test_incremental_parsing \
'Parse badly formatted entity references.' \
'& m33p;-&.:-copy;-&;-&#;-&#xx;' \
'
#text: & m33p;
#text: -
entity-reference: .:-copy
#text: -
#text: &;
#text: -
entity-reference: #
#text: -
entity-reference: #xx'
test_incremental_parsing \
'Parse processing instructions.' \
'<?xml encoding="UTF8"?>
...
<?ecmascript
var val=2;
?>' \
'
proc-instruction: xml -> encoding="UTF8"
attribute: encoding -> UTF8
#text: \n...\n
proc-instruction: ecmascript -> var val=2;\n'
test_incremental_parsing \
'Parse XML processing instructions.' \
'<?xml version="1.0" />?><?xml />-' \
'
proc-instruction: xml -> version="1.0" />
attribute: version -> 1.0
proc-instruction: xml -> />-'
test_incremental_parsing \
'Parse XML stylesheet processing instructions.' \
'<?xml-stylesheet type="text/xsl" href="url"?>' \
'
proc-instruction: xml-stylesheet -> type="text/xsl" href="url"
attribute: type -> text/xsl
attribute: href -> url'
test_incremental_parsing \
'Parse exotic processing instructions.' \
'<?xml ?+>+?>-?>-<?js?>-<??>-' \
'
proc-instruction: xml -> ?+>+
#text: -?>-
proc-instruction: js ->
#text: -
proc-instruction: ->
#text: -'
test_incremental_parsing \
'Parse incorrect processing instructions.' \
'<?js<?>-<?<??>-<?xml <=";&?>-<?' \
'
proc-instruction: js -> <
#text: -
proc-instruction: -> <?
#text: -
proc-instruction: xml -> <=";&
#text: -'
test_incremental_parsing \
'Parse incorrect processing instructions (II).' \
'<?><?' \
'
proc-instruction: -> ><?'
test_incremental_parsing \
'Skip spaces not inside text.' \
'<
root
ns:attr
=
"value"
><?
target
data?>< / root >' \
'
element: root
attribute: ns:attr -> value
proc-instruction: target -> data'
test_done test_done

View File

@ -4,6 +4,6 @@ include $(top_builddir)/Makefile.config
INCLUDES += $(SPIDERMONKEY_CFLAGS) INCLUDES += $(SPIDERMONKEY_CFLAGS)
OBJS = smjs.o core.o global_object.o hooks.o elinks_object.o cache_object.o \ OBJS = smjs.o core.o global_object.o hooks.o elinks_object.o cache_object.o \
bookmarks.o keybinding.o view_state_object.o bookmarks.o keybinding.o
include $(top_srcdir)/Makefile.lib include $(top_srcdir)/Makefile.lib

View File

@ -12,10 +12,13 @@
#include "main/event.h" #include "main/event.h"
#include "main/module.h" #include "main/module.h"
#include "scripting/smjs/cache_object.h" #include "scripting/smjs/cache_object.h"
#include "scripting/smjs/view_state_object.h"
#include "scripting/smjs/core.h" #include "scripting/smjs/core.h"
#include "scripting/smjs/elinks_object.h" #include "scripting/smjs/elinks_object.h"
#include "scripting/smjs/hooks.h" #include "scripting/smjs/hooks.h"
#include "session/location.h"
#include "session/session.h" #include "session/session.h"
#include "viewer/text/vs.h"
static enum evhook_status static enum evhook_status
@ -88,8 +91,8 @@ script_hook_pre_format_html(va_list ap, void *data)
struct session *ses = va_arg(ap, struct session *); struct session *ses = va_arg(ap, struct session *);
struct cache_entry *cached = va_arg(ap, struct cache_entry *); struct cache_entry *cached = va_arg(ap, struct cache_entry *);
enum evhook_status ret = EVENT_HOOK_STATUS_NEXT; enum evhook_status ret = EVENT_HOOK_STATUS_NEXT;
JSObject *cache_entry_object; JSObject *cache_entry_object, *view_state_object = JSVAL_NULL;
jsval args[1], rval; jsval args[2], rval;
evhook_use_params(ses && cached); evhook_use_params(ses && cached);
@ -97,13 +100,20 @@ script_hook_pre_format_html(va_list ap, void *data)
smjs_ses = ses; smjs_ses = ses;
if (have_location(ses)) {
struct view_state *vs = &cur_loc(ses)->vs;
view_state_object = smjs_get_view_state_object(vs);
}
cache_entry_object = smjs_get_cache_entry_object(cached); cache_entry_object = smjs_get_cache_entry_object(cached);
if (!cache_entry_object) goto end; if (!cache_entry_object) goto end;
args[0] = OBJECT_TO_JSVAL(cache_entry_object); args[0] = OBJECT_TO_JSVAL(cache_entry_object);
args[1] = OBJECT_TO_JSVAL(view_state_object);
if (JS_TRUE == smjs_invoke_elinks_object_method("preformat_html", if (JS_TRUE == smjs_invoke_elinks_object_method("preformat_html",
args, 1, &rval)) args, 2, &rval))
if (JS_FALSE == JSVAL_TO_BOOLEAN(rval)) if (JS_FALSE == JSVAL_TO_BOOLEAN(rval))
ret = EVENT_HOOK_STATUS_LAST; ret = EVENT_HOOK_STATUS_LAST;

View File

@ -0,0 +1,109 @@
/* Exports struct view_state to the world of ECMAScript */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include "elinks.h"
#include "ecmascript/spidermonkey/util.h"
#include "protocol/uri.h"
#include "scripting/smjs/view_state_object.h"
#include "scripting/smjs/core.h"
#include "util/error.h"
#include "util/memory.h"
#include "viewer/text/vs.h"
enum view_state_prop {
VIEW_STATE_PLAIN,
VIEW_STATE_URI,
};
static const JSPropertySpec view_state_props[] = {
{ "plain", VIEW_STATE_PLAIN, JSPROP_ENUMERATE },
{ "uri", VIEW_STATE_URI, JSPROP_ENUMERATE | JSPROP_READONLY },
{ NULL }
};
static JSBool
view_state_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
{
struct view_state *vs = JS_GetPrivate(ctx, obj);
undef_to_jsval(ctx, vp);
if (!JSVAL_IS_INT(id))
return JS_FALSE;
switch (JSVAL_TO_INT(id)) {
case VIEW_STATE_PLAIN:
*vp = INT_TO_JSVAL(vs->plain);
return JS_TRUE;
case VIEW_STATE_URI:
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(smjs_ctx,
struri(vs->uri)));
return JS_TRUE;
default:
INTERNAL("Invalid ID %d in view_state_get_property().",
JSVAL_TO_INT(id));
}
return JS_FALSE;
}
static JSBool
view_state_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
{
struct view_state *vs = JS_GetPrivate(ctx, obj);
if (!JSVAL_IS_INT(id))
return JS_FALSE;
switch (JSVAL_TO_INT(id)) {
case VIEW_STATE_PLAIN: {
vs->plain = atol(jsval_to_string(ctx, vp));
return JS_TRUE;
}
default:
INTERNAL("Invalid ID %d in view_state_set_property().",
JSVAL_TO_INT(id));
}
return JS_FALSE;
}
static const JSClass view_state_class = {
"view_state",
JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
view_state_get_property, view_state_set_property,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
JSObject *
smjs_get_view_state_object(struct view_state *vs)
{
JSObject *view_state_object;
assert(smjs_ctx);
view_state_object = JS_NewObject(smjs_ctx,
(JSClass *) &view_state_class,
NULL, NULL);
if (!view_state_object) return NULL;
if (JS_FALSE == JS_SetPrivate(smjs_ctx, view_state_object, vs))
return NULL;
if (JS_FALSE == JS_DefineProperties(smjs_ctx, view_state_object,
(JSPropertySpec *) view_state_props))
return NULL;
return view_state_object;
}

View File

@ -0,0 +1,9 @@
#ifndef EL__SCRIPTING_SMJS_VIEW_STATE_OBJECT_H
#define EL__SCRIPTING_SMJS_VIEW_STATE_OBJECT_H
struct view_state;
JSObject *smjs_get_view_state_object(struct view_state *vs);
#endif