1 /*
2 * Copyright (c) 1994-2015, 2018-2020 Paul Mattes.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the names of Paul Mattes nor the names of his contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /*
29 * fprint_screen.c
30 * Screen printing functions.
31 */
32
33 #include "globals.h"
34
35 #include <assert.h>
36
37 #include "appres.h"
38 #include "3270ds.h"
39 #include "ctlr.h"
40
41 #include "ctlrc.h"
42
43 #include "resources.h"
44
45 #include "fprint_screen.h"
46 #if defined(_WIN32) /*[*/
47 # include "gdi_print.h"
48 #endif /*]*/
49 #include "nvt.h"
50 #include "trace.h"
51 #include "unicodec.h"
52 #include "utf8.h"
53 #include "utils.h"
54 #include "varbuf.h"
55
56 /* Typedefs */
57 typedef struct {
58 ptype_t ptype; /* Type P_XXX (text, html, rtf) */
59 unsigned opts; /* FPS_XXX options */
60 bool need_separator; /* Pending page indicator */
61 bool broken; /* If set, output has failed already. */
62 int spp; /* Screens per page. */
63 int screens; /* Screen count this page. */
64 FILE *file; /* Stream to write to */
65 char *caption; /* Caption with %T% expanded */
66 char *printer_name; /* Printer name (used by GDI) */
67 } real_fps_t;
68
69 /* Globals */
70
71 /* Statics */
72
73 /*
74 * Map default 3279 colors. This code is duplicated three times. ;-(
75 */
76 static int
color_from_fa(unsigned char fa)77 color_from_fa(unsigned char fa)
78 {
79 static int field_colors[4] = {
80 HOST_COLOR_GREEN, /* default */
81 HOST_COLOR_RED, /* intensified */
82 HOST_COLOR_BLUE, /* protected */
83 HOST_COLOR_WHITE /* protected, intensified */
84 # define DEFCOLOR_MAP(f) \
85 ((((f) & FA_PROTECT) >> 4) | (((f) & FA_INT_HIGH_SEL) >> 3))
86 };
87
88 if (mode.m3279) {
89 return field_colors[DEFCOLOR_MAP(fa)];
90 } else {
91 return HOST_COLOR_GREEN;
92 }
93 }
94
95 /*
96 * Map 3279 colors onto HTML colors.
97 */
98 static char *
html_color(int color)99 html_color(int color)
100 {
101 static char *html_color_map[] = {
102 "black",
103 "deepSkyBlue",
104 "red",
105 "pink",
106 "green",
107 "turquoise",
108 "yellow",
109 "white",
110 "black",
111 "blue3",
112 "orange",
113 "purple",
114 "paleGreen",
115 "paleTurquoise2",
116 "grey",
117 "white"
118 };
119 if (color >= HOST_COLOR_NEUTRAL_BLACK && color <= HOST_COLOR_WHITE) {
120 return html_color_map[color];
121 } else {
122 return "black";
123 }
124 }
125
126 /* Convert a caption string to UTF-8 RTF. */
127 static char *
rtf_caption(const char * caption)128 rtf_caption(const char *caption)
129 {
130 ucs4_t u;
131 int consumed;
132 enum me_fail error;
133 char mb[16];
134 varbuf_t r;
135
136 vb_init(&r);
137
138 while (*caption) {
139 u = multibyte_to_unicode(caption, strlen(caption), &consumed, &error);
140 if (u == 0) {
141 break;
142 }
143 if (u & ~0x7f) {
144 vb_appendf(&r, "\\u%u?", u);
145 } else {
146 unicode_to_multibyte(u, mb, sizeof(mb));
147 if (mb[0] == '\\' || mb[0] == '{' || mb[0] == '}') {
148 vb_appendf(&r, "\\%c", mb[0]);
149 } else if (mb[0] == '-') {
150 vb_appends(&r, "\\_");
151 } else if (mb[0] == ' ') {
152 vb_appends(&r, "\\~");
153 } else {
154 vb_append(&r, &mb[0], 1);
155 }
156 }
157
158 caption += consumed;
159 }
160 return vb_consume(&r);
161 }
162
163 /* Convert a caption string to UTF-8 HTML. */
164 static char *
html_caption(const char * caption)165 html_caption(const char *caption)
166 {
167 ucs4_t u;
168 int consumed;
169 enum me_fail error;
170 char u8buf[16];
171 int nu8;
172 varbuf_t r;
173
174 vb_init(&r);
175
176 while (*caption) {
177 u = multibyte_to_unicode(caption, strlen(caption), &consumed, &error);
178 if (u == 0) {
179 break;
180 }
181 switch (u) {
182 case '<':
183 vb_appends(&r, "<");
184 break;
185 case '>':
186 vb_appends(&r, ">");
187 break;
188 case '&':
189 vb_appends(&r, "&");
190 break;
191 default:
192 nu8 = unicode_to_utf8(u, u8buf);
193 vb_append(&r, u8buf, nu8);
194 break;
195 }
196 caption += consumed;
197 }
198 return vb_consume(&r);
199 }
200
201 /*
202 * Write a screen trace header to a stream.
203 * Returns the context to use with subsequent calls.
204 */
205 fps_status_t
fprint_screen_start(FILE * f,ptype_t ptype,unsigned opts,const char * caption,const char * printer_name,fps_t * fps_ret,void * wait_context)206 fprint_screen_start(FILE *f, ptype_t ptype, unsigned opts, const char *caption,
207 const char *printer_name, fps_t *fps_ret, void *wait_context)
208 {
209 real_fps_t *fps;
210 int rv = FPS_STATUS_SUCCESS;
211 char *pt_spp;
212
213 /* Non-text types can always generate blank output. */
214 if (ptype != P_TEXT) {
215 opts |= FPS_EVEN_IF_EMPTY;
216 }
217
218 /* Reset and save the state. */
219 fps = (real_fps_t *)Malloc(sizeof(real_fps_t));
220 fps->ptype = ptype;
221 fps->opts = opts;
222 fps->need_separator = false;
223 fps->broken = false;
224 fps->spp = 1;
225 fps->screens = 0;
226 fps->file = f;
227
228 if (caption != NULL) {
229 char *xcaption;
230 char *ts = strstr(caption, "%T%");
231
232 if (ts != NULL) {
233 time_t t = time(NULL);
234 struct tm *tm = localtime(&t);
235
236 xcaption = xs_buffer("%.*s" "%04d-%02d-%02d %02d:%02d:%02d" "%s",
237 (int)(ts - caption), caption,
238 tm->tm_year + 1900,
239 tm->tm_mon + 1,
240 tm->tm_mday,
241 tm->tm_hour,
242 tm->tm_min,
243 tm->tm_sec,
244 ts + 3);
245 } else {
246 xcaption = NewString(caption);
247 }
248 fps->caption = xcaption;
249 } else {
250 fps->caption = NULL;
251 }
252
253 if (printer_name != NULL && printer_name[0]) {
254 fps->printer_name = NewString(printer_name);
255 } else {
256 fps->printer_name = NULL;
257 }
258
259 switch (ptype) {
260 case P_RTF: {
261 char *pt_font = get_resource(ResPrintTextFont);
262 char *pt_size = get_resource(ResPrintTextSize);
263 int pt_nsize;
264
265 if (pt_font == NULL) {
266 pt_font = "Courier New";
267 }
268 if (pt_size == NULL) {
269 pt_size = "8";
270 }
271 pt_nsize = atoi(pt_size);
272 if (pt_nsize <= 0) {
273 pt_nsize = 8;
274 }
275
276 if (fprintf(f, "{\\rtf1\\ansi\\ansicpg%u\\deff0\\deflang1033{"
277 "\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0 %s;}}\n"
278 "\\viewkind4\\uc1\\pard\\f0\\fs%d ",
279 #if defined(_WIN32) /*[*/
280 GetACP(),
281 #else /*][*/
282 1252, /* the number doesn't matter */
283 #endif /*]*/
284 pt_font, pt_nsize * 2) < 0) {
285 rv = FPS_STATUS_ERROR;
286 }
287 if (rv == FPS_STATUS_SUCCESS && fps->caption != NULL) {
288 char *hcaption = rtf_caption(fps->caption);
289
290 if (fprintf(f, "%s\\par\\par\n", hcaption) < 0) {
291 rv = FPS_STATUS_ERROR;
292 }
293 Free(hcaption);
294 }
295 break;
296 }
297 case P_HTML: {
298 char *hcaption = NULL;
299
300 /* Make the caption HTML-safe. */
301 if (fps->caption != NULL) {
302 hcaption = html_caption(fps->caption);
303 }
304
305 /* Print the preamble. */
306 if (!(opts & FPS_NO_HEADER) &&
307 fprintf(f, "<html>\n"
308 "<head>\n"
309 " <meta http-equiv=\"Content-Type\" "
310 "content=\"text/html; charset=UTF-8\">\n"
311 "</head>\n"
312 " <body>\n") < 0) {
313 rv = FPS_STATUS_ERROR;
314 }
315 if (rv == FPS_STATUS_SUCCESS && hcaption) {
316 if (fprintf(f, "<p>%s</p>\n", hcaption) < 0) {
317 rv = FPS_STATUS_ERROR;
318 }
319 Free(hcaption);
320 }
321 break;
322 }
323 case P_TEXT:
324 if (fps->caption != NULL) {
325 if (fprintf(f, "%s\n\n", fps->caption) < 0) {
326 rv = FPS_STATUS_ERROR;
327 }
328 }
329 break;
330 case P_GDI:
331 #if defined(_WIN32) /*[*/
332 switch (gdi_print_start(printer_name, opts, wait_context)) {
333 case GDI_STATUS_SUCCESS:
334 break;
335 case GDI_STATUS_ERROR:
336 rv = FPS_STATUS_ERROR;
337 break;
338 case GDI_STATUS_CANCEL:
339 rv = FPS_STATUS_CANCEL;
340 break;
341 case GDI_STATUS_WAIT:
342 rv = FPS_STATUS_WAIT;
343 break;
344 }
345 #else /*][*/
346 assert(ptype != P_GDI);
347 #endif /*]*/
348 break;
349 case P_NONE:
350 assert(ptype != P_NONE);
351 break;
352 }
353
354 /* Set up screens-per-page. */
355 pt_spp = get_resource(ResPrintTextScreensPerPage);
356 if (pt_spp != NULL) {
357 fps->spp = atoi(pt_spp);
358 if (fps->spp < 1 || fps->spp > 5) {
359 fps->spp = 1;
360 }
361 }
362
363 if (rv != FPS_STATUS_SUCCESS) {
364 /* We've failed; there's no point in returning the context. */
365 Free(fps->caption);
366 Free(fps->printer_name);
367 Free(fps);
368 *fps_ret = NULL;
369 } else {
370 *fps_ret = (fps_t)(void *)fps;
371 }
372
373 return rv;
374 }
375
376 #define FAIL do { \
377 rv = -1; \
378 goto done; \
379 } while(false)
380
381 /*
382 * Add a screen image to a stream.
383 *
384 * Returns 0 for no screen written, 1 for screen written, -1 for error.
385 */
386 fps_status_t
fprint_screen_body(fps_t ofps)387 fprint_screen_body(fps_t ofps)
388 {
389 real_fps_t *fps = (real_fps_t *)(void *)ofps;
390 register int i;
391 ucs4_t uc;
392 int ns = 0;
393 int nr = 0;
394 bool any = false;
395 int fa_addr = find_field_attribute(0);
396 unsigned char fa = ea_buf[fa_addr].fa;
397 int fa_fg, current_fg;
398 int fa_bg, current_bg;
399 bool fa_high, current_high;
400 bool fa_ital, current_ital;
401 bool mi;
402 #if defined(_WIN32) /*[*/
403 gdi_header_t h;
404 #endif /*]*/
405 fps_status_t rv = FPS_STATUS_SUCCESS;
406
407 /* Quick short-circuit. */
408 if (fps == NULL || fps->broken) {
409 return FPS_STATUS_ERROR;
410 }
411
412 mi = ((fps->opts & FPS_MODIFIED_ITALIC)) != 0;
413 if (ea_buf[fa_addr].fg) {
414 fa_fg = ea_buf[fa_addr].fg & 0x0f;
415 } else {
416 fa_fg = color_from_fa(fa);
417 }
418 current_fg = fa_fg;
419
420 if (ea_buf[fa_addr].bg) {
421 fa_bg = ea_buf[fa_addr].bg & 0x0f;
422 } else {
423 fa_bg = HOST_COLOR_BLACK;
424 }
425 current_bg = fa_bg;
426
427 if (ea_buf[fa_addr].gr & GR_INTENSIFY) {
428 fa_high = true;
429 } else {
430 fa_high = FA_IS_HIGH(fa);
431 }
432 current_high = fa_high;
433 fa_ital = mi && FA_IS_MODIFIED(fa);
434 current_ital = fa_ital;
435
436 switch (fps->ptype) {
437 case P_RTF:
438 if (fps->need_separator) {
439 if (fps->screens < fps->spp) {
440 if (fprintf(fps->file, "\\par\n") < 0) {
441 FAIL;
442 }
443 } else {
444 if (fprintf(fps->file, "\n\\page\n") < 0) {
445 FAIL;
446 }
447 fps->screens = 0;
448 }
449 }
450 if (current_high) {
451 if (fprintf(fps->file, "\\b ") < 0) {
452 FAIL;
453 }
454 }
455 break;
456 case P_HTML:
457 if (fprintf(fps->file, " <table border=0>"
458 "<tr bgcolor=black><td>"
459 "<pre><span style=\"color:%s;"
460 "background:%s;"
461 "font-weight:%s;"
462 "font-style:%s\">",
463 html_color(current_fg),
464 html_color(current_bg),
465 current_high? "bold": "normal",
466 current_ital? "italic": "normal") < 0) {
467 FAIL;
468 }
469 break;
470 case P_TEXT:
471 if (fps->need_separator) {
472 if ((fps->opts & FPS_FF_SEP) && fps->screens >= fps->spp) {
473 if (fputc('\f', fps->file) < 0) {
474 FAIL;
475 }
476 fps->screens = 0;
477 } else {
478 for (i = 0; i < COLS; i++) {
479 if (fputc('=', fps->file) < 0) {
480 FAIL;
481 }
482 }
483 if (fputc('\n', fps->file) < 0) {
484 FAIL;
485 }
486 }
487 }
488 break;
489 #if defined(_WIN32) /*[*/
490 case P_GDI:
491 /*
492 * Write the current screen buffer to the file.
493 * We will read it back and print it when we are done.
494 */
495 h.signature = GDI_SIGNATURE;
496 h.rows = ROWS;
497 h.cols = COLS;
498 if (fwrite(&h, sizeof(h), 1, fps->file) != 1) {
499 FAIL;
500 }
501 if (fwrite(ea_buf, sizeof(struct ea), ROWS * COLS, fps->file)
502 != ROWS * COLS) {
503 FAIL;
504 }
505 fflush(fps->file);
506 rv = FPS_STATUS_SUCCESS_WRITTEN;
507 goto done;
508 #endif /*]*/
509 default:
510 break;
511 }
512
513 fps->need_separator = false;
514
515 for (i = 0; i < ROWS*COLS; i++) {
516 char mb[16];
517 int nmb;
518
519 uc = 0;
520
521 if (i && !(i % COLS)) {
522 if (fps->ptype == P_HTML) {
523 if (fputc('\n', fps->file) < 0) {
524 FAIL;
525 }
526 } else {
527 nr++;
528 }
529 ns = 0;
530 }
531 if (ea_buf[i].fa) {
532 uc = ' ';
533 fa = ea_buf[i].fa;
534 if (ea_buf[i].fg) {
535 fa_fg = ea_buf[i].fg & 0x0f;
536 } else {
537 fa_fg = color_from_fa(fa);
538 }
539 if (ea_buf[i].bg) {
540 fa_bg = ea_buf[i].bg & 0x0f;
541 } else {
542 fa_bg = HOST_COLOR_BLACK;
543 }
544 if (ea_buf[i].gr & GR_INTENSIFY) {
545 fa_high = true;
546 } else {
547 fa_high = FA_IS_HIGH(fa);
548 }
549 fa_ital = mi && FA_IS_MODIFIED(fa);
550 }
551 if (FA_IS_ZERO(fa)) {
552 if (ctlr_dbcs_state(i) == DBCS_LEFT) {
553 uc = 0x3000;
554 } else {
555 uc = ' ';
556 }
557 } else if (is_nvt(&ea_buf[i], false, &uc)) {
558 /* NVT-mode text. */
559 if (ctlr_dbcs_state(i) == DBCS_RIGHT) {
560 continue;
561 }
562 } else {
563 /* Convert EBCDIC to Unicode. */
564 switch (ctlr_dbcs_state(i)) {
565 case DBCS_NONE:
566 case DBCS_SB:
567 uc = ebcdic_to_unicode(ea_buf[i].ec, ea_buf[i].cs, EUO_NONE);
568 if (uc == 0) {
569 uc = ' ';
570 }
571 break;
572 case DBCS_LEFT:
573 uc = ebcdic_to_unicode((ea_buf[i].ec << 8) | ea_buf[i + 1].ec,
574 CS_BASE, EUO_NONE);
575 if (uc == 0) {
576 uc = 0x3000;
577 }
578 break;
579 case DBCS_RIGHT:
580 /* skip altogether, we took care of it above */
581 continue;
582 default:
583 uc = ' ';
584 break;
585 }
586 }
587
588 /* Translate to a type-specific format and write it out. */
589 if (uc == ' ' && fps->ptype != P_HTML) {
590 ns++;
591 } else if (uc == 0x3000) {
592 if (fps->ptype == P_HTML) {
593 if (fprintf(fps->file, " ") < 0) {
594 FAIL;
595 }
596 } else {
597 ns += 2;
598 }
599 } else {
600 while (nr) {
601 if (fps->ptype == P_RTF)
602 if (fprintf(fps->file, "\\par") < 0) {
603 FAIL;
604 }
605 if (fputc('\n', fps->file) < 0) {
606 FAIL;
607 }
608 nr--;
609 }
610 while (ns) {
611 if (fps->ptype == P_RTF) {
612 if (fprintf(fps->file, "\\~") < 0) {
613 FAIL;
614 }
615 } else {
616 if (fputc(' ', fps->file) < 0) {
617 FAIL;
618 }
619 }
620 ns--;
621 }
622 if (fps->ptype == P_RTF) {
623 bool high;
624
625 if (ea_buf[i].gr & GR_INTENSIFY) {
626 high = true;
627 } else {
628 high = fa_high;
629 }
630 if (high != current_high) {
631 if (high) {
632 if (fprintf(fps->file, "\\b ") < 0) {
633 FAIL;
634 }
635 } else {
636 if (fprintf(fps->file, "\\b0 ") < 0) {
637 FAIL;
638 }
639 }
640 current_high = high;
641 }
642 }
643 if (fps->ptype == P_HTML) {
644 int fg_color, bg_color;
645 bool high;
646
647 if (ea_buf[i].fg) {
648 fg_color = ea_buf[i].fg & 0x0f;
649 } else {
650 fg_color = fa_fg;
651 }
652 if (ea_buf[i].bg) {
653 bg_color = ea_buf[i].bg & 0x0f;
654 } else {
655 bg_color = fa_bg;
656 }
657 if (ea_buf[i].gr & GR_REVERSE) {
658 int tmp;
659
660 tmp = fg_color;
661 fg_color = bg_color;
662 bg_color = tmp;
663 }
664
665 if (i == cursor_addr) {
666 fg_color = (bg_color == HOST_COLOR_RED)?
667 HOST_COLOR_BLACK: bg_color;
668 bg_color = HOST_COLOR_RED;
669 }
670 if (ea_buf[i].gr & GR_INTENSIFY) {
671 high = true;
672 } else {
673 high = fa_high;
674 }
675
676 if (fg_color != current_fg ||
677 bg_color != current_bg ||
678 high != current_high ||
679 fa_ital != current_ital) {
680 if (fprintf(fps->file,
681 "</span><span "
682 "style=\"color:%s;"
683 "background:%s;"
684 "font-weight:%s;"
685 "font-style:%s\">",
686 html_color(fg_color),
687 html_color(bg_color),
688 high? "bold": "normal",
689 fa_ital? "italic": "normal") < 0) {
690 FAIL;
691 }
692 current_fg = fg_color;
693 current_bg = bg_color;
694 current_high = high;
695 current_ital = fa_ital;
696 }
697 }
698 any = true;
699 if (fps->ptype == P_RTF) {
700 if (uc & ~0x7f) {
701 if (fprintf(fps->file, "\\u%u?", uc) < 0) {
702 FAIL;
703 }
704 } else {
705 nmb = unicode_to_multibyte(uc, mb, sizeof(mb));
706 if (mb[0] == '\\' || mb[0] == '{' || mb[0] == '}') {
707 if (fprintf(fps->file, "\\%c", mb[0]) < 0) {
708 FAIL;
709 }
710 } else if (mb[0] == '-') {
711 if (fprintf(fps->file, "\\_") < 0) {
712 FAIL;
713 }
714 } else if (mb[0] == ' ') {
715 if (fprintf(fps->file, "\\~") < 0) {
716 FAIL;
717 }
718 } else {
719 if (fputc(mb[0], fps->file) < 0) {
720 FAIL;
721 }
722 }
723 }
724 } else if (fps->ptype == P_HTML) {
725 if (uc == '<') {
726 if (fprintf(fps->file, "<") < 0) {
727 FAIL;
728 }
729 } else if (uc == '&') {
730 if (fprintf(fps->file, "&") < 0) {
731 FAIL;
732 }
733 } else if (uc == '>') {
734 if (fprintf(fps->file, ">") < 0) {
735 FAIL;
736 }
737 } else {
738 nmb = unicode_to_utf8(uc, mb);
739 {
740 int k;
741
742 for (k = 0; k < nmb; k++) {
743 if (fputc(mb[k], fps->file) < 0) {
744 FAIL;
745 }
746 }
747 }
748 }
749 } else {
750 nmb = unicode_to_multibyte(uc, mb, sizeof(mb));
751 if (fputs(mb, fps->file) < 0) {
752 FAIL;
753 }
754 }
755 }
756 }
757
758 if (fps->ptype == P_HTML) {
759 if (fputc('\n', fps->file) < 0) {
760 FAIL;
761 }
762 } else {
763 nr++;
764 }
765 if (!any && !(fps->opts & FPS_EVEN_IF_EMPTY) && fps->ptype == P_TEXT) {
766 return FPS_STATUS_SUCCESS;
767 }
768 while (nr) {
769 if (fps->ptype == P_RTF) {
770 if (fprintf(fps->file, "\\par") < 0) {
771 FAIL;
772 }
773 }
774 if (fps->ptype == P_TEXT) {
775 if (fputc('\n', fps->file) < 0) {
776 FAIL;
777 }
778 }
779 nr--;
780 }
781 if (fps->ptype == P_HTML) {
782 if (fprintf(fps->file, "%s</span></pre></td></tr>\n </table>\n",
783 current_high? "</b>": "") < 0) {
784 FAIL;
785 }
786 }
787 fps->need_separator = true;
788 fps->screens++;
789 rv = FPS_STATUS_SUCCESS_WRITTEN; /* wrote a screen */
790
791 done:
792 if (FPS_IS_ERROR(rv)) {
793 fps->broken = true;
794 }
795 return rv;
796 }
797
798 #undef FAIL
799
800 /*
801 * Finish writing a multi-screen image.
802 * Returns 0 success, -1 for error. In either case, the context is freed.
803 */
804 fps_status_t
fprint_screen_done(fps_t * ofps)805 fprint_screen_done(fps_t *ofps)
806 {
807 real_fps_t *fps = (real_fps_t *)*(void **)ofps;
808 int rv = FPS_STATUS_SUCCESS;
809
810 if (fps == NULL) {
811 return FPS_STATUS_ERROR;
812 }
813
814 if (!fps->broken) {
815 switch (fps->ptype) {
816 case P_RTF:
817 if (fprintf(fps->file, "\n}\n%c", 0) < 0) {
818 rv = FPS_STATUS_ERROR;
819 }
820 break;
821 case P_HTML:
822 if (!(fps->opts & FPS_NO_HEADER) &&
823 fprintf(fps->file, " </body>\n</html>\n") < 0) {
824 rv = FPS_STATUS_ERROR;
825 }
826 break;
827 #if defined(_WIN32) /*[*/
828 case P_GDI:
829 vtrace("Printing to GDI printer\n");
830 if (gdi_print_finish(fps->file, fps->caption) < 0) {
831 rv = FPS_STATUS_ERROR;
832 }
833 break;
834 #endif /*]*/
835 default:
836 break;
837 }
838 }
839
840 /* Done with the context. */
841 Free(fps->caption);
842 Free(fps->printer_name);
843 memset(fps, '\0', sizeof(*fps));
844 Free(*(void **)ofps);
845 *(void **)ofps = NULL;
846
847 return rv;
848 }
849
850 /*
851 * Write a header, screen image, and trailer to a file.
852 */
853 fps_status_t
fprint_screen(FILE * f,ptype_t ptype,unsigned opts,const char * caption,const char * printer_name,void * wait_context)854 fprint_screen(FILE *f, ptype_t ptype, unsigned opts, const char *caption,
855 const char *printer_name, void *wait_context)
856 {
857 fps_t fps;
858 fps_status_t srv;
859 fps_status_t srv_body;
860
861 srv = fprint_screen_start(f, ptype, opts, caption, printer_name, &fps,
862 wait_context);
863 if (FPS_IS_ERROR(srv) || srv == FPS_STATUS_WAIT) {
864 return srv;
865 }
866 srv_body = fprint_screen_body(fps);
867 if (FPS_IS_ERROR(srv_body)) {
868 fprint_screen_done(&fps);
869 return srv_body;
870 }
871 srv = fprint_screen_done(&fps);
872 if (FPS_IS_ERROR(srv)) {
873 return srv;
874 }
875 return srv_body;
876 }
877