/* HTML parser */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* strcasestr() */
#endif
#include
#include
#include
#include
#include "elinks.h"
#include "bfu/listmenu.h"
#include "bfu/menu.h"
#include "bookmarks/bookmarks.h"
#include "config/options.h"
#include "config/kbdbind.h"
#include "document/html/frames.h"
#include "document/html/parser/link.h"
#include "document/html/parser/parse.h"
#include "document/html/parser/stack.h"
#include "document/html/parser.h"
#include "document/html/renderer.h"
#include "globhist/globhist.h"
#include "mime/mime.h"
#include "protocol/uri.h"
#include "util/conv.h"
#include "util/error.h"
#include "util/memdebug.h"
#include "util/memory.h"
#include "util/string.h"
/* Unsafe macros */
#include "document/html/internal.h"
void
html_a(struct html_context *html_context, unsigned char *a,
unsigned char *xxx3, unsigned char *xxx4, unsigned char **xxx5)
{
unsigned char *href;
href = get_url_val(a, "href", html_context->options);
if (href) {
unsigned char *target;
mem_free_set(&format.link,
join_urls(html_context->base_href,
trim_chars(href, ' ', 0)));
mem_free(href);
target = get_target(html_context->options, a);
if (target) {
mem_free_set(&format.target, target);
} else {
mem_free_set(&format.target, stracpy(html_context->base_target));
}
if (0) {
; /* Shut up compiler */
#ifdef CONFIG_GLOBHIST
} else if (get_global_history_item(format.link)) {
format.style.fg = format.vlink;
html_top.pseudo_class &= ~ELEMENT_LINK;
html_top.pseudo_class |= ELEMENT_VISITED;
#endif
#ifdef CONFIG_BOOKMARKS
} else if (get_bookmark(format.link)) {
format.style.fg = format.bookmark_link;
html_top.pseudo_class &= ~ELEMENT_VISITED;
/* XXX: Really set ELEMENT_LINK? --pasky */
html_top.pseudo_class |= ELEMENT_LINK;
#endif
} else {
format.style.fg = format.clink;
html_top.pseudo_class &= ~ELEMENT_VISITED;
html_top.pseudo_class |= ELEMENT_LINK;
}
mem_free_set(&format.title,
get_attr_val(a, "title", html_context->options));
html_focusable(html_context, a);
} else {
kill_html_stack_item(html_context, &html_top);
}
set_fragment_identifier(html_context, a, "name");
}
/* Returns an allocated string made after @label
* but limited to @max_len length, by truncating
* the middle of @label string, which is replaced
* by an asterisk ('*').
* If @max_len < 0 it returns NULL.
* If @max_len == 0 it returns an unmodified copy
* of @label string.
* In either case, it may return NULL if a memory
* allocation failure occurs.
* Example:
* truncate_label("some_string", 5) => "so*ng" */
static unsigned char *
truncate_label(unsigned char *label, int max_len)
{
unsigned char *new_label;
int len = strlen(label);
int left_part_len;
int right_part_len;
if (max_len < 0) return NULL;
if (max_len == 0 || len <= max_len)
return stracpy(label);
right_part_len = left_part_len = max_len / 2;
if (left_part_len + right_part_len + 1 > max_len)
right_part_len--;
new_label = mem_alloc(max_len + 1);
if (!new_label) return NULL;
if (left_part_len)
memcpy(new_label, label, left_part_len);
new_label[left_part_len] = '*';
if (right_part_len)
memcpy(new_label + left_part_len + 1,
label + len - right_part_len, right_part_len);
new_label[max_len] = '\0';
return new_label;
}
/* Get image filename from its src attribute. */
static unsigned char *
get_image_filename_from_src(int max_len, unsigned char *src)
{
unsigned char *text = NULL;
unsigned char *start, *filename;
int len;
if (!src) return NULL;
/* We can display image as [foo.gif]. */
len = strcspn(src, "?");
for (start = src + len; start > src; start--)
if (dir_sep(start[-1])) {
break;
}
len -= start - src;
filename = memacpy(start, len);
if (filename) {
/* XXX: Due to a compatibility alias (added: 2004-12-15 in
* 0.10pre3.CVS for document.browse.images.file_tags) this can
* return a negative @max_len. */
text = truncate_label(filename, max_len);
mem_free(filename);
}
return text;
}
/* Returns an allocated string containing formatted @label. */
static unsigned char *
get_image_label(int max_len, unsigned char *label)
{
unsigned char *formatted_label;
if (!label) return NULL;
formatted_label = truncate_label(label, max_len);
mem_free(label);
return formatted_label;
}
static void
put_image_label(unsigned char *a, unsigned char *label,
struct html_context *html_context)
{
color_T fg;
/* This is not 100% appropriate for
, but well, accepting
* accesskey and tabindex near
is just our little
* extension to the standard. After all, it makes sense. */
html_focusable(html_context, a);
fg = format.style.fg;
format.style.fg = format.image_link;
put_chrs(html_context, label, strlen(label));
format.style.fg = fg;
}
static void
html_img_do(unsigned char *a, unsigned char *object_src,
struct html_context *html_context)
{
int ismap, usemap = 0;
int add_brackets = 0;
unsigned char *src = NULL;
unsigned char *label = NULL;
unsigned char *usemap_attr;
struct document_options *options = html_context->options;
int display_style = options->image_link.display_style;
/* Note about display_style:
* 0 means always display IMG
* 1 means always display filename
* 2 means display alt/title attribute if possible, IMG if not
* 3 means display alt/title attribute if possible, filename if not */
usemap_attr = get_attr_val(a, "usemap", options);
if (usemap_attr) {
unsigned char *joined_urls = join_urls(html_context->base_href,
usemap_attr);
unsigned char *map_url;
mem_free(usemap_attr);
if (!joined_urls) return;
map_url = straconcat("MAP@", joined_urls, NULL);
mem_free(joined_urls);
if (!map_url) return;
html_stack_dup(html_context, ELEMENT_KILLABLE);
mem_free_set(&format.link, map_url);
format.form = NULL;
format.style.attr |= AT_BOLD;
usemap = 1;
}
ismap = format.link
&& has_attr(a, "ismap", options)
&& !usemap;
if (display_style == 2 || display_style == 3) {
label = get_attr_val(a, "alt", options);
if (!label)
label = get_attr_val(a, "title", options);
/* Little hack to preserve rendering of [ ], in directory listings,
* but we still want to drop extra spaces in alt or title attribute
* to limit display width on certain websites. --Zas */
if (label && strlen(label) > 5) clr_spaces(label);
}
src = null_or_stracpy(object_src);
if (!src) src = get_url_val(a, "src", options);
if (!src) src = get_url_val(a, "dynsrc", options);
/* If we have no label yet (no title or alt), so
* just use default ones, or image filename. */
if (!label || !*label) {
mem_free_set(&label, NULL);
/* Do we want to display images with no alt/title and with no
* link on them ?
* If not, just exit now. */
if (!options->images && !format.link) {
mem_free_if(src);
if (usemap) kill_html_stack_item(html_context, &html_top);
return;
}
add_brackets = 1;
if (usemap) {
label = stracpy("USEMAP");
} else if (ismap) {
label = stracpy("ISMAP");
} else {
if (display_style == 3)
label = get_image_filename_from_src(options->image_link.filename_maxlen, src);
}
} else {
label = get_image_label(options->image_link.label_maxlen, label);
}
if (!label || !*label) {
mem_free_set(&label, NULL);
add_brackets = 1;
if (display_style == 1)
label = get_image_filename_from_src(options->image_link.filename_maxlen, src);
if (!label || !*label)
mem_free_set(&label, stracpy("IMG"));
}
mem_free_set(&format.image, NULL);
mem_free_set(&format.title, NULL);
if (label) {
int img_link_tag = options->image_link.tagging;
if (img_link_tag && (img_link_tag == 2 || add_brackets)) {
unsigned char *img_link_prefix = options->image_link.prefix;
unsigned char *img_link_suffix = options->image_link.suffix;
unsigned char *new_label = straconcat(img_link_prefix, label, img_link_suffix, NULL);
if (new_label) mem_free_set(&label, new_label);
}
if (!options->image_link.show_any_as_links) {
put_image_label(a, label, html_context);
} else {
if (src) {
format.image = join_urls(html_context->base_href, src);
}
format.title = get_attr_val(a, "title", options);
if (ismap) {
unsigned char *new_link;
html_stack_dup(html_context, ELEMENT_KILLABLE);
new_link = straconcat(format.link, "?0,0", NULL);
if (new_link)
mem_free_set(&format.link, new_link);
}
put_image_label(a, label, html_context);
if (ismap) kill_html_stack_item(html_context, &html_top);
mem_free_set(&format.image, NULL);
mem_free_set(&format.title, NULL);
}
mem_free(label);
}
mem_free_if(src);
if (usemap) kill_html_stack_item(html_context, &html_top);
}
void
html_img(struct html_context *html_context, unsigned char *a,
unsigned char *xxx3, unsigned char *xxx4, unsigned char **xxx5)
{
html_img_do(a, NULL, html_context);
}
void
put_link_line(unsigned char *prefix, unsigned char *linkname,
unsigned char *link, unsigned char *target,
struct html_context *html_context)
{
html_context->has_link_lines = 1;
html_stack_dup(html_context, ELEMENT_KILLABLE);
ln_break(html_context, 1);
mem_free_set(&format.link, NULL);
mem_free_set(&format.target, NULL);
mem_free_set(&format.title, NULL);
format.form = NULL;
put_chrs(html_context, prefix, strlen(prefix));
format.link = join_urls(html_context->base_href, link);
format.target = stracpy(target);
format.style.fg = format.clink;
put_chrs(html_context, linkname, strlen(linkname));
ln_break(html_context, 1);
kill_html_stack_item(html_context, &html_top);
}
void
html_applet(struct html_context *html_context, unsigned char *a,
unsigned char *xxx3, unsigned char *xxx4, unsigned char **xxx5)
{
unsigned char *code, *alt;
code = get_url_val(a, "code", html_context->options);
if (!code) return;
alt = get_attr_val(a, "alt", html_context->options);
html_focusable(html_context, a);
if (alt && *alt) {
put_link_line("Applet: ", alt, code,
html_context->options->framename, html_context);
} else {
put_link_line("", "Applet", code,
html_context->options->framename, html_context);
}
mem_free_if(alt);
mem_free(code);
}
static void
html_iframe_do(unsigned char *a, unsigned char *object_src,
struct html_context *html_context)
{
unsigned char *name, *url = NULL;
url = null_or_stracpy(object_src);
if (!url) url = get_url_val(a, "src", html_context->options);
if (!url) return;
name = get_attr_val(a, "name", html_context->options);
if (!name) name = get_attr_val(a, "id", html_context->options);
if (!name) name = stracpy("");
if (!name) {
mem_free(url);
return;
}
html_focusable(html_context, a);
if (*name) {
put_link_line("IFrame: ", name, url,
html_context->options->framename, html_context);
} else {
put_link_line("", "IFrame", url,
html_context->options->framename, html_context);
}
mem_free(name);
mem_free(url);
}
void
html_iframe(struct html_context *html_context, unsigned char *a,
unsigned char *xxx3, unsigned char *xxx4, unsigned char **xxx5)
{
html_iframe_do(a, NULL, html_context);
}
void
html_object(struct html_context *html_context, unsigned char *a,
unsigned char *xxx3, unsigned char *xxx4, unsigned char **xxx5)
{
unsigned char *type, *url;
/* This is just some dirty wrapper. We emulate various things through
* this, which is anyway in the spirit of