1
0
mirror of https://github.com/rkd77/elinks.git synced 2025-02-02 15:09:23 -05:00
Kalle Olavi Niemitalo 200e36c002 bug 1080: Fold dump_color_mode* functions together
Instead of having four separate function definitions, have just one
sprinkled with #ifdefs, and #include that four times.  The purpose
being to make it clearer which parts of these functions are identical
and which ones differ.

As a side effect, this change makes ELinks ignore --dump-color-mode
when dumping in UTF-8.  Colourful UTF-8 dumping has not been
implemented and the fallback is now different from before.
2009-06-09 00:06:10 +03:00

526 lines
11 KiB
C

/* Support for dumping to the file on startup (w/o bfu) */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h> /* NetBSD flavour */
#ifdef HAVE_SYS_SIGNAL_H
#include <sys/signal.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h> /* OS/2 needs this after sys/types.h */
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "elinks.h"
#include "cache/cache.h"
#include "config/options.h"
#include "document/document.h"
#include "document/options.h"
#include "document/renderer.h"
#include "document/view.h"
#include "intl/charsets.h"
#include "intl/gettext/libintl.h"
#include "main/select.h"
#include "main/main.h"
#include "network/connection.h"
#include "network/state.h"
#include "osdep/ascii.h"
#include "osdep/osdep.h"
#include "protocol/protocol.h"
#include "protocol/uri.h"
#include "session/download.h"
#include "terminal/color.h"
#include "terminal/hardio.h"
#include "terminal/terminal.h"
#include "util/memory.h"
#include "util/string.h"
#include "viewer/dump/dump.h"
#include "viewer/text/view.h"
#include "viewer/text/vs.h"
static int dump_pos;
static struct download dump_download;
static int dump_redir_count = 0;
static int dump_to_file_16(struct document *document, int fd);
#if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
static int dump_to_file_256(struct document *document, int fd);
#endif
#ifdef CONFIG_TRUE_COLOR
static int dump_to_file_true_color(struct document *document, int fd);
#endif
/* This dumps the given @cached's source onto @fd nothing more. It returns 0 if it
* all went fine and 1 if something isn't quite right and we should terminate
* ourselves ASAP. */
static int
dump_source(int fd, struct download *download, struct cache_entry *cached)
{
struct fragment *frag;
if (!cached) return 0;
nextfrag:
foreach (frag, cached->frag) {
int d = dump_pos - frag->offset;
int l, w;
if (d < 0 || frag->length <= d)
continue;
l = frag->length - d;
w = hard_write(fd, frag->data + d, l);
if (w != l) {
detach_connection(download, dump_pos);
if (w < 0)
ERROR(gettext("Can't write to stdout: %s"),
(unsigned char *) strerror(errno));
else
ERROR(gettext("Can't write to stdout."));
program.retval = RET_ERROR;
return 1;
}
dump_pos += w;
detach_connection(download, dump_pos);
goto nextfrag;
}
return 0;
}
/* This dumps the given @cached's formatted output onto @fd. */
static void
dump_formatted(int fd, struct download *download, struct cache_entry *cached)
{
struct document_options o;
struct document_view formatted;
struct view_state vs;
int width;
if (!cached) return;
memset(&formatted, 0, sizeof(formatted));
init_document_options(&o);
width = get_opt_int("document.dump.width");
set_box(&o.box, 0, 1, width, DEFAULT_TERMINAL_HEIGHT);
o.cp = get_opt_codepage("document.dump.codepage");
o.color_mode = get_opt_int("document.dump.color_mode");
o.plain = 0;
o.frames = 0;
o.links_numbering = get_opt_bool("document.dump.numbering");
init_vs(&vs, cached->uri, -1);
render_document(&vs, &formatted, &o);
switch(o.color_mode) {
case COLOR_MODE_DUMP:
case COLOR_MODE_MONO: /* FIXME: inversion */
dump_to_file(formatted.document, fd);
break;
default:
/* If the desired color mode was not compiled in,
* use 16 colors. */
case COLOR_MODE_16:
dump_to_file_16(formatted.document, fd);
break;
#ifdef CONFIG_88_COLORS
case COLOR_MODE_88:
dump_to_file_256(formatted.document, fd);
break;
#endif
#ifdef CONFIG_256_COLORS
case COLOR_MODE_256:
dump_to_file_256(formatted.document, fd);
break;
#endif
#ifdef CONFIG_TRUE_COLOR
case COLOR_MODE_TRUE_COLOR:
dump_to_file_true_color(formatted.document, fd);
break;
#endif
}
detach_formatted(&formatted);
destroy_vs(&vs, 1);
}
static unsigned char *
subst_url(unsigned char *str, struct string *url)
{
struct string string;
if (!init_string(&string)) return NULL;
while (*str) {
int p;
for (p = 0; str[p] && str[p] != '%' && str[p] != '\\'; p++);
add_bytes_to_string(&string, str, p);
str += p;
if (*str == '\\') {
unsigned char ch;
str++;
switch (*str) {
case 'f':
ch = '\f';
break;
case 'n':
ch = '\n';
break;
case 't':
ch = '\t';
break;
default:
ch = *str;
}
if (*str) {
add_char_to_string(&string, ch);
str++;
}
continue;
} else if (*str != '%') {
break;
}
str++;
switch (*str) {
case 'u':
if (url) add_string_to_string(&string, url);
break;
}
if (*str) str++;
}
return string.source;
}
static void
dump_print(unsigned char *option, struct string *url)
{
unsigned char *str = get_opt_str(option);
if (str) {
unsigned char *realstr = subst_url(str, url);
if (realstr) {
printf("%s", realstr);
fflush(stdout);
mem_free(realstr);
}
}
}
static void
dump_loading_callback(struct download *download, void *p)
{
struct cache_entry *cached = download->cached;
int fd = get_output_handle();
if (fd == -1) return;
if (cached && cached->redirect && dump_redir_count++ < MAX_REDIRECTS) {
struct uri *uri = cached->redirect;
cancel_download(download, 0);
load_uri(uri, cached->uri, download, PRI_MAIN, 0, -1);
return;
}
if (is_in_queued_state(download->state)) return;
if (get_cmd_opt_bool("dump")) {
if (is_in_transfering_state(download->state))
return;
dump_formatted(fd, download, cached);
} else {
if (dump_source(fd, download, cached) > 0)
goto terminate;
if (is_in_progress_state(download->state))
return;
}
if (!is_in_state(download->state, S_OK)) {
usrerror(get_state_message(download->state, NULL));
program.retval = RET_ERROR;
goto terminate;
}
terminate:
program.terminate = 1;
dump_next(NULL);
}
static void
dump_start(unsigned char *url)
{
unsigned char *wd = get_cwd();
struct uri *uri = get_translated_uri(url, wd);
mem_free_if(wd);
if (!uri || get_protocol_external_handler(NULL, uri)) {
usrerror(gettext("URL protocol not supported (%s)."), url);
goto terminate;
}
dump_download.callback = (download_callback_T *) dump_loading_callback;
dump_pos = 0;
if (load_uri(uri, NULL, &dump_download, PRI_MAIN, 0, -1)) {
terminate:
dump_next(NULL);
program.terminate = 1;
program.retval = RET_SYNTAX;
}
if (uri) done_uri(uri);
}
void
dump_next(LIST_OF(struct string_list_item) *url_list)
{
static INIT_LIST_OF(struct string_list_item, todo_list);
static INIT_LIST_OF(struct string_list_item, done_list);
struct string_list_item *item;
if (url_list) {
/* Steal all them nice list items but keep the same order */
while (!list_empty(*url_list)) {
item = url_list->next;
del_from_list(item);
add_to_list_end(todo_list, item);
}
}
/* Dump each url list item one at a time */
if (!list_empty(todo_list)) {
static int first = 1;
program.terminate = 0;
item = todo_list.next;
del_from_list(item);
add_to_list(done_list, item);
if (!first) {
dump_print("document.dump.separator", NULL);
} else {
first = 0;
}
dump_print("document.dump.header", &item->string);
dump_start(item->string.source);
/* XXX: I think it ought to print footer at the end of
* the whole dump (not only this file). Testing required.
* --pasky */
dump_print("document.dump.footer", &item->string);
} else {
free_string_list(&done_list);
program.terminate = 1;
}
}
/* Using this function in dump_to_file() is unfortunately slightly slower than
* the current code. However having this here instead of in the scripting
* backends is better. */
struct string *
add_document_to_string(struct string *string, struct document *document)
{
int y;
assert(string && document);
if_assert_failed return NULL;
#ifdef CONFIG_UTF8
if (is_cp_utf8(document->options.cp))
goto utf8;
#endif /* CONFIG_UTF8 */
for (y = 0; y < document->height; y++) {
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];
if (data <= ' ') {
/* Count spaces. */
white++;
} else {
/* Print spaces if any. */
if (white) {
add_xchar_to_string(string, ' ', white);
white = 0;
}
add_char_to_string(string, data);
}
}
}
add_char_to_string(string, '\n');
}
#ifdef CONFIG_UTF8
goto end;
utf8:
for (y = 0; y < document->height; y++) {
int white = 0;
int x;
for (x = 0; x < document->data[y].length; x++) {
struct screen_char *pos = &document->data[y].chars[x];
unicode_val_T data = pos->data;
unsigned int frame = (pos->attr & SCREEN_ATTR_FRAME);
if (!isscreensafe_ucs(data)) {
white++;
continue;
} else {
if (frame && data >= 176 && data < 224)
data = frame_dumb[data - 176];
if (data <= ' ') {
/* Count spaces. */
white++;
} else if (data == UCS_NO_CHAR) {
/* This is the second cell of
* a double-cell character. */
} else {
/* Print spaces if any. */
if (white) {
add_xchar_to_string(string, ' ', white);
white = 0;
}
add_to_string(string, encode_utf8(data));
}
}
}
add_char_to_string(string, '\n');
}
end:
#endif /* CONFIG_UTF8 */
return string;
}
#define D_BUF 65536
static int
write_char(unsigned char c, int fd, unsigned char *buf, int *bptr)
{
buf[(*bptr)++] = c;
if ((*bptr) >= D_BUF) {
if (hard_write(fd, buf, (*bptr)) != (*bptr))
return -1;
(*bptr) = 0;
}
return 0;
}
static int
write_color_16(unsigned char color, int fd, unsigned char *buf, int *bptr)
{
unsigned char bufor[] = "\033[0;30;40m";
unsigned char *data = bufor;
int background = (color >> 4) & 7;
int foreground = color & 7;
bufor[5] += foreground;
if (background) bufor[8] += background;
else {
bufor[6] = 'm';
bufor[7] = '\0';
}
while(*data) {
if (write_char(*data++, fd, buf, bptr)) return -1;
}
return 0;
}
#define DUMP_COLOR_MODE_16
#include "dump-color-mode.h"
#undef DUMP_COLOR_MODE_16
/* configure --enable-debug uses gcc -Wall -Werror, and -Wall includes
* -Wunused-function, so declaring or defining any unused function
* would break the build. */
#if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
static int
write_color_256(unsigned char *str, unsigned char color, int fd, unsigned char *buf, int *bptr)
{
unsigned char bufor[16];
unsigned char *data = bufor;
snprintf(bufor, 16, "\033[%s;5;%dm", str, color);
while(*data) {
if (write_char(*data++, fd, buf, bptr)) return -1;
}
return 0;
}
#define DUMP_COLOR_MODE_256
#include "dump-color-mode.h"
#undef DUMP_COLOR_MODE_256
#endif /* defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS) */
#ifdef CONFIG_TRUE_COLOR
static int
write_true_color(unsigned char *str, unsigned char *color, int fd, unsigned char *buf, int *bptr)
{
unsigned char bufor[24];
unsigned char *data = bufor;
snprintf(bufor, 24, "\033[%s;2;%d;%d;%dm", str, color[0], color[1], color[2]);
while(*data) {
if (write_char(*data++, fd, buf, bptr)) return -1;
}
return 0;
}
#define DUMP_COLOR_MODE_TRUE
#include "dump-color-mode.h"
#undef DUMP_COLOR_MODE_TRUE
#endif /* CONFIG_TRUE_COLOR */
#include "dump-color-mode.h"
#undef D_BUF