1 /* Support for dumping to the file on startup (w/o bfu) */
2
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6
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
20
21 #include "elinks.h"
22
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"
47
48
49 static int dump_pos;
50 static struct download dump_download;
51 static int dump_redir_count = 0;
52
53
54 /* This dumps the given @cached's source onto @fd nothing more. It returns 0 if it
55 * all went fine and 1 if something isn't quite right and we should terminate
56 * ourselves ASAP. */
57 static int
dump_source(int fd,struct download * download,struct cache_entry * cached)58 dump_source(int fd, struct download *download, struct cache_entry *cached)
59 {
60 struct fragment *frag;
61
62 if (!cached) return 0;
63
64 nextfrag:
65 foreach (frag, cached->frag) {
66 int d = dump_pos - frag->offset;
67 int l, w;
68
69 if (d < 0 || frag->length <= d)
70 continue;
71
72 l = frag->length - d;
73 w = hard_write(fd, frag->data + d, l);
74
75 if (w != l) {
76 detach_connection(download, dump_pos);
77
78 if (w < 0)
79 ERROR(gettext("Can't write to stdout: %s"),
80 (unsigned char *) strerror(errno));
81 else
82 ERROR(gettext("Can't write to stdout."));
83
84 program.retval = RET_ERROR;
85 return 1;
86 }
87
88 dump_pos += w;
89 detach_connection(download, dump_pos);
90 goto nextfrag;
91 }
92
93 return 0;
94 }
95
96 /* This dumps the given @cached's formatted output onto @fd. */
97 static void
dump_formatted(int fd,struct download * download,struct cache_entry * cached)98 dump_formatted(int fd, struct download *download, struct cache_entry *cached)
99 {
100 struct document_options o;
101 struct document_view formatted;
102 struct view_state vs;
103 int width;
104
105 if (!cached) return;
106
107 memset(&formatted, 0, sizeof(formatted));
108
109 init_document_options(&o);
110 width = get_opt_int("document.dump.width");
111 set_box(&o.box, 0, 1, width, DEFAULT_TERMINAL_HEIGHT);
112
113 o.cp = get_opt_codepage("document.dump.codepage");
114 o.color_mode = COLOR_MODE_DUMP;
115 o.plain = 0;
116 o.frames = 0;
117 o.links_numbering = get_opt_bool("document.dump.numbering");
118
119 init_vs(&vs, cached->uri, -1);
120
121 render_document(&vs, &formatted, &o);
122 dump_to_file(formatted.document, fd);
123
124 detach_formatted(&formatted);
125 destroy_vs(&vs, 1);
126 }
127
128 static unsigned char *
subst_url(unsigned char * str,struct string * url)129 subst_url(unsigned char *str, struct string *url)
130 {
131 struct string string;
132
133 if (!init_string(&string)) return NULL;
134
135 while (*str) {
136 int p;
137
138 for (p = 0; str[p] && str[p] != '%' && str[p] != '\\'; p++);
139
140 add_bytes_to_string(&string, str, p);
141 str += p;
142
143 if (*str == '\\') {
144 unsigned char ch;
145
146 str++;
147 switch (*str) {
148 case 'f':
149 ch = '\f';
150 break;
151 case 'n':
152 ch = '\n';
153 break;
154 case 't':
155 ch = '\t';
156 break;
157 default:
158 ch = *str;
159 }
160 if (*str) {
161 add_char_to_string(&string, ch);
162 str++;
163 }
164 continue;
165
166 } else if (*str != '%') {
167 break;
168 }
169
170 str++;
171 switch (*str) {
172 case 'u':
173 if (url) add_string_to_string(&string, url);
174 break;
175 }
176
177 if (*str) str++;
178 }
179
180 return string.source;
181 }
182
183 static void
dump_print(unsigned char * option,struct string * url)184 dump_print(unsigned char *option, struct string *url)
185 {
186 unsigned char *str = get_opt_str(option);
187
188 if (str) {
189 unsigned char *realstr = subst_url(str, url);
190
191 if (realstr) {
192 printf("%s", realstr);
193 fflush(stdout);
194 mem_free(realstr);
195 }
196 }
197 }
198
199 static void
dump_loading_callback(struct download * download,void * p)200 dump_loading_callback(struct download *download, void *p)
201 {
202 struct cache_entry *cached = download->cached;
203 int fd = get_output_handle();
204
205 if (fd == -1) return;
206 if (cached && cached->redirect && dump_redir_count++ < MAX_REDIRECTS) {
207 struct uri *uri = cached->redirect;
208
209 if (is_in_progress_state(download->state))
210 change_connection(download, NULL, PRI_CANCEL, 0);
211
212 load_uri(uri, cached->uri, download, PRI_MAIN, 0, -1);
213 return;
214 }
215
216 if (is_in_queued_state(download->state)) return;
217
218 if (get_cmd_opt_bool("dump")) {
219 if (is_in_transfering_state(download->state))
220 return;
221
222 dump_formatted(fd, download, cached);
223
224 } else {
225 if (dump_source(fd, download, cached) > 0)
226 goto terminate;
227
228 if (is_in_progress_state(download->state))
229 return;
230
231 }
232
233 if (download->state != S_OK) {
234 usrerror(get_state_message(download->state, NULL));
235 program.retval = RET_ERROR;
236 goto terminate;
237 }
238
239 terminate:
240 program.terminate = 1;
241 dump_next(NULL);
242 }
243
244 static void
dump_start(unsigned char * url)245 dump_start(unsigned char *url)
246 {
247 unsigned char *wd = get_cwd();
248 struct uri *uri = get_translated_uri(url, wd);
249
250 mem_free_if(wd);
251
252 if (!uri || get_protocol_external_handler(NULL, uri)) {
253 usrerror(gettext("URL protocol not supported (%s)."), url);
254 goto terminate;
255 }
256
257 dump_download.callback = (download_callback_T *) dump_loading_callback;
258 dump_pos = 0;
259
260 if (load_uri(uri, NULL, &dump_download, PRI_MAIN, 0, -1)) {
261 terminate:
262 dump_next(NULL);
263 program.terminate = 1;
264 program.retval = RET_SYNTAX;
265 }
266
267 if (uri) done_uri(uri);
268 }
269
270 void
dump_next(struct list_head * url_list)271 dump_next(struct list_head *url_list)
272 {
273 static INIT_LIST_HEAD(todo_list);
274 static INIT_LIST_HEAD(done_list);
275 struct string_list_item *item;
276
277 if (url_list) {
278 /* Steal all them nice list items but keep the same order */
279 while (!list_empty(*url_list)) {
280 item = url_list->next;
281 del_from_list(item);
282 add_to_list_end(todo_list, item);
283 }
284 }
285
286 /* Dump each url list item one at a time */
287 if (!list_empty(todo_list)) {
288 static int first = 1;
289
290 program.terminate = 0;
291
292 item = todo_list.next;
293 del_from_list(item);
294 add_to_list(done_list, item);
295
296 if (!first) {
297 dump_print("document.dump.separator", NULL);
298 } else {
299 first = 0;
300 }
301
302 dump_print("document.dump.header", &item->string);
303 dump_start(item->string.source);
304 /* XXX: I think it ought to print footer at the end of
305 * the whole dump (not only this file). Testing required.
306 * --pasky */
307 dump_print("document.dump.footer", &item->string);
308
309 } else {
310 free_string_list(&done_list);
311 program.terminate = 1;
312 }
313 }
314
315 /* Using this function in dump_to_file() is unfortunately slightly slower than
316 * the current code. However having this here instead of in the scripting
317 * backends is better. */
318 struct string *
add_document_to_string(struct string * string,struct document * document)319 add_document_to_string(struct string *string, struct document *document)
320 {
321 int y;
322
323 assert(string && document);
324 if_assert_failed return NULL;
325
326 for (y = 0; y < document->height; y++) {
327 int white = 0;
328 int x;
329
330 for (x = 0; x < document->data[y].length; x++) {
331 struct screen_char *pos = &document->data[y].chars[x];
332 unsigned char data = pos->data;
333 unsigned int frame = (pos->attr & SCREEN_ATTR_FRAME);
334
335 if (!isscreensafe(data)) {
336 white++;
337 continue;
338 } else {
339 if (frame && data >= 176 && data < 224)
340 data = frame_dumb[data - 176];
341
342 if (data <= ' ') {
343 /* Count spaces. */
344 white++;
345 } else {
346 /* Print spaces if any. */
347 if (white) {
348 add_xchar_to_string(string, ' ', white);
349 white = 0;
350 }
351 add_char_to_string(string, data);
352 }
353 }
354 }
355
356 add_char_to_string(string, '\n');
357 }
358
359 return string;
360 }
361
362 #define D_BUF 65536
363
364 static int
write_char(unsigned char c,int fd,unsigned char * buf,int * bptr)365 write_char(unsigned char c, int fd, unsigned char *buf, int *bptr)
366 {
367 buf[(*bptr)++] = c;
368 if ((*bptr) >= D_BUF) {
369 if (hard_write(fd, buf, (*bptr)) != (*bptr))
370 return -1;
371 (*bptr) = 0;
372 }
373
374 return 0;
375 }
376
377 int
dump_to_file(struct document * document,int fd)378 dump_to_file(struct document *document, int fd)
379 {
380 int y;
381 int bptr = 0;
382 unsigned char *buf = mem_alloc(D_BUF);
383
384 if (!buf) return -1;
385
386 for (y = 0; y < document->height; y++) {
387 int white = 0;
388 int x;
389
390 for (x = 0; x < document->data[y].length; x++) {
391 unsigned char c;
392 unsigned char attr = document->data[y].chars[x].attr;
393
394 c = document->data[y].chars[x].data;
395
396 if ((attr & SCREEN_ATTR_FRAME)
397 && c >= 176 && c < 224)
398 c = frame_dumb[c - 176];
399
400 if (c <= ' ') {
401 /* Count spaces. */
402 white++;
403 continue;
404 }
405
406 /* Print spaces if any. */
407 while (white) {
408 if (write_char(' ', fd, buf, &bptr))
409 goto fail;
410 white--;
411 }
412
413 /* Print normal char. */
414 if (write_char(c, fd, buf, &bptr))
415 goto fail;
416 }
417
418 /* Print end of line. */
419 if (write_char('\n', fd, buf, &bptr))
420 goto fail;
421 }
422
423 if (hard_write(fd, buf, bptr) != bptr) {
424 fail:
425 mem_free(buf);
426 return -1;
427 }
428
429 if (document->nlinks && get_opt_bool("document.dump.references")) {
430 int x;
431 unsigned char *header = "\nReferences\n\n Visible links\n";
432 int headlen = strlen(header);
433
434 if (hard_write(fd, header, headlen) != headlen)
435 goto fail;
436
437 for (x = 0; x < document->nlinks; x++) {
438 struct link *link = &document->links[x];
439 unsigned char *where = link->where;
440
441 if (!where) continue;
442
443 if (document->options.links_numbering) {
444 if (link->title && *link->title)
445 snprintf(buf, D_BUF, "%4d. %s\n\t%s\n",
446 x + 1, link->title, where);
447 else
448 snprintf(buf, D_BUF, "%4d. %s\n",
449 x + 1, where);
450 } else {
451 if (link->title && *link->title)
452 snprintf(buf, D_BUF, " . %s\n\t%s\n",
453 link->title, where);
454 else
455 snprintf(buf, D_BUF, " . %s\n", where);
456 }
457
458 bptr = strlen(buf);
459 if (hard_write(fd, buf, bptr) != bptr)
460 goto fail;
461 }
462 }
463
464 mem_free(buf);
465 return 0;
466 }
467
468 #undef D_BUF
469