Lines filled with spaces
[elinks.git] / src / viewer / dump / dump.c
blob6179f6f6835398caf3edac1397261d0a58b1d224
1 /* Support for dumping to the file on startup (w/o bfu) */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <sys/types.h> /* NetBSD flavour */
11 #ifdef HAVE_SYS_SIGNAL_H
12 #include <sys/signal.h>
13 #endif
14 #ifdef HAVE_FCNTL_H
15 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
16 #endif
17 #ifdef HAVE_UNISTD_H
18 #include <unistd.h>
19 #endif
21 #include "elinks.h"
23 #include "cache/cache.h"
24 #include "config/options.h"
25 #include "document/document.h"
26 #include "document/options.h"
27 #include "document/renderer.h"
28 #include "document/view.h"
29 #include "intl/gettext/libintl.h"
30 #include "main/select.h"
31 #include "main/main.h"
32 #include "network/connection.h"
33 #include "network/state.h"
34 #include "osdep/ascii.h"
35 #include "osdep/osdep.h"
36 #include "protocol/protocol.h"
37 #include "protocol/uri.h"
38 #include "session/download.h"
39 #include "terminal/color.h"
40 #include "terminal/hardio.h"
41 #include "terminal/terminal.h"
42 #include "util/memory.h"
43 #include "util/string.h"
44 #include "viewer/dump/dump.h"
45 #include "viewer/text/view.h"
46 #include "viewer/text/vs.h"
49 static int dump_pos;
50 static struct download dump_download;
51 static int dump_redir_count = 0;
53 static int dump_to_file_16(struct document *document, int fd);
54 static int dump_to_file_256(struct document *document, int fd);
56 /* This dumps the given @cached's source onto @fd nothing more. It returns 0 if it
57 * all went fine and 1 if something isn't quite right and we should terminate
58 * ourselves ASAP. */
59 static int
60 dump_source(int fd, struct download *download, struct cache_entry *cached)
62 struct fragment *frag;
64 if (!cached) return 0;
66 nextfrag:
67 foreach (frag, cached->frag) {
68 int d = dump_pos - frag->offset;
69 int l, w;
71 if (d < 0 || frag->length <= d)
72 continue;
74 l = frag->length - d;
75 w = hard_write(fd, frag->data + d, l);
77 if (w != l) {
78 detach_connection(download, dump_pos);
80 if (w < 0)
81 ERROR(gettext("Can't write to stdout: %s"),
82 (unsigned char *) strerror(errno));
83 else
84 ERROR(gettext("Can't write to stdout."));
86 program.retval = RET_ERROR;
87 return 1;
90 dump_pos += w;
91 detach_connection(download, dump_pos);
92 goto nextfrag;
95 return 0;
98 /* This dumps the given @cached's formatted output onto @fd. */
99 static void
100 dump_formatted(int fd, struct download *download, struct cache_entry *cached)
102 struct document_options o;
103 struct document_view formatted;
104 struct view_state vs;
105 int width;
107 if (!cached) return;
109 memset(&formatted, 0, sizeof(formatted));
111 init_document_options(&o);
112 width = get_opt_int("document.dump.width");
113 set_box(&o.box, 0, 1, width, DEFAULT_TERMINAL_HEIGHT);
115 o.cp = get_opt_codepage("document.dump.codepage");
116 o.color_mode = get_opt_int("document.dump.color_mode");
117 o.plain = 0;
118 o.frames = 0;
119 o.links_numbering = get_opt_bool("document.dump.numbering");
121 init_vs(&vs, cached->uri, -1);
123 render_document(&vs, &formatted, &o);
124 switch(o.color_mode) {
125 case COLOR_MODE_DUMP:
126 case COLOR_MODE_MONO: /* FIXME: inversion */
127 dump_to_file(formatted.document, fd);
128 break;
129 case COLOR_MODE_16:
130 dump_to_file_16(formatted.document, fd);
131 break;
132 #ifdef CONFIG_88_COLORS
133 case COLOR_MODE_88:
134 dump_to_file_256(formatted.document, fd);
135 break;
136 #endif
137 #ifdef CONFIG_256_COLORS
138 case COLOR_MODE_256:
139 dump_to_file_256(formatted.document, fd);
140 break;
141 #endif
142 default:
143 break;
146 detach_formatted(&formatted);
147 destroy_vs(&vs, 1);
150 static unsigned char *
151 subst_url(unsigned char *str, struct string *url)
153 struct string string;
155 if (!init_string(&string)) return NULL;
157 while (*str) {
158 int p;
160 for (p = 0; str[p] && str[p] != '%' && str[p] != '\\'; p++);
162 add_bytes_to_string(&string, str, p);
163 str += p;
165 if (*str == '\\') {
166 unsigned char ch;
168 str++;
169 switch (*str) {
170 case 'f':
171 ch = '\f';
172 break;
173 case 'n':
174 ch = '\n';
175 break;
176 case 't':
177 ch = '\t';
178 break;
179 default:
180 ch = *str;
182 if (*str) {
183 add_char_to_string(&string, ch);
184 str++;
186 continue;
188 } else if (*str != '%') {
189 break;
192 str++;
193 switch (*str) {
194 case 'u':
195 if (url) add_string_to_string(&string, url);
196 break;
199 if (*str) str++;
202 return string.source;
205 static void
206 dump_print(unsigned char *option, struct string *url)
208 unsigned char *str = get_opt_str(option);
210 if (str) {
211 unsigned char *realstr = subst_url(str, url);
213 if (realstr) {
214 printf("%s", realstr);
215 fflush(stdout);
216 mem_free(realstr);
221 static void
222 dump_loading_callback(struct download *download, void *p)
224 struct cache_entry *cached = download->cached;
225 int fd = get_output_handle();
227 if (fd == -1) return;
228 if (cached && cached->redirect && dump_redir_count++ < MAX_REDIRECTS) {
229 struct uri *uri = cached->redirect;
231 cancel_download(download, 0);
233 load_uri(uri, cached->uri, download, PRI_MAIN, 0, -1);
234 return;
237 if (is_in_queued_state(download->state)) return;
239 if (get_cmd_opt_bool("dump")) {
240 if (is_in_transfering_state(download->state))
241 return;
243 dump_formatted(fd, download, cached);
245 } else {
246 if (dump_source(fd, download, cached) > 0)
247 goto terminate;
249 if (is_in_progress_state(download->state))
250 return;
254 if (download->state != S_OK) {
255 usrerror(get_state_message(download->state, NULL));
256 program.retval = RET_ERROR;
257 goto terminate;
260 terminate:
261 program.terminate = 1;
262 dump_next(NULL);
265 static void
266 dump_start(unsigned char *url)
268 unsigned char *wd = get_cwd();
269 struct uri *uri = get_translated_uri(url, wd);
271 mem_free_if(wd);
273 if (!uri || get_protocol_external_handler(NULL, uri)) {
274 usrerror(gettext("URL protocol not supported (%s)."), url);
275 goto terminate;
278 dump_download.callback = (download_callback_T *) dump_loading_callback;
279 dump_pos = 0;
281 if (load_uri(uri, NULL, &dump_download, PRI_MAIN, 0, -1)) {
282 terminate:
283 dump_next(NULL);
284 program.terminate = 1;
285 program.retval = RET_SYNTAX;
288 if (uri) done_uri(uri);
291 void
292 dump_next(struct list_head *url_list)
294 static INIT_LIST_HEAD(todo_list);
295 static INIT_LIST_HEAD(done_list);
296 struct string_list_item *item;
298 if (url_list) {
299 /* Steal all them nice list items but keep the same order */
300 while (!list_empty(*url_list)) {
301 item = url_list->next;
302 del_from_list(item);
303 add_to_list_end(todo_list, item);
307 /* Dump each url list item one at a time */
308 if (!list_empty(todo_list)) {
309 static int first = 1;
311 program.terminate = 0;
313 item = todo_list.next;
314 del_from_list(item);
315 add_to_list(done_list, item);
317 if (!first) {
318 dump_print("document.dump.separator", NULL);
319 } else {
320 first = 0;
323 dump_print("document.dump.header", &item->string);
324 dump_start(item->string.source);
325 /* XXX: I think it ought to print footer at the end of
326 * the whole dump (not only this file). Testing required.
327 * --pasky */
328 dump_print("document.dump.footer", &item->string);
330 } else {
331 free_string_list(&done_list);
332 program.terminate = 1;
336 /* Using this function in dump_to_file() is unfortunately slightly slower than
337 * the current code. However having this here instead of in the scripting
338 * backends is better. */
339 struct string *
340 add_document_to_string(struct string *string, struct document *document)
342 int y;
344 assert(string && document);
345 if_assert_failed return NULL;
347 for (y = 0; y < document->height; y++) {
348 int white = 0;
349 int x;
351 for (x = 0; x < document->data[y].length; x++) {
352 struct screen_char *pos = &document->data[y].chars[x];
353 unsigned char data = pos->data;
354 unsigned int frame = (pos->attr & SCREEN_ATTR_FRAME);
356 if (!isscreensafe(data)) {
357 white++;
358 continue;
359 } else {
360 if (frame && data >= 176 && data < 224)
361 data = frame_dumb[data - 176];
363 if (data <= ' ') {
364 /* Count spaces. */
365 white++;
366 } else {
367 /* Print spaces if any. */
368 if (white) {
369 add_xchar_to_string(string, ' ', white);
370 white = 0;
372 add_char_to_string(string, data);
377 add_char_to_string(string, '\n');
380 return string;
383 #define D_BUF 65536
385 static int
386 write_char(unsigned char c, int fd, unsigned char *buf, int *bptr)
388 buf[(*bptr)++] = c;
389 if ((*bptr) >= D_BUF) {
390 if (hard_write(fd, buf, (*bptr)) != (*bptr))
391 return -1;
392 (*bptr) = 0;
395 return 0;
398 static int
399 write_color_16(unsigned char color, int fd, unsigned char *buf, int *bptr)
401 unsigned char bufor[] = "\033[0;30;40m";
402 unsigned char *data = bufor;
403 int background = (color >> 4) & 7;
404 int foreground = color & 7;
406 bufor[5] += foreground;
407 if (background) bufor[8] += background;
408 else {
409 bufor[6] = 'm';
410 bufor[7] = '\0';
412 while(*data) {
413 if (write_char(*data++, fd, buf, bptr)) return -1;
415 return 0;
419 static int
420 dump_to_file_16(struct document *document, int fd)
422 int y;
423 int bptr = 0;
424 unsigned char *buf = mem_alloc(D_BUF);
425 unsigned char color = 0;
426 int width = get_opt_int("document.dump.width");
428 if (!buf) return -1;
430 for (y = 0; y < document->height; y++) {
431 int white = 0;
432 int x;
434 write_color_16(color, fd, buf, &bptr);
435 for (x = 0; x < document->data[y].length; x++) {
436 unsigned char c;
437 unsigned char attr = document->data[y].chars[x].attr;
438 unsigned char color1 = document->data[y].chars[x].color[0];
440 if (color != color1) {
441 color = color1;
442 if (write_color_16(color, fd, buf, &bptr))
443 goto fail;
446 c = document->data[y].chars[x].data;
448 if ((attr & SCREEN_ATTR_FRAME)
449 && c >= 176 && c < 224)
450 c = frame_dumb[c - 176];
452 if (c <= ' ') {
453 /* Count spaces. */
454 white++;
455 continue;
458 /* Print spaces if any. */
459 while (white) {
460 if (write_char(' ', fd, buf, &bptr))
461 goto fail;
462 white--;
465 /* Print normal char. */
466 if (write_char(c, fd, buf, &bptr))
467 goto fail;
469 for (;x < width; x++) {
470 if (write_char(' ', fd, buf, &bptr))
471 goto fail;
474 /* Print end of line. */
475 if (write_char('\n', fd, buf, &bptr))
476 goto fail;
479 if (hard_write(fd, buf, bptr) != bptr) {
480 fail:
481 mem_free(buf);
482 return -1;
485 if (document->nlinks && get_opt_bool("document.dump.references")) {
486 int x;
487 unsigned char *header = "\nReferences\n\n Visible links\n";
488 int headlen = strlen(header);
490 if (hard_write(fd, header, headlen) != headlen)
491 goto fail;
493 for (x = 0; x < document->nlinks; x++) {
494 struct link *link = &document->links[x];
495 unsigned char *where = link->where;
497 if (!where) continue;
499 if (document->options.links_numbering) {
500 if (link->title && *link->title)
501 snprintf(buf, D_BUF, "%4d. %s\n\t%s\n",
502 x + 1, link->title, where);
503 else
504 snprintf(buf, D_BUF, "%4d. %s\n",
505 x + 1, where);
506 } else {
507 if (link->title && *link->title)
508 snprintf(buf, D_BUF, " . %s\n\t%s\n",
509 link->title, where);
510 else
511 snprintf(buf, D_BUF, " . %s\n", where);
514 bptr = strlen(buf);
515 if (hard_write(fd, buf, bptr) != bptr)
516 goto fail;
520 mem_free(buf);
521 return 0;
524 static int
525 write_color_256(unsigned char *str, unsigned char color, int fd, unsigned char *buf, int *bptr)
527 unsigned char bufor[16];
528 unsigned char *data = bufor;
530 snprintf(bufor, 16, "\033[%s;5;%dm", str, color);
531 while(*data) {
532 if (write_char(*data++, fd, buf, bptr)) return -1;
534 return 0;
537 static int
538 dump_to_file_256(struct document *document, int fd)
540 int y;
541 int bptr = 0;
542 unsigned char *buf = mem_alloc(D_BUF);
543 unsigned char foreground = 0;
544 unsigned char background = 0;
545 int width = get_opt_int("document.dump.width");
547 if (!buf) return -1;
549 for (y = 0; y < document->height; y++) {
550 int white = 0;
551 int x;
552 write_color_256("38", foreground, fd, buf, &bptr);
553 write_color_256("48", background, fd, buf, &bptr);
555 for (x = 0; x < document->data[y].length; x++) {
556 unsigned char c;
557 unsigned char attr = document->data[y].chars[x].attr;
558 unsigned char color1 = document->data[y].chars[x].color[0];
559 unsigned char color2 = document->data[y].chars[x].color[1];
561 if (foreground != color1) {
562 foreground = color1;
563 if (write_color_256("38", foreground, fd, buf, &bptr))
564 goto fail;
567 if (background != color2) {
568 background = color2;
569 if (write_color_256("48", background, fd, buf, &bptr))
570 goto fail;
573 c = document->data[y].chars[x].data;
575 if ((attr & SCREEN_ATTR_FRAME)
576 && c >= 176 && c < 224)
577 c = frame_dumb[c - 176];
579 if (c <= ' ') {
580 /* Count spaces. */
581 white++;
582 continue;
585 /* Print spaces if any. */
586 while (white) {
587 if (write_char(' ', fd, buf, &bptr))
588 goto fail;
589 white--;
592 /* Print normal char. */
593 if (write_char(c, fd, buf, &bptr))
594 goto fail;
596 for (;x < width; x++) {
597 if (write_char(' ', fd, buf, &bptr))
598 goto fail;
601 /* Print end of line. */
602 if (write_char('\n', fd, buf, &bptr))
603 goto fail;
606 if (hard_write(fd, buf, bptr) != bptr) {
607 fail:
608 mem_free(buf);
609 return -1;
612 if (document->nlinks && get_opt_bool("document.dump.references")) {
613 int x;
614 unsigned char *header = "\nReferences\n\n Visible links\n";
615 int headlen = strlen(header);
617 if (hard_write(fd, header, headlen) != headlen)
618 goto fail;
620 for (x = 0; x < document->nlinks; x++) {
621 struct link *link = &document->links[x];
622 unsigned char *where = link->where;
624 if (!where) continue;
626 if (document->options.links_numbering) {
627 if (link->title && *link->title)
628 snprintf(buf, D_BUF, "%4d. %s\n\t%s\n",
629 x + 1, link->title, where);
630 else
631 snprintf(buf, D_BUF, "%4d. %s\n",
632 x + 1, where);
633 } else {
634 if (link->title && *link->title)
635 snprintf(buf, D_BUF, " . %s\n\t%s\n",
636 link->title, where);
637 else
638 snprintf(buf, D_BUF, " . %s\n", where);
641 bptr = strlen(buf);
642 if (hard_write(fd, buf, bptr) != bptr)
643 goto fail;
647 mem_free(buf);
648 return 0;
653 dump_to_file(struct document *document, int fd)
655 int y;
656 int bptr = 0;
657 unsigned char *buf = mem_alloc(D_BUF);
659 if (!buf) return -1;
661 for (y = 0; y < document->height; y++) {
662 int white = 0;
663 int x;
665 for (x = 0; x < document->data[y].length; x++) {
666 unsigned char c;
667 unsigned char attr = document->data[y].chars[x].attr;
669 c = document->data[y].chars[x].data;
671 if ((attr & SCREEN_ATTR_FRAME)
672 && c >= 176 && c < 224)
673 c = frame_dumb[c - 176];
675 if (c <= ' ') {
676 /* Count spaces. */
677 white++;
678 continue;
681 /* Print spaces if any. */
682 while (white) {
683 if (write_char(' ', fd, buf, &bptr))
684 goto fail;
685 white--;
688 /* Print normal char. */
689 if (write_char(c, fd, buf, &bptr))
690 goto fail;
693 /* Print end of line. */
694 if (write_char('\n', fd, buf, &bptr))
695 goto fail;
698 if (hard_write(fd, buf, bptr) != bptr) {
699 fail:
700 mem_free(buf);
701 return -1;
704 if (document->nlinks && get_opt_bool("document.dump.references")) {
705 int x;
706 unsigned char *header = "\nReferences\n\n Visible links\n";
707 int headlen = strlen(header);
709 if (hard_write(fd, header, headlen) != headlen)
710 goto fail;
712 for (x = 0; x < document->nlinks; x++) {
713 struct link *link = &document->links[x];
714 unsigned char *where = link->where;
716 if (!where) continue;
718 if (document->options.links_numbering) {
719 if (link->title && *link->title)
720 snprintf(buf, D_BUF, "%4d. %s\n\t%s\n",
721 x + 1, link->title, where);
722 else
723 snprintf(buf, D_BUF, "%4d. %s\n",
724 x + 1, where);
725 } else {
726 if (link->title && *link->title)
727 snprintf(buf, D_BUF, " . %s\n\t%s\n",
728 link->title, where);
729 else
730 snprintf(buf, D_BUF, " . %s\n", where);
733 bptr = strlen(buf);
734 if (hard_write(fd, buf, bptr) != bptr)
735 goto fail;
739 mem_free(buf);
740 return 0;
743 #undef D_BUF