⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 renderer.c

📁 一个很有名的浏览器
💻 C
字号:
/* Plain text document renderer *//* $Id: renderer.c,v 1.171.2.3 2005/04/05 21:08:41 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <ctype.h>#include <string.h>#include "elinks.h"#include "main.h"#include "bookmarks/bookmarks.h"#include "cache/cache.h"#include "config/options.h"#include "document/docdata.h"#include "document/document.h"#include "document/options.h"#include "document/plain/renderer.h"#include "document/renderer.h"#include "globhist/globhist.h"#include "intl/charsets.h"#include "protocol/protocol.h"#include "protocol/uri.h"#include "terminal/color.h"#include "terminal/draw.h"#include "util/color.h"#include "util/error.h"#include "util/memory.h"#include "util/string.h"struct plain_renderer {	/* The document being renderered */	struct document *document;	/* The data and data length of the defragmented cache entry */	unsigned char *source;	int length;	/* The convert table that should be used for converting line strings to	 * the rendered strings. */	struct conv_table *convert_table;	/* The default template char data for text */	struct screen_char template;	/* The maximum width any line can have (used for wrapping text) */	int max_width;	/* The current line number */	int lineno;	/* Are we doing line compression */	unsigned int compress:1;};#define realloc_document_links(doc, size) \	ALIGN_LINK(&(doc)->links, (doc)->nlinks, size)static struct screen_char *realloc_line(struct document *document, int x, int y){	struct line *line = realloc_lines(document, y);	if (!line) return NULL;	if (x > line->length) {		if (!ALIGN_LINE(&line->chars, line->length, x))			return NULL;		line->length = x;	}	return line->chars;}static inline struct link *add_document_link(struct document *document, unsigned char *uri, int length,		  int x, int y){	struct link *link;	struct point *point;	if (!realloc_document_links(document, document->nlinks + 1))		return NULL;	link = &document->links[document->nlinks];	if (!realloc_points(link, length))		return NULL;	link->npoints = length;	link->type = LINK_HYPERTEXT;	link->where = uri;	link->color.background = document->options.default_bg;	link->color.foreground = document->options.default_link;	for (point = link->points; length > 0; length--, point++, x++) {		point->x = x;		point->y = y;	}	document->nlinks++;	return link;}/* Searches a word to find an email adress or an URI to add as a link. */static inline struct link *check_link_word(struct document *document, unsigned char *uri, int length,		int x, int y){	struct uri test;	unsigned char *where = NULL;	unsigned char *mailto = memchr(uri, '@', length);	int keep = uri[length];	struct link *new_link;	assert(document);	if_assert_failed return NULL;	uri[length] = 0;	if (mailto && mailto > uri && mailto - uri < length - 1) {		where = straconcat("mailto:", uri, NULL);	} else if (parse_uri(&test, uri) == URI_ERRNO_OK		   && test.protocol != PROTOCOL_UNKNOWN		   && (test.datalen || test.hostlen)) {		where = memacpy(uri, length);	}	uri[length] = keep;	if (!where) return NULL;	new_link = add_document_link(document, where, length, x, y);	if (!new_link) mem_free(where);	return new_link;}#define url_char(c) (		\		(c) > ' '	\		&& (c) != '<'	\		&& (c) != '>'	\		&& (c) != '('	\		&& (c) != ')'	\		&& !isquote(c))static inline intget_uri_length(unsigned char *line, int length){	int uri_end = 0;	while (uri_end < length	       && url_char(line[uri_end]))		uri_end++;	for (; uri_end > 0; uri_end--) {		if (line[uri_end - 1] != '.'		    && line[uri_end - 1] != ',')			break;	}	return uri_end;}static intprint_document_link(struct plain_renderer *renderer, int lineno,		    unsigned char *line, int line_pos, int width,		    int expanded, struct screen_char *pos){	struct document *document = renderer->document;	unsigned char *start = &line[line_pos];	int len = get_uri_length(start, width - line_pos);	int screen_column = line_pos + expanded;	struct link *new_link;	int link_end = line_pos + len;	unsigned char saved_char;	struct document_options *doc_opts = &document->options;	struct screen_char template = renderer->template;	int i;	if (!len) return 0;	new_link = check_link_word(document, start, len, screen_column,				   lineno);	if (!new_link) return 0;	saved_char = line[link_end];	line[link_end] = '\0';	if (0)		; /* Shut up compiler */#ifdef CONFIG_GLOBHIST	else if (get_global_history_item(start))		new_link->color.foreground = doc_opts->default_vlink;#endif#ifdef CONFIG_BOOKMARKS	else if (get_bookmark(start))		new_link->color.foreground = doc_opts->default_bookmark_link;#endif	else		new_link->color.foreground = doc_opts->default_link;	line[link_end] = saved_char;	new_link->color.background = doc_opts->default_bg;	set_term_color(&template, &new_link->color,		       doc_opts->color_flags, doc_opts->color_mode);	for (i = len; i; i--) {		template.data = line[line_pos++];		copy_screen_chars(pos++, &template, 1);	}	return len;}static inline intadd_document_line(struct plain_renderer *renderer,		  unsigned char *line, int line_width){	struct document *document = renderer->document;	struct screen_char *template = &renderer->template;	struct screen_char saved_renderer_template = *template;	struct screen_char *pos, *startpos;	int lineno = renderer->lineno;	int expanded = 0;	int width = line_width;	int line_pos;	line = convert_string(renderer->convert_table, line, width, CSM_NONE, &width, NULL, NULL);	if (!line) return 0;	/* Now expand tabs */	for (line_pos = 0; line_pos < width; line_pos++) {		unsigned char line_char = line[line_pos];		if (line_char == ASCII_TAB		    && (line_pos + 1 == width			|| line[line_pos + 1] != ASCII_BS)) {			int tab_width = 7 - ((line_pos + expanded) & 7);			expanded += tab_width;		} else if (line_char == ASCII_BS) {#if 0		This does not work: Suppose we have seventeen spaces		followed by a back-space; that will call for sixteen		bytes of memory, but we will print seventeen spaces		before we hit the back-space -- overflow!			/* Don't count the character			 * that the back-space character will delete */			if (expanded + line_pos)				expanded--;#endif#if 0			/* Don't count the back-space character */			if (expanded > 0)				expanded--;#endif		}	}	assert(expanded >= 0);	startpos = pos = realloc_line(document, width + expanded, lineno);	if (!pos) {		mem_free(line);		return 0;	}	expanded = 0;	for (line_pos = 0; line_pos < width; line_pos++) {		unsigned char line_char = line[line_pos];		unsigned char next_char, prev_char;		prev_char = line_pos > 0 ? line[line_pos - 1] : '\0';		next_char = (line_pos + 1 < width) ? line[line_pos + 1]						   : '\0';		/* Do not expand tabs that precede back-spaces; this saves the		 * back-space code some trouble. */		if (line_char == ASCII_TAB && next_char != ASCII_BS) {			int tab_width = 7 - ((line_pos + expanded) & 7);			expanded += tab_width;			template->data = ' ';			do				copy_screen_chars(pos++, template, 1);			while (tab_width--);			*template = saved_renderer_template;		} else if (line_char == ASCII_BS) {			if (!(expanded + line_pos)) {				/* We've backspaced to the start of the line */				if (expanded > 0)					expanded--; /* Don't count it */				continue;			}			if (pos > startpos)				pos--;  /* Backspace */			/* Handle x^H_ as _^Hx, but prevent an infinite loop			 * swapping two underscores. */			if (next_char == '_'  && prev_char != '_') {				/* x^H_ becomes _^Hx */				if (line_pos - 1 >= 0)					line[line_pos - 1] = next_char;				if (line_pos + 1 < width)					line[line_pos + 1] = prev_char;				/* Go back and reparse the swapped characters */				if (line_pos - 2 >= 0)					line_pos -= 2;				continue;			}			if (expanded - 2 >= 0) {				/* Don't count the backspace character or the				 * deleted character when returning the line's				 * width or when expanding tabs. */				expanded -= 2;			}			if (pos->data == '_' && next_char == '_') {				/* Is _^H_ an underlined underscore				 * or an emboldened underscore? */				if (expanded + line_pos >= 0				    && pos - 1 >= startpos				    && (pos - 1)->attr) {					/* There is some preceding text,					 * and it has an attribute; copy it */					template->attr |= (pos - 1)->attr;				} else {					/* Default to bold; seems more useful					 * than underlining the underscore */					template->attr |= SCREEN_ATTR_BOLD;				}			} else if (pos->data == '_') {				/* Underline _^Hx */				template->attr |= SCREEN_ATTR_UNDERLINE;			} else if (pos->data == next_char) {				/* Embolden x^Hx */				template->attr |= SCREEN_ATTR_BOLD;			}			/* Handle _^Hx^Hx as both bold and underlined */			if (template->attr)				template->attr |= pos->attr;		} else {			int added_chars = 0;			if (document->options.plain_display_links			    && isalpha(line_char) && isalpha(next_char)) {				/* We only want to check for a URI if there are				 * at least two consecutive alphabetic				 * characters, or if we are at the very start of				 * the line.  It improves performance a bit.				 * --Zas */				added_chars = print_document_link(renderer,								  lineno, line,								  line_pos,								  width,								  expanded,								  pos);			}			if (added_chars) {				line_pos += added_chars - 1;				pos += added_chars;			} else {				if (!isscreensafe(line_char))					line_char = '.';				template->data = line_char;				copy_screen_chars(pos++, template, 1);				/* Detect copy of nul chars to screen, this				 * should not occur. --Zas */				assert(line_char);			}			*template = saved_renderer_template;		}	}	mem_free(line);	return width + expanded;}static voidinit_template(struct screen_char *template, color_t background, color_t foreground){	struct color_pair colors = INIT_COLOR_PAIR(background, foreground);	template->attr = 0;	template->data = ' ';	set_term_color(template, &colors, global_doc_opts->color_flags, global_doc_opts->color_mode);}static struct node *add_node(struct plain_renderer *renderer, int x, int width, int height){	struct node *node = mem_alloc(sizeof(*node));	if (node) {		struct document *document = renderer->document;		set_box(&node->box, x, renderer->lineno, width, height);		int_lower_bound(&document->width, width);		int_lower_bound(&document->height, height);		add_to_list(document->nodes, node);	}	return node;}static voidadd_document_lines(struct plain_renderer *renderer){	unsigned char *source = renderer->source;	int length = renderer->length;	int was_empty_line = 0;	for (; length > 0; renderer->lineno++) {		unsigned char *xsource;		int width, added, only_spaces = 1, spaces = 0, was_spaces = 0;		int last_space = 0;		int step = 0;		int doc_width = int_min(renderer->max_width, length);		/* End of line detection: We handle \r, \r\n and \n types. */		for (width = 0; width < doc_width; width++) {			if (source[width] == ASCII_CR)				step++;			if (source[width + step] == ASCII_LF)				step++;			if (step) break;			if (isspace(source[width])) {				last_space = width;				if (only_spaces)					spaces++;				else					was_spaces++;			} else {				only_spaces = 0;				was_spaces = 0;			}		}		if (only_spaces && step) {			if (renderer->compress && was_empty_line) {				/* Successive empty lines will appear as one. */				length -= step + spaces;				source += step + spaces;				renderer->lineno--;				assert(renderer->lineno >= 0);				continue;			}			was_empty_line = 1;			/* No need to keep whitespaces on an empty line. */			source += spaces;			length -= spaces;			width -= spaces;		} else {			was_empty_line = 0;			if (was_spaces && step) {				/* Drop trailing whitespaces. */				width -= was_spaces;				step += was_spaces;			}			if (!step && (width < length) && last_space) {				width = last_space;				step = 1;			}		}		assert(width >= 0);		/* We will touch the supplied source, so better replicate it. */		xsource = memacpy(source, width);		if (!xsource) continue;		added = add_document_line(renderer, xsource, width);		mem_free(xsource);		if (added) {			/* Add (search) nodes on a line by line basis */			add_node(renderer, 0, added, 1);		}		/* Skip end of line chars too. */		width += step;		length -= width;		source += width;	}	assert(!length);}voidrender_plain_document(struct cache_entry *cached, struct document *document,		      struct string *buffer){	struct conv_table *convert_table;	unsigned char *head = empty_string_or_(cached->head);	struct plain_renderer renderer;	convert_table = get_convert_table(head, document->options.cp,					  document->options.assume_cp,					  &document->cp,					  &document->cp_status,					  document->options.hard_assume);	document->bgcolor = global_doc_opts->default_bg;	document->width = 0;	renderer.source = buffer->source;	renderer.length = buffer->length;	renderer.document = document;	renderer.lineno = 0;	renderer.convert_table = convert_table;	renderer.compress = document->options.plain_compress_empty_lines;	renderer.max_width = document->options.wrap ? document->options.box.width						    : INT_MAX;	/* Setup the style */	init_template(&renderer.template, global_doc_opts->default_bg,					  global_doc_opts->default_fg);	add_document_lines(&renderer);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -