1 /*
2 * Copyright (c) 1993-2014, 2019 Paul Mattes.
3 * Copyright (c) 1990, Jeff Sparkes.
4 * Copyright (c) 1989, Georgia Tech Research Corporation (GTRC), Atlanta, GA
5 * 30332.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the names of Paul Mattes, Jeff Sparkes, GTRC nor the names of
16 * their contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY PAUL MATTES, JEFF SPARKES AND GTRC "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL PAUL MATTES, JEFF SPARKES OR GTRC BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * ctlr.c
34 * This module handles interpretation of the 3270 data stream and
35 * maintenance of the 3270 device state.
36 *
37 */
38
39 #include <stdio.h>
40 #include <errno.h>
41 #include <string.h>
42 #if !defined(_MSC_VER) /*[*/
43 #include <unistd.h>
44 #endif /*]*/
45 #include <stdlib.h>
46 #include <sys/types.h>
47 #if !defined(_WIN32) /*[*/
48 #include <sys/wait.h>
49 #endif /*]*/
50 #include <signal.h>
51 #include "globals.h"
52 #include "pr3287.h"
53 #include "3270ds.h"
54 #include "codepage.h"
55 #include "ctlrc.h"
56 #include "trace.h"
57 #include "sf.h"
58 #include "tables.h"
59 #include "unicodec.h"
60 #include "xtablec.h"
61 #if defined(_WIN32) /*[*/
62 #include "wsc.h"
63 #include <windows.h>
64 #endif /*]*/
65
66 #define CS_GE 0x04 /* hack */
67
68 #define WCC_LINE_LENGTH(c) ((c) & 0x30)
69 #define WCC_132 0x00
70 #define WCC_40 0x10
71 #define WCC_64 0x20
72 #define WCC_80 0x30
73
74 #define MAX_BUF (MAX_UNF_MPP * MAX_UNF_MPP) /* swag */
75
76 #define VISIBLE 0x01 /* visible field */
77 #define INVISIBLE 0x02 /* invisible field */
78
79 #define BUFSZ 4096
80
81 #define FCORDER_NOP 0x0001 /* dummy filler for DBCS right half */
82
83 static const char *ll_name[] = { "unformatted132", "formatted40", "formatted64", "formatted80" };
84 static int ll_len[] = { 132, 40, 64, 80 };
85
86 /* 3270 (formatted mode) data */
87 static unsigned char default_gr;
88 static unsigned char default_cs;
89 static int line_length;
90 static ucs4_t page_buf[MAX_BUF];
91 static unsigned char *xlate_buf[MAX_BUF];
92 int xlate_len[MAX_BUF];
93 static int baddr = 0;
94 static bool page_buf_initted = false;
95 static bool any_3270_printable = false;
96 static int any_3270_output = 0;
97 #if !defined(_WIN32) /*[*/
98 static FILE *prfile = NULL;
99 static int prpid = -1;
100 #else /*][*/
101 static int ws_initted = 0;
102 static int ws_needpre = 1;
103 #endif /*]*/
104 static unsigned char wcc_line_length;
105
106 static int ctlr_erase(void);
107 static int dump_formatted(void);
108 static int dump_unformatted(void);
109 static int stash(unsigned char c);
110 static int prflush(void);
111 static int copyfile(const char *filename);
112
113 #define DECODE_BADDR(c1, c2) \
114 ((((c1) & 0xC0) == 0x00) ? \
115 (((c1) & 0x3F) << 8) | (c2) : \
116 (((c1) & 0x3F) << 6) | ((c2) & 0x3F))
117
118 /* SCS constants and data. */
119 #define MAX_MPP 132
120 #define MAX_MPL 108
121
122 static ucs4_t linebuf[MAX_MPP+1];
123 static struct {
124 unsigned malloc_len;
125 unsigned data_len;
126 char *buf;
127 } trnbuf[MAX_MPP+1];
128 static char htabs[MAX_MPP+1];
129 static char vtabs[MAX_MPL+1];
130 static int lm, tm, bm, mpp, mpl, scs_any;
131 static int pp;
132 static int line;
133 static bool scs_initted = false;
134 static bool any_scs_output = false;
135 static size_t scs_leftover_len = 0;
136 static int scs_leftover_buf[256];
137 static int scs_dbcs_subfield = 0;
138 static unsigned char scs_dbcs_c1 = 0;
139 static unsigned scs_cs = 0;
140 static bool ffeoj_last = false;
141
142 /*
143 * Interpret an incoming 3270 command.
144 */
145 enum pds
process_ds(unsigned char * buf,size_t buflen)146 process_ds(unsigned char *buf, size_t buflen)
147 {
148 if (!buflen) {
149 return PDS_OKAY_NO_OUTPUT;
150 }
151
152 trace_ds("< ");
153
154 switch (buf[0]) { /* 3270 command */
155 case CMD_EAU: /* erase all unprotected */
156 case SNA_CMD_EAU:
157 trace_ds("EraseAllUnprotected\n");
158 if (ctlr_erase() < 0 || prflush() < 0) {
159 return PDS_FAILED;
160 }
161 return PDS_OKAY_NO_OUTPUT;
162 case CMD_EWA: /* erase/write alternate */
163 case SNA_CMD_EWA:
164 trace_ds("EraseWriteAlternate");
165 if (ctlr_erase() < 0 || prflush() < 0) {
166 return PDS_FAILED;
167 }
168 baddr = 0;
169 ctlr_write(buf, buflen, true);
170 return PDS_OKAY_NO_OUTPUT;
171 case CMD_EW: /* erase/write */
172 case SNA_CMD_EW:
173 trace_ds("EraseWrite");
174 if (ctlr_erase() < 0 || prflush() < 0) {
175 return PDS_FAILED;
176 }
177 baddr = 0;
178 ctlr_write(buf, buflen, true);
179 return PDS_OKAY_NO_OUTPUT;
180 case CMD_W: /* write */
181 case SNA_CMD_W:
182 trace_ds("Write");
183 ctlr_write(buf, buflen, false);
184 return PDS_OKAY_NO_OUTPUT;
185 case CMD_RB: /* read buffer */
186 case SNA_CMD_RB:
187 trace_ds("ReadBuffer\n");
188 return PDS_BAD_CMD;
189 case CMD_RM: /* read modifed */
190 case SNA_CMD_RM:
191 trace_ds("ReadModified\n");
192 return PDS_BAD_CMD;
193 case CMD_RMA: /* read modifed all */
194 case SNA_CMD_RMA:
195 trace_ds("ReadModifiedAll\n");
196 return PDS_BAD_CMD;
197 case CMD_WSF: /* write structured field */
198 case SNA_CMD_WSF:
199 trace_ds("WriteStructuredField");
200 return write_structured_field(buf, buflen);
201 case CMD_NOP: /* no-op */
202 trace_ds("NoOp\n");
203 return PDS_OKAY_NO_OUTPUT;
204 default:
205 /* unknown 3270 command */
206 errmsg("Unknown 3270 Data Stream command: 0x%X", buf[0]);
207 return PDS_BAD_CMD;
208 }
209 }
210
211 /*
212 * Process a 3270 Write command.
213 */
214 void
ctlr_write(unsigned char buf[],size_t buflen,bool erase)215 ctlr_write(unsigned char buf[], size_t buflen, bool erase)
216 {
217 unsigned char *cp;
218 bool wcc_keyboard_restore, wcc_sound_alarm;
219 bool wcc_start_printer;
220 bool ra_ge;
221 int i;
222 unsigned char na;
223 int any_fa;
224 ucs4_t ra_xlate = 0;
225 const char *paren = "(";
226 int xbaddr;
227 enum { NONE, ORDER, SBA, TEXT, NULLCH } previous = NONE;
228
229 #define END_TEXT0 { if (previous == TEXT) trace_ds("'"); }
230 #define END_TEXT(cmd) { END_TEXT0; trace_ds(" %s", cmd); }
231
232 #define START_FIELD(fa) { \
233 ctlr_add(0, FA_IS_ZERO(fa)?INVISIBLE:VISIBLE, 0, default_gr); \
234 trace_ds(see_attr(fa)); \
235 }
236
237 if (buflen < 2) {
238 return;
239 }
240
241 if (!page_buf_initted) {
242 memset(page_buf, '\0', MAX_BUF * sizeof(ucs4_t));
243 memset(xlate_buf, '\0', MAX_BUF * sizeof(unsigned char *));
244 memset(xlate_len, '\0', MAX_BUF * sizeof(int));
245 page_buf_initted = true;
246 baddr = 0;
247 }
248
249 default_gr = 0;
250 default_cs = 0;
251
252 if (WCC_RESET(buf[1])) {
253 trace_ds("%sreset", paren);
254 paren = ",";
255 }
256 wcc_line_length = WCC_LINE_LENGTH(buf[1]);
257 if (wcc_line_length) {
258 trace_ds("%s%s", paren, ll_name[wcc_line_length >> 4]);
259 paren = ",";
260 } else {
261 trace_ds("%sunformatted", paren);
262 paren = ",";
263 }
264 line_length = ll_len[wcc_line_length >> 4];
265 wcc_sound_alarm = WCC_SOUND_ALARM(buf[1]);
266 if (wcc_sound_alarm) {
267 trace_ds("%salarm", paren);
268 paren = ",";
269 }
270 wcc_keyboard_restore = WCC_KEYBOARD_RESTORE(buf[1]);
271 if (wcc_keyboard_restore) {
272 trace_ds("%srestore", paren);
273 paren = ",";
274 }
275
276 if (WCC_RESET_MDT(buf[1])) {
277 trace_ds("%sresetMDT", paren);
278 paren = ",";
279 }
280 wcc_start_printer = WCC_START_PRINTER(buf[1]);
281 if (wcc_start_printer) {
282 trace_ds("%sstartprinter", paren);
283 paren = ",";
284 }
285 if (strcmp(paren, "(")) {
286 trace_ds(")");
287 }
288
289 for (cp = &buf[2]; cp < (buf + buflen); cp++) {
290 switch (*cp) {
291 case ORDER_SF: /* start field */
292 END_TEXT("StartField");
293 previous = ORDER;
294 cp++; /* skip field attribute */
295 START_FIELD(*cp);
296 break;
297 case ORDER_SBA: /* set buffer address */
298 cp += 2; /* skip buffer address */
299 xbaddr = DECODE_BADDR(*(cp - 1), *cp);
300 END_TEXT("SetBufferAddress");
301 if (wcc_line_length) {
302 trace_ds("(%d,%d)", 1 + (xbaddr / line_length),
303 1 + (xbaddr % line_length));
304 } else {
305 trace_ds("(%d[%+d])", xbaddr, xbaddr - baddr);
306 }
307 if (xbaddr >= MAX_BUF) {
308 /* Error! */
309 baddr = 0;
310 return;
311 }
312 if (wcc_line_length) {
313 /* Formatted. */
314 baddr = xbaddr;
315 } else if (xbaddr > baddr) {
316 /* Unformatted. */
317 while (baddr < xbaddr) {
318 ctlr_add(0, ' ', default_cs, default_gr);
319 }
320 }
321 previous = SBA;
322 break;
323 case ORDER_IC: /* insert cursor */
324 END_TEXT("InsertCursor");
325 previous = ORDER;
326 break;
327 case ORDER_PT: /* program tab */
328 END_TEXT("ProgramTab");
329 previous = ORDER;
330 break;
331 case ORDER_RA: /* repeat to address */
332 cp += 2; /* skip buffer address */
333 xbaddr = DECODE_BADDR(*(cp-1), *cp);
334 END_TEXT("RepeatToAddress");
335 if (wcc_line_length) {
336 trace_ds("(%d,%d)", 1 + (xbaddr / line_length),
337 1 + (xbaddr % line_length));
338 } else {
339 trace_ds("(%d[%+d])", xbaddr, xbaddr - baddr);
340 }
341 cp++; /* skip char to repeat */
342 if (*cp == ORDER_GE){
343 ra_ge = true;
344 trace_ds("GraphicEscape");
345 cp++;
346 } else {
347 ra_ge = false;
348 }
349 trace_ds("'%s'", see_ebc(*cp));
350 previous = ORDER;
351 if (xbaddr > MAX_BUF || xbaddr < baddr) {
352 baddr = 0;
353 return;
354 }
355 /* Translate '*cp' once. */
356 switch (*cp) {
357 case FCORDER_FF:
358 case FCORDER_CR:
359 case FCORDER_NL:
360 case FCORDER_EM:
361 ra_xlate = *cp;
362 break;
363 default:
364 if (*cp <= 0x3F) {
365 ra_xlate = '\0';
366 } else {
367 ra_xlate = ebcdic_to_unicode(*cp, ra_ge? CS_GE: CS_BASE,
368 EUO_NONE);
369 }
370 break;
371 }
372 while (baddr < xbaddr) {
373 ctlr_add(ra_ge? 0: *cp, ra_xlate, ra_ge? CS_GE: default_cs,
374 default_gr);
375 }
376 break;
377 case ORDER_EUA: /* erase unprotected to address */
378 cp += 2; /* skip buffer address */
379 xbaddr = DECODE_BADDR(*(cp-1), *cp);
380 END_TEXT("EraseUnprotectedAll");
381 previous = ORDER;
382 break;
383 case ORDER_GE: /* graphic escape */
384 END_TEXT("GraphicEscape ");
385 cp++; /* skip char */
386 previous = ORDER;
387 if (*cp) {
388 trace_ds("'");
389 }
390 trace_ds("%s", see_ebc(*cp));
391 if (*cp) {
392 trace_ds("'");
393 }
394 ctlr_add(0, ebcdic_to_unicode(*cp, CS_GE, EUO_NONE), CS_GE,
395 default_gr);
396 break;
397 case ORDER_MF: /* modify field */
398 END_TEXT("ModifyField");
399 previous = ORDER;
400 cp++;
401 na = *cp;
402 cp += na * 2;
403 break;
404 case ORDER_SFE: /* start field extended */
405 END_TEXT("StartFieldExtended");
406 previous = ORDER;
407 cp++; /* skip order */
408 na = *cp;
409 any_fa = 0;
410
411 for (i = 0; i < (int)na; i++) {
412 cp++;
413 if (*cp == XA_3270) {
414 trace_ds(" 3270");
415 cp++;
416 START_FIELD(*cp);
417 any_fa++;
418 } else if (*cp == XA_FOREGROUND) {
419 trace_ds("%s", see_efa(*cp, *(cp + 1)));
420 cp++;
421 } else if (*cp == XA_HIGHLIGHTING) {
422 trace_ds("%s", see_efa(*cp, *(cp + 1)));
423 cp++;
424 } else if (*cp == XA_CHARSET) {
425 trace_ds("%s", see_efa(*cp, *(cp + 1)));
426 cp++;
427 } else if (*cp == XA_ALL) {
428 trace_ds("%s", see_efa(*cp, *(cp + 1)));
429 cp++;
430 } else {
431 trace_ds("%s[unsupported]", see_efa(*cp, *(cp + 1)));
432 cp++;
433 }
434 }
435 if (!any_fa) {
436 START_FIELD(0);
437 }
438 ctlr_add(0, '\0', 0, default_gr);
439 break;
440 case ORDER_SA: /* set attribute */
441 END_TEXT("SetAttribtue");
442 previous = ORDER;
443 cp++;
444 if (*cp == XA_FOREGROUND) {
445 trace_ds("%s", see_efa(*cp, *(cp + 1)));
446 } else if (*cp == XA_HIGHLIGHTING) {
447 trace_ds("%s", see_efa(*cp, *(cp + 1)));
448 default_gr = *(cp + 1) & 0x07;
449 } else if (*cp == XA_ALL) {
450 trace_ds("%s", see_efa(*cp, *(cp + 1)));
451 default_gr = 0;
452 default_cs = 0;
453 } else if (*cp == XA_CHARSET) {
454 trace_ds("%s", see_efa(*cp, *(cp + 1)));
455 default_cs = (*(cp + 1) == 0xf1) ? 1 : 0;
456 } else {
457 trace_ds("%s[unsupported]", see_efa(*cp, *(cp + 1)));
458 }
459 cp++;
460 break;
461 case FCORDER_FF: /* Form Feed */
462 END_TEXT("FF");
463 previous = ORDER;
464 ctlr_add(0, FCORDER_FF, default_cs, default_gr);
465 break;
466 case FCORDER_CR: /* Carriage Return */
467 END_TEXT("CR");
468 previous = ORDER;
469 ctlr_add(0, FCORDER_CR, default_cs, default_gr);
470 break;
471 case FCORDER_NL: /* New Line */
472 END_TEXT("NL");
473 previous = ORDER;
474 ctlr_add(0, FCORDER_NL, default_cs, default_gr);
475 break;
476 case FCORDER_EM: /* End of Media */
477 END_TEXT("EM");
478 previous = ORDER;
479 ctlr_add(0, FCORDER_EM, default_cs, default_gr);
480 break;
481 case FCORDER_DUP: /* Visible control characters */
482 case FCORDER_FM:
483 END_TEXT(see_ebc(*cp));
484 previous = ORDER;
485 ctlr_add(0, ebc2asc0[*cp], default_cs, default_gr);
486 break;
487 case FCORDER_SUB: /* misc format control orders */
488 case FCORDER_EO:
489 END_TEXT(see_ebc(*cp));
490 previous = ORDER;
491 ctlr_add(0, '\0', default_cs, default_gr);
492 break;
493 case FCORDER_NULL:
494 END_TEXT("NULL");
495 previous = NULLCH;
496 ctlr_add(0, '\0', default_cs, default_gr);
497 break;
498 default: /* enter character */
499 if (*cp <= 0x3F) {
500 END_TEXT("ILLEGAL-ORDER ");
501 previous = ORDER;
502 ctlr_add(0, '\0', default_cs, default_gr);
503 trace_ds("%s", see_ebc(*cp));
504 break;
505 }
506 if (previous != TEXT) {
507 trace_ds(" '");
508 }
509 previous = TEXT;
510 trace_ds("%s", see_ebc(*cp));
511 ctlr_add(*cp, ebcdic_to_unicode(*cp, default_cs, EUO_NONE),
512 default_cs, default_gr);
513 break;
514 }
515 }
516
517 trace_ds("\n");
518 }
519
520 #undef START_FIELDx
521 #undef START_FIELD0
522 #undef START_FIELD
523 #undef END_TEXT0
524 #undef END_TEXT
525
526 /*
527 * Process SCS (SNA Character Stream) data.
528 */
529
530 /* Reinitialize the SCS virtual 3287. */
531 static void
init_scs_horiz(void)532 init_scs_horiz(void)
533 {
534 int i;
535
536 mpp = MAX_MPP;
537 lm = 1;
538 htabs[1] = 1;
539 for (i = 2; i <= MAX_MPP; i++) {
540 htabs[i] = 0;
541 }
542 }
543
544 static void
init_scs_vert(void)545 init_scs_vert(void)
546 {
547 int i;
548
549 mpl = 1;
550 tm = 1;
551 bm = mpl;
552 vtabs[1] = 1;
553 for (i = 0; i <= MAX_MPL; i++) {
554 vtabs[i] = 0;
555 }
556 }
557
558 static void
init_scs(void)559 init_scs(void)
560 {
561 int i;
562
563 if (scs_initted) {
564 return;
565 }
566
567 trace_ds("Initializing SCS virtual 3287.\n");
568 init_scs_horiz();
569 init_scs_vert();
570 pp = 1;
571 line = 1;
572 scs_any = 0;
573 for (i = 0; i < MAX_MPP+1; i++) {
574 linebuf[i] = ' ';
575 }
576 for (i = 0; i < MAX_MPP+1; i++) {
577 if (trnbuf[i].malloc_len != 0) {
578 Free(trnbuf[i].buf);
579 trnbuf[i].buf = NULL;
580 trnbuf[i].malloc_len = 0;
581 }
582 trnbuf[i].data_len = 0;
583 }
584 scs_leftover_len = 0;
585 scs_dbcs_subfield = 0;
586 scs_dbcs_c1 = 0;
587 scs_cs = 0;
588
589 scs_initted = true;
590 }
591
592 #if defined(_WIN32) /*[*/
593 static int
unicode_to_printer(ucs4_t u,char mb[],int mb_len)594 unicode_to_printer(ucs4_t u, char mb[], int mb_len)
595 {
596 int nc;
597 wchar_t wuc = u;
598
599 nc = WideCharToMultiByte(options.printercp, 0, &wuc, 1, mb, mb_len, NULL,
600 NULL);
601 if (nc > 0) {
602 mb[nc++] = '\0';
603 }
604 return nc;
605 }
606 #endif /*[*/
607
608 /*
609 * Our philosophy for automatic newlines and formfeeds is that we generate them
610 * only if the user attempts to put data outside the MPP/MPL-defined area.
611 * Therefore, the user can put a byte on the last column of each line, and on
612 * the last column of the last line of the page, and not need to worry about
613 * suppressing their own NL or FF.
614 */
615
616 /*
617 * Dump and reset the current line.
618 * This will always result in at least one byte of output to the printer (a
619 * newline). The 'line' variable is always incremented, and may end up
620 * pointing past the bottom margin. The 'pp' variable is set to the left
621 * margin.
622 *
623 * We do not observe the -skipcc option with SCS data.
624 */
625 static int
dump_scs_line(bool reset_pp,bool always_nl)626 dump_scs_line(bool reset_pp, bool always_nl)
627 {
628 int i;
629 bool any_data = false;
630
631 /* Find the last non-space character in the line buffer. */
632 for (i = mpp; i >= 1; i--) {
633 if (trnbuf[i].data_len != 0 || linebuf[i] != ' ') {
634 break;
635 }
636 }
637
638 /*
639 * If there is data there, print it with a trailing newline and
640 * clear out the line buffer for next time. If not, just print the
641 * newline.
642 */
643 if (i >= 1) {
644 int j;
645 int n_data = 0;
646 int n_trn = 0;
647 int k;
648
649 for (j = 1; j <= i; j++) {
650 /*
651 * Dump and transparent data that precedes this
652 * character.
653 */
654 if (trnbuf[j].data_len) {
655 unsigned k;
656
657 n_trn += trnbuf[j].data_len;
658 for (k = 0; k < trnbuf[j].data_len; k++) {
659 if (stash(trnbuf[j].buf[k]) < 0) {
660 return -1;
661 }
662 }
663 trnbuf[j].data_len = 0;
664 }
665 if (j < i || linebuf[j] != ' ') {
666 char mb[16];
667 int len;
668
669 if (linebuf[j] == FCORDER_NOP) {
670 continue;
671 }
672 n_data++;
673 any_data = true;
674 scs_any = true;
675 #if !defined(_WIN32) /*[*/
676 len = unicode_to_multibyte(linebuf[j], mb, sizeof(mb));
677 #else /*][*/
678 len = unicode_to_printer(linebuf[j], mb, sizeof(mb));
679 #endif /*]*/
680 if (len == 0) {
681 mb[0] = ' ';
682 len = 1;
683 } else {
684 len--;
685 }
686 for (k = 0; k < len; k++) {
687 if (stash(mb[k]) < 0) {
688 return -1;
689 }
690 }
691 }
692 }
693 #if defined(DEBUG_FF) /*[*/
694 trace_ds(" [dumping %d+%dt]", n_data, n_trn);
695 #endif /*]*/
696 for (k = 0; k < MAX_MPP+1; k++) {
697 linebuf[k] = ' ';
698 }
699 }
700 if (any_data || always_nl) {
701 if (options.crlf) {
702 if (stash('\r') < 0) {
703 return -1;
704 }
705 }
706 if (stash('\n') < 0)
707 return -1;
708 line++;
709 }
710 #if defined(DEBUG_FF) /*[*/
711 trace_ds(" [line=%d]", line);
712 #endif /*]*/
713 if (reset_pp) {
714 pp = lm;
715 }
716 any_scs_output = false;
717 return 0;
718 }
719
720 /* SCS formfeed. */
721 static int
scs_formfeed(bool explicit)722 scs_formfeed(bool explicit)
723 {
724 int nls = 0;
725
726 /*
727 * In ffskip mode, if it's an explicit formfeed, and we haven't
728 * printed any non-transparent data, do nothing.
729 */
730 if (options.ffskip && explicit && !scs_any) {
731 return 0;
732 }
733
734 /*
735 * In ffthru mode, pass through a \f, but only if it's explicit.
736 */
737 if (options.ffthru) {
738 if (explicit) {
739 if (stash('\f') < 0) {
740 return -1;
741 }
742 scs_any = 0;
743 }
744 line = 1;
745 return 0;
746 }
747
748 if (explicit) {
749 scs_any = 0;
750 }
751
752 if (mpl > 1) {
753 /* Skip to the end of the physical page. */
754 while (line <= mpl) {
755 if (options.crlf) {
756 if (stash('\r') < 0) {
757 return -1;
758 }
759 }
760 if (stash('\n') < 0) {
761 return -1;
762 }
763 nls++;
764 line++;
765 }
766 line = 1;
767
768 /* Skip the top margin. */
769 while (line < tm) {
770 if (options.crlf) {
771 if (stash('\r') < 0) {
772 return -1;
773 }
774 }
775 if (stash('\n') < 0) {
776 return -1;
777 }
778 nls++;
779 line++;
780 }
781 #if defined(DEBUG_FF) /*[*/
782 if (nls) {
783 trace_ds(" [formfeed %s %d]", explicit? "explicit": "implicit",
784 nls);
785 }
786 #endif /*]*/
787 } else {
788 line = 1;
789 }
790 return 0;
791 }
792
793 /*
794 * Add a printable character to the SCS virtual 3287.
795 * If the line position is past the bottom margin, we will skip to the top of
796 * the next page. If the character position is past the MPP, we will skip to
797 * the left margin of the next line.
798 */
799 static int
add_scs(ucs4_t c)800 add_scs(ucs4_t c)
801 {
802 /*
803 * They're about to print something.
804 * If the line is past the bottom margin, we need to skip to the
805 * MPL, and then past the top margin.
806 */
807 if (line > bm) {
808 if (scs_formfeed(false) < 0) {
809 return -1;
810 }
811 }
812
813 /*
814 * If this character would overflow the line, then dump the current
815 * line and start over at the left margin.
816 */
817 if (pp > mpp) {
818 if (dump_scs_line(true, true) < 0) {
819 return -1;
820 }
821 }
822
823 /*
824 * Store this character in the line buffer and advance the print
825 * position.
826 */
827 if (c != ' ') {
828 linebuf[pp++] = c;
829 } else {
830 pp++;
831 }
832 any_scs_output = true;
833 ffeoj_last = false;
834 return 0;
835 }
836
837 /*
838 * Add a string of transparent data to the SCS virtual 3287.
839 * Transparent data lives between the 'counted' 3287 characters. Really.
840 */
841 static void
add_scs_trn(unsigned char * cp,int cnt)842 add_scs_trn(unsigned char *cp, int cnt)
843 {
844 int i;
845 unsigned new_malloc_len;
846
847 for (i = 0; i < cnt; i++) {
848 trace_ds(" %02x", cp[i]);
849 }
850
851 new_malloc_len = trnbuf[pp].data_len + cnt;
852 while (trnbuf[pp].malloc_len < new_malloc_len) {
853 trnbuf[pp].malloc_len += BUFSZ;
854 trnbuf[pp].buf = Realloc(trnbuf[pp].buf, trnbuf[pp].malloc_len);
855 }
856 memcpy(trnbuf[pp].buf + trnbuf[pp].data_len, cp, cnt);
857 trnbuf[pp].data_len += cnt;
858 any_scs_output = true;
859 ffeoj_last = true;
860 }
861
862 /*
863 * Process a bufferful of SCS data.
864 *
865 * Note that unlike a 3270 Write command, even though the record is bounded
866 * by an EOR, the SCS data are not guaranteed to be complete.
867 *
868 * Rather than have a full FSM for every byte of every SCS order, we resort
869 * to the rather inefficient method of concatenating the previous, incomplete
870 * record with a copy of the new record, processing it as a contiguous
871 * buffer, and saving any incomplete order for next time.
872 */
873
874 /*
875 * 'Internal' SCS function, called by process_scs() below with the previous
876 * leftover data plus the current buffer.
877 *
878 * If an incomplete order is detected, saves it in scs_leftover_buf for
879 * next time.
880 */
881 static enum pds
process_scs_contig(unsigned char * buf,size_t buflen)882 process_scs_contig(unsigned char *buf, size_t buflen)
883 {
884 unsigned char *cp;
885 int i;
886 int cnt;
887 int tab;
888 ucs4_t uc;
889 enum { NONE, DATA, ORDER } last = NONE;
890 # define END_TEXT(s) { \
891 if (last == DATA) { \
892 trace_ds("'"); \
893 } \
894 trace_ds(" " s); \
895 last = ORDER; \
896 }
897 # define LEFTOVER { \
898 trace_ds(" [pending]"); \
899 scs_leftover_len = buflen - (cp - buf); \
900 memcpy(scs_leftover_buf, cp, scs_leftover_len); \
901 cp = buf + buflen; \
902 }
903
904 trace_ds("< ");
905
906 init_scs();
907
908 for (cp = &buf[0]; cp < (buf + buflen); cp++) {
909 switch (*cp) {
910 case SCS_BS: /* back space */
911 END_TEXT("BS");
912 if (pp != 1) {
913 pp--;
914 }
915 if (scs_dbcs_subfield && pp != 1) {
916 pp--;
917 }
918 break;
919 case SCS_CR: /* carriage return */
920 END_TEXT("CR");
921 pp = lm;
922 break;
923 case SCS_ENP: /* enable presentation */
924 END_TEXT("ENP");
925 /* No-op. */
926 break;
927 case SCS_FF: /* form feed */
928 END_TEXT("FF");
929 /* Dump any pending data, and go to the next line. */
930 if (dump_scs_line(true, false) < 0) {
931 return PDS_FAILED;
932 }
933 /*
934 * If there is a max page length, skip to the next
935 * page.
936 */
937 if (scs_formfeed(true) < 0) {
938 return PDS_FAILED;
939 }
940 break;
941 case SCS_HT: /* horizontal tab */
942 END_TEXT("HT");
943 for (i = pp + 1; i <= mpp; i++) {
944 if (htabs[i]) {
945 break;
946 }
947 }
948 if (i <= mpp) {
949 pp = i;
950 } else {
951 if (add_scs(' ') < 0) {
952 return PDS_FAILED;
953 }
954 }
955 break;
956 case SCS_INP: /* inhibit presentation */
957 END_TEXT("INP");
958 /* No-op. */
959 break;
960 case SCS_IRS: /* inter-record separator */
961 END_TEXT("IRS");
962 case SCS_NL: /* new line */
963 if (*cp == SCS_NL) {
964 END_TEXT("NL");
965 }
966 if (dump_scs_line(true, true) < 0) {
967 return PDS_FAILED;
968 }
969 break;
970 case SCS_VT: /* vertical tab */
971 END_TEXT("VT");
972 for (i = line + 1; i <= MAX_MPL; i++){
973 if (vtabs[i]) {
974 break;
975 }
976 }
977 if (i <= MAX_MPL) {
978 if (dump_scs_line(false, true) < 0) {
979 return PDS_FAILED;
980 }
981 while (line < i) {
982 if (options.crlf) {
983 if (stash('\r') < 0) {
984 return PDS_FAILED;
985 }
986 }
987 if (stash('\n') < 0) {
988 return PDS_FAILED;
989 }
990 line++;
991 }
992 break;
993 } else {
994 /* fall through... */
995 }
996 case SCS_VCS: /* vertical channel select */
997 if (*cp == SCS_VCS) {
998 END_TEXT("VCS");
999 }
1000 /* fall through... */
1001 case SCS_LF: /* line feed */
1002 if (*cp == SCS_LF)
1003 END_TEXT("LF");
1004 if (dump_scs_line(false, true) < 0)
1005 return PDS_FAILED;
1006 break;
1007 case SCS_GE: /* graphic escape */
1008 END_TEXT("GE");
1009 if ((cp + 1) >= buf + buflen) {
1010 LEFTOVER;
1011 break;
1012 }
1013 /* Skip over the order. */
1014 cp++;
1015 /* No support, so all characters are spaces. */
1016 trace_ds(" %02x", *cp);
1017 if (add_scs(' ') < 0) {
1018 return PDS_FAILED;
1019 }
1020 break;
1021 case SCS_SA: /* set attribute */
1022 END_TEXT("SA");
1023 if ((cp + 2) >= buf + buflen) {
1024 LEFTOVER;
1025 break;
1026 }
1027 switch (*(cp + 1)) {
1028 case SCS_SA_RESET:
1029 trace_ds(" Reset(%02x)", *(cp + 2));
1030 scs_dbcs_subfield = 0;
1031 scs_cs = 0;
1032 break;
1033 case SCS_SA_HIGHLIGHT:
1034 trace_ds(" Highlight(%02x)", *(cp + 2));
1035 break;
1036 case SCS_SA_CS:
1037 trace_ds(" CharacterSet(%02x)", *(cp + 2));
1038 if (scs_cs != *(cp + 2)) {
1039 if (scs_cs == 0xf8) {
1040 scs_dbcs_subfield = 0;
1041 } else if (*(cp + 2) == 0xf8) {
1042 scs_dbcs_subfield = 1;
1043 }
1044 scs_cs = *(cp + 2);
1045 }
1046 break;
1047 case SCS_SA_GRID:
1048 trace_ds(" Grid(%02x)", *(cp + 2));
1049 break;
1050 default:
1051 trace_ds(" Unknown(%02x %02x)", *(cp + 1), *(cp + 2));
1052 break;
1053 }
1054 /* Skip it. */
1055 cp += 2;
1056 break;
1057 case SCS_TRN: /* transparent */
1058 END_TEXT("TRN");
1059 /* Make sure a length byte is present. */
1060 if ((cp + 1) >= buf + buflen) {
1061 LEFTOVER;
1062 break;
1063 }
1064 /* Skip over the order. */
1065 cp++;
1066 /*
1067 * Next byte is the length of the transparent data,
1068 * not including the length byte itself.
1069 */
1070 cnt = *cp;
1071 if (cp + cnt - 1 >= buf + buflen) {
1072 cp--;
1073 LEFTOVER;
1074 break;
1075 }
1076 trace_ds("(%d)", cnt);
1077 /* Copy out the data literally. */
1078 add_scs_trn(cp+1, cnt);
1079 cp += cnt;
1080 scs_dbcs_subfield = 0;
1081 break;
1082 case SCS_SET: /* set... */
1083 /* Skip over the first byte of the order. */
1084 if (cp + 2 >= buf + buflen ||
1085 cp + *(cp + 2) - 1 >= buf + buflen) {
1086 END_TEXT("SET");
1087 LEFTOVER;
1088 break;
1089 }
1090 switch (*++cp) {
1091 case SCS_SHF: /* set horizontal format */
1092 END_TEXT("SHF");
1093 /* Take defaults first. */
1094 init_scs_horiz();
1095 /*
1096 * The length is next. It includes the
1097 * length field itself.
1098 */
1099 cnt = *++cp;
1100 trace_ds("(%d)", cnt);
1101 if (cnt < 2) {
1102 break; /* no more data */
1103 }
1104 /* Skip over the length byte. */
1105 if (!--cnt || cp + 1 >= buf + buflen) {
1106 break;
1107 }
1108 /* The MPP is next. */
1109 mpp = *++cp;
1110 trace_ds(" mpp=%d", mpp);
1111 if (!mpp || mpp > MAX_MPP) {
1112 mpp = MAX_MPP;
1113 }
1114 /* Skip over the MPP. */
1115 if (!--cnt || cp + 1 >= buf + buflen) {
1116 break;
1117 }
1118 /* The LM is next. */
1119 lm = *++cp;
1120 trace_ds(" lm=%d", lm);
1121 if (lm < 1 || lm >= mpp) {
1122 lm = 1;
1123 }
1124 /* Skip over the LM. */
1125 if (!--cnt || cp + 1 >= buf + buflen) {
1126 break;
1127 }
1128 /* Skip over the RM. */
1129 cp++;
1130 trace_ds(" rm=%d", *cp);
1131 /* Next are the tab stops. */
1132 while (--cnt && cp + 1 < buf + buflen) {
1133 tab = *++cp;
1134 trace_ds(" tab=%d", tab);
1135 if (tab >= 1 && tab <= mpp) {
1136 htabs[tab] = 1;
1137 }
1138 }
1139 break;
1140 case SCS_SLD: /* set line density */
1141 END_TEXT("SLD");
1142 /*
1143 * Skip over the second byte of the
1144 * order.
1145 */
1146 cp++;
1147 /*
1148 * The length is next. It does not
1149 * include length field itself.
1150 */
1151 if (cp >= buf + buflen) {
1152 break;
1153 }
1154 cnt = *cp;
1155 trace_ds("(%d)", cnt);
1156 if (cnt != 2) {
1157 break; /* be gentle */
1158 }
1159 cnt--;
1160 trace_ds(" %02x", *(cp + 1));
1161 cp += cnt;
1162 break;
1163 case SCS_SVF: /* set vertical format */
1164 END_TEXT("SVF");
1165 /* Take defaults first. */
1166 init_scs_vert();
1167 /*
1168 * Skip over the second byte of the
1169 * order.
1170 */
1171 cp++;
1172 /*
1173 * The length is next. It includes the
1174 * length field itself.
1175 */
1176 if (cp >= buf + buflen) {
1177 break;
1178 }
1179 cnt = *cp;
1180 trace_ds("(%d)", cnt);
1181 if (cnt < 2) {
1182 break; /* no more data */
1183 }
1184 /* Skip over the length byte. */
1185 cp++;
1186 cnt--;
1187 if (!cnt || cp >= buf + buflen) {
1188 break;
1189 }
1190 /* The MPL is next. */
1191 mpl = *cp;
1192 trace_ds(" mpl=%d", mpl);
1193 if (!mpl || mpl > MAX_MPL) {
1194 mpl = 1;
1195 }
1196 if (cnt < 2) {
1197 bm = mpl;
1198 break;
1199 }
1200 /* Skip over the MPL. */
1201 cp++;
1202 cnt--;
1203 if (!cnt || cp >= buf + buflen) {
1204 break;
1205 }
1206 /* The TM is next. */
1207 tm = *cp;
1208 trace_ds(" tm=%d", tm);
1209 if (tm < 1 || tm >= mpl) {
1210 tm = 1;
1211 }
1212 if (cnt < 2) {
1213 break;
1214 }
1215 /* Skip over the TM. */
1216 cp++;
1217 cnt--;
1218 if (!cnt || cp >= buf + buflen) {
1219 break;
1220 }
1221 /* The BM is next. */
1222 bm = *cp;
1223 trace_ds(" bm=%d", bm);
1224 if (bm < tm || bm >= mpl) {
1225 bm = mpl;
1226 }
1227 if (cnt < 2) {
1228 break;
1229 }
1230 /* Skip over the BM. */
1231 cp++;
1232 cnt--;
1233 /* Next are the tab stops. */
1234 while (cnt > 1 && cp < buf + buflen) {
1235 tab = *cp;
1236 trace_ds(" tab=%d", tab);
1237 if (tab >= 1 && tab <= mpp) {
1238 vtabs[tab] = 1;
1239 }
1240 cp++;
1241 cnt--;
1242 }
1243 break;
1244 default:
1245 END_TEXT("SET(?");
1246 trace_ds("%02x)", *cp);
1247 cp += *(cp + 1);
1248 break;
1249 }
1250 break;
1251 case SCS_SO: /* DBCS subfield start */
1252 END_TEXT("SO");
1253 scs_dbcs_subfield = 1;
1254 break;
1255 case SCS_SI: /* DBCS subfield end */
1256 END_TEXT("SI");
1257 scs_dbcs_subfield = 0;
1258 break;
1259 default:
1260 /*
1261 * Stray control codes are spaces, all else gets
1262 * translated from EBCDIC to ASCII.
1263 */
1264 if (*cp <= 0x3f) {
1265 END_TEXT("?");
1266 trace_ds("%02x", *cp);
1267 if (add_scs(' ') < 0) {
1268 return PDS_FAILED;
1269 }
1270 break;
1271 }
1272
1273 if (last == NONE) {
1274 trace_ds("'");
1275 } else if (last == ORDER) {
1276 trace_ds(" '");
1277 }
1278 if (scs_dbcs_subfield && dbcs) {
1279 if (scs_dbcs_subfield % 2) {
1280 scs_dbcs_c1 = *cp;
1281 } else {
1282 uc = ebcdic_to_unicode( (scs_dbcs_c1 << 8) | *cp, CS_BASE,
1283 EUO_NONE);
1284 if (uc == 0) {
1285 /* No translation. */
1286 trace_ds("?DBCS(X'%02x%02x')", scs_dbcs_c1, *cp);
1287 if (add_scs(' ') < 0) {
1288 return PDS_FAILED;
1289 }
1290 if (add_scs(' ') < 0) {
1291 return PDS_FAILED;
1292 }
1293 } else {
1294 /*
1295 * Add the Unicode character
1296 * and a no-op to account for
1297 * the right-hand side.
1298 */
1299 trace_ds("DBCS(X'%02x%02x')", scs_dbcs_c1, *cp);
1300 if (add_scs(uc) < 0) {
1301 return PDS_FAILED;
1302 }
1303 if (add_scs(FCORDER_NOP) < 0) {
1304 return PDS_FAILED;
1305 }
1306 }
1307 }
1308 scs_dbcs_subfield++;
1309 last = DATA;
1310 break;
1311 }
1312 uc = ebcdic_to_unicode(*cp, CS_BASE, EUO_NONE);
1313 {
1314 char mb[16];
1315
1316 unicode_to_multibyte(uc, mb, sizeof(mb));
1317 trace_ds("%s", mb);
1318 }
1319 if (add_scs(uc) < 0) {
1320 return PDS_FAILED;
1321 }
1322 last = DATA;
1323 break;
1324 }
1325 }
1326
1327 if (last == DATA) {
1328 trace_ds("'");
1329 }
1330 trace_ds("\n");
1331 if (prflush() < 0) {
1332 return PDS_FAILED;
1333 }
1334 return PDS_OKAY_NO_OUTPUT;
1335 }
1336
1337 /*
1338 * 'External' SCS function. Handles leftover data from any previous,
1339 * incomplete SCS record.
1340 */
1341 enum pds
process_scs(unsigned char * buf,size_t buflen)1342 process_scs(unsigned char *buf, size_t buflen)
1343 {
1344 enum pds r;
1345
1346 if (scs_leftover_len) {
1347 unsigned char *contig = Malloc(scs_leftover_len + buflen);
1348 size_t total_len;
1349
1350 memcpy(contig, scs_leftover_buf, scs_leftover_len);
1351 memcpy(contig + scs_leftover_len, buf, buflen);
1352 total_len = scs_leftover_len + buflen;
1353 scs_leftover_len = 0;
1354 r = process_scs_contig(contig, total_len);
1355 Free(contig);
1356 } else {
1357 r = process_scs_contig(buf, buflen);
1358 }
1359 return r;
1360 }
1361
1362 #if !defined(_WIN32) /*[*/
1363 /*
1364 * SIGCHLD handler. Does nothing, but on systems that conform to the Single
1365 * Unix Specification, defining it ensures that the print command process will
1366 * become a zombie if it exits prematurely.
1367 */
1368 static void
sigchld_handler(int sig)1369 sigchld_handler(int sig)
1370 {
1371 }
1372
1373 /*
1374 * Special version of popen where the child ignores SIGINT.
1375 */
1376 static FILE *
popen_no_sigint(const char * command)1377 popen_no_sigint(const char *command)
1378 {
1379 int fds[2];
1380 FILE *f;
1381
1382 /* Create a pipe. */
1383 if (pipe(fds) < 0) {
1384 return NULL;
1385 }
1386
1387 /* Create a stdio stream from the write end. */
1388 f = fdopen(fds[1], "w");
1389 if (f == NULL) {
1390 close(fds[0]);
1391 close(fds[1]);
1392 return NULL;
1393 }
1394
1395 /* Handle SIGCHLD signals. */
1396 signal(SIGCHLD, sigchld_handler);
1397
1398 /* Fork a child process. */
1399 switch ((prpid = fork())) {
1400 case 0: /* child */
1401 fclose(f);
1402 dup2(fds[0], 0);
1403 close(fds[1]);
1404 signal(SIGINT, SIG_IGN);
1405 execl("/bin/sh", "sh", "-c", command, NULL);
1406
1407 /* execl failed, return nonzero status */
1408 exit(1);
1409 break;
1410 case -1: /* parent, error */
1411 fclose(f);
1412 close(fds[0]);
1413 return NULL;
1414 default: /* parent, success */
1415 close(fds[0]);
1416 break;
1417 }
1418
1419 return f;
1420 }
1421
1422 static int
pclose_no_sigint(FILE * f)1423 pclose_no_sigint(FILE *f)
1424 {
1425 int rc;
1426 int status;
1427
1428 fclose(f);
1429 do {
1430 rc = waitpid(prpid, &status, 0);
1431 } while (rc < 0 && errno == EINTR);
1432 prpid = -1;
1433 if (rc < 0) {
1434 return rc;
1435 } else {
1436 return status;
1437 }
1438 }
1439 #endif /*]*/
1440
1441 /*
1442 * Send a character to the printer.
1443 */
1444 static int
stash(unsigned char c)1445 stash(unsigned char c)
1446 {
1447 #if defined(_WIN32) /*[*/
1448 if (!ws_initted) {
1449 if (ws_start(options.printer) < 0) {
1450 return -1;
1451 }
1452 ws_initted = 1;
1453 }
1454 if (ws_needpre) {
1455 if ((options.trnpre != NULL) && copyfile(options.trnpre) < 0) {
1456 return -1;
1457 }
1458 ws_needpre = 0;
1459 }
1460
1461 trace_pdc(c);
1462 if (ws_putc((char)c)) {
1463 return -1;
1464 }
1465 #else /*][*/
1466 if (prfile == NULL) {
1467 prfile = popen_no_sigint(options.command);
1468 if (prfile == NULL) {
1469 errmsg("%s: %s", options.command, strerror(errno));
1470 return -1;
1471 }
1472 if ((options.trnpre != NULL) && copyfile(options.trnpre) < 0) {
1473 pclose_no_sigint(prfile);
1474 prfile = NULL;
1475 return -1;
1476 }
1477 }
1478
1479 trace_pdc(c);
1480 if (fputc(c, prfile) == EOF) {
1481 errmsg("Write error to '%s': %s", options.command, strerror(errno));
1482 pclose_no_sigint(prfile);
1483 prfile = NULL;
1484 return -1;
1485 }
1486 #endif /*]*/
1487
1488 return 0;
1489 }
1490
1491 /*
1492 * Flush the pipe going to the printer process, to try to flush out any
1493 * pending errors.
1494 */
1495 static int
prflush(void)1496 prflush(void)
1497 {
1498 #if defined(_WIN32) /*[*/
1499 if (ws_initted && ws_flush() < 0) {
1500 return -1;
1501 }
1502 #else /*][*/
1503 if (prfile != NULL) {
1504 if (fflush(prfile) < 0) {
1505 errmsg("Flush error to '%s': %s", options.command, strerror(errno));
1506 pclose_no_sigint(prfile);
1507 prfile = NULL;
1508 return -1;
1509 }
1510 }
1511 #endif /*]*/
1512 return 0;
1513 }
1514
1515 /*
1516 * Change a character in the 3270 buffer.
1517 */
1518 void
ctlr_add(unsigned char ebc,ucs4_t c,unsigned char cs,unsigned char gr)1519 ctlr_add(unsigned char ebc, ucs4_t c, unsigned char cs, unsigned char gr)
1520 {
1521 /* Map control characters, according to the write mode. */
1522 if (c < ' ') {
1523 if (wcc_line_length) {
1524 /*
1525 * When formatted, all control characters but FFs and
1526 * the funky VISIBLE/INVISIBLE controls are translated
1527 * to NULLs, so they don't display, and don't
1528 * contribute to empty lines.
1529 */
1530 if (c != FCORDER_FF && c != VISIBLE && c != INVISIBLE) {
1531 c = '\0';
1532 }
1533 } else {
1534 /*
1535 * Unformatted, all control characters but CR/NL/FF/EM
1536 * are displayed as spaces.
1537 */
1538 if (c != FCORDER_CR && c != FCORDER_NL &&
1539 c != FCORDER_FF && c != FCORDER_EM) {
1540 c = ' ';
1541 }
1542 }
1543 }
1544
1545 /* Add the character. */
1546 page_buf[baddr] = c;
1547 if (ebc >= 0x40)
1548 xlate_len[baddr] = xtable_lookup(ebc, &xlate_buf[baddr]);
1549 baddr = (baddr + 1) % MAX_BUF;
1550 any_3270_output = 1;
1551 ffeoj_last = false;
1552
1553 /* Implement -emflush mode. */
1554 if (options.emflush && !wcc_line_length && c == FCORDER_EM) {
1555 /* XXX: Unfortunately, we do not return error status here. */
1556 dump_unformatted();
1557 baddr = 1;
1558 any_3270_output = 0;
1559 }
1560 }
1561
1562 static struct {
1563 char buf; /* printable data */
1564 unsigned char *trn; /* transparent data */
1565 unsigned trn_len; /* length of transparent data */
1566 } uo_data[MAX_UNF_MPP + 2]; /* room for full line plus carriage control */
1567 static unsigned uo_col; /* current output column */
1568 static unsigned uo_maxcol; /* maximum column buffered */
1569 static bool uo_last_cr = false; /* last data was CR */
1570
1571 /*
1572 * Dump and free any transparent unformatted data at col.
1573 */
1574 static int
dump_uo_trn(unsigned col)1575 dump_uo_trn(unsigned col)
1576 {
1577 unsigned i;
1578 int rv = 0;
1579
1580 if (uo_data[col].trn != NULL) {
1581 for (i = 0; i < uo_data[col].trn_len; i++) {
1582 if (stash(uo_data[col].trn[i]) < 0) {
1583 rv = -1;
1584 break;
1585 }
1586 }
1587 Free(uo_data[col].trn);
1588 uo_data[col].trn = NULL;
1589 uo_data[col].trn_len = 0;
1590 }
1591 return rv;
1592 }
1593
1594 /*
1595 * Dump pending unformatted output.
1596 */
1597 static int
dump_uo(void)1598 dump_uo(void)
1599 {
1600 unsigned i;
1601
1602 for (i = 0; i < uo_maxcol; i++) {
1603 if (dump_uo_trn(i) < 0) {
1604 return -1;
1605 }
1606 if (!i && options.skipcc) {
1607 continue;
1608 }
1609 if (stash(uo_data[i].buf) < 0) {
1610 return -1;
1611 }
1612 }
1613 if (uo_maxcol < MAX_UNF_MPP + 2) {
1614 if (dump_uo_trn(uo_maxcol) < 0) {
1615 return -1;
1616 }
1617 }
1618 return 0;
1619 }
1620
1621 /*
1622 * Unformatted output function. Processes one character of output data.
1623 *
1624 * This function will buffer up to MPP characters of output, until it is
1625 * passed a '\n' or '\f' character.
1626 *
1627 * By default, it will process '\r' characters like a printer, i.e., it will
1628 * not overwrite a buffered non-space character with a space character. This
1629 * is how an output line can span multiple 3270 unformatted write commands.
1630 *
1631 * If 'crthru' is set, '\r' characters simply trigger a buffer flush.
1632 */
1633 static int
uoutput(char c)1634 uoutput(char c)
1635 {
1636 switch (c) {
1637 case '\r':
1638 if (options.crthru) {
1639 if (dump_uo() < 0) {
1640 return -1;
1641 }
1642 if (stash(c) < 0) {
1643 return -1;
1644 }
1645 uo_col = uo_maxcol = 0;
1646 uo_last_cr = true;
1647 } else {
1648 uo_col = 0;
1649 }
1650 break;
1651 case '\n':
1652 if (dump_uo() < 0) {
1653 return -1;
1654 }
1655 if (options.crlf && !uo_last_cr) {
1656 if (stash('\r') < 0) {
1657 return -1;
1658 }
1659 }
1660 if (stash(c) < 0) {
1661 return -1;
1662 }
1663 uo_col = uo_maxcol = 0;
1664 uo_last_cr = false;
1665 break;
1666 case '\f':
1667 uo_last_cr = false;
1668 if (any_3270_printable || !options.ffskip) {
1669 if (dump_uo() < 0) {
1670 return -1;
1671 }
1672 if (stash(c) < 0) {
1673 return -1;
1674 }
1675 }
1676 uo_col = uo_maxcol = 0;
1677 break;
1678 default:
1679 uo_last_cr = false;
1680
1681 /* Don't overwrite with spaces. */
1682 if (c == ' ') {
1683 if (uo_col >= uo_maxcol) {
1684 uo_data[uo_col++].buf = c;
1685 } else {
1686 uo_col++;
1687 }
1688 } else {
1689 uo_data[uo_col++].buf = c;
1690 any_3270_printable = true;
1691 }
1692 if (uo_col > uo_maxcol) {
1693 uo_maxcol = uo_col;
1694 }
1695 break;
1696 }
1697 return 0;
1698 }
1699
1700 /*
1701 * Add transparent data to the unformatted output buffer.
1702 */
1703 static void
uoutput_trn(const unsigned char * s,int len)1704 uoutput_trn(const unsigned char *s, int len)
1705 {
1706 unsigned char *new;
1707
1708 if (len <= 0) {
1709 return;
1710 }
1711 new = Realloc(uo_data[uo_col].trn, uo_data[uo_col].trn_len + len);
1712 if (uo_data[uo_col].trn != NULL) {
1713 memcpy(new, uo_data[uo_col].trn, uo_data[uo_col].trn_len);
1714 }
1715 memcpy(new + uo_data[uo_col].trn_len, s, len);
1716 uo_data[uo_col].trn = new;
1717 uo_data[uo_col].trn_len += len;
1718 }
1719
1720 /*
1721 * Dump an unformatted output buffer.
1722 *
1723 * The buffer is treated as a sequence of characters, with control characters
1724 * for new line, carriage return, form feed and end of media.
1725 *
1726 * By definition, the "print position" is 0 when this function begins and ends.
1727 */
1728 static int
dump_unformatted(void)1729 dump_unformatted(void)
1730 {
1731 int i;
1732 int prcol = 0;
1733 ucs4_t c;
1734 int done = 0;
1735 char mb[16];
1736 const char *mbp;
1737 int len;
1738 int j;
1739
1740 if (!any_3270_output) {
1741 return 0;
1742 }
1743
1744 for (i = 0; i < MAX_BUF && !done; i++) {
1745 switch (c = page_buf[i]) {
1746 case '\0':
1747 break;
1748 case FCORDER_NOP:
1749 break;
1750 case FCORDER_CR:
1751 if (uoutput('\r') < 0) {
1752 return -1;
1753 }
1754 prcol = 0;
1755 break;
1756 case FCORDER_NL:
1757 if (uoutput('\n') < 0) {
1758 return -1;
1759 }
1760 prcol = 0;
1761 break;
1762 case FCORDER_FF:
1763 if (uoutput('\f') < 0) {
1764 return -1;
1765 }
1766 prcol = 0;
1767 break;
1768 case FCORDER_EM:
1769 if (prcol != 0) {
1770 if (uoutput('\n') < 0) {
1771 return -1;
1772 }
1773 }
1774 done = 1;
1775 break;
1776 default: /* printable */
1777 /*
1778 * Insert a newline if they send a 133rd printable
1779 * character on a line.
1780 *
1781 * If they specified '-skipcc', don't count the first
1782 * character on the line as printable.
1783 */
1784 if (++prcol > options.mpp + (options.skipcc != 0)) {
1785 if (uoutput('\n') < 0) {
1786 return -1;
1787 }
1788 prcol = 0;
1789 }
1790
1791 /* Handle transparent data. */
1792 if (xlate_buf[i] != NULL) {
1793 uoutput_trn(xlate_buf[i], xlate_len[i]);
1794 break;
1795 }
1796
1797 #if !defined(_WIN32) /*[*/
1798 len = unicode_to_multibyte(c, mb, sizeof(mb));
1799 #else /*][*/
1800 len = unicode_to_printer(c, mb, sizeof(mb));
1801 #endif /*]*/
1802 if (len == 0) {
1803 mb[0] = ' ';
1804 len = 1;
1805 } else {
1806 len--;
1807 }
1808 mbp = mb;
1809 for (j = 0; j < len; j++) {
1810 if (uoutput(mbp[j]) < 0) {
1811 return -1;
1812 }
1813 }
1814
1815 break;
1816 }
1817 }
1818
1819 /* If the buffer didn't end with an EM, flush any pending line. */
1820 if (!done) {
1821 if (uoutput('\n') < 0) {
1822 return -1;
1823 }
1824 }
1825
1826 /* Clear out the buffer. */
1827 memset(page_buf, '\0', MAX_BUF * sizeof(ucs4_t));
1828 memset(xlate_buf, '\0', MAX_BUF * sizeof(unsigned char *));
1829 memset(xlate_len, '\0', MAX_BUF * sizeof(int));
1830
1831 /* Clear the output state. */
1832 for (i = 0; i < MAX_UNF_MPP + 2; i++) {
1833 uo_data[i].buf = 0;
1834 if (uo_data[i].trn != NULL) {
1835 Free(uo_data[i].trn);
1836 }
1837 uo_data[i].trn = NULL;
1838 uo_data[i].trn_len = 0;
1839 }
1840 uo_col = 0;
1841 uo_maxcol = 0;
1842 uo_last_cr = false;
1843
1844 /* Flush buffered data. */
1845 #if defined(_WIN32) /*[*/
1846 if (ws_initted) {
1847 ws_flush();
1848 }
1849 #else /*][*/
1850 fflush(prfile);
1851 #endif /*]*/
1852 any_3270_output = 0;
1853
1854 return 0;
1855 }
1856
1857 /*
1858 * Dump a formatted output buffer.
1859 *
1860 * The buffer is treated as a sequence of lines, with the length specified by
1861 * the write control character.
1862 *
1863 * Each line is terminated by a newline, with trailing spaces and nulls
1864 * suppressed.
1865 * Nulls are displayed as spaces, except when they constitute an entire line,
1866 * in which case the line is suppressed.
1867 * Formfeeds are passed through, and otherwise treated like nulls.
1868 *
1869 * We do not observe the -skipcc option with formatted data.
1870 */
1871 static int
dump_formatted(void)1872 dump_formatted(void)
1873 {
1874 int i;
1875 ucs4_t *cp = page_buf;
1876 int visible = 1;
1877 int newlines = 0;
1878 bool data_without_newline = false;
1879
1880 if (!any_3270_output) {
1881 return 0;
1882 }
1883 for (i = 0; i < MAX_UNF_MPP; i++) {
1884 int blanks = 0;
1885 int any_data = 0;
1886 int j;
1887
1888 for (j = 0; j < line_length && ((i * line_length) + j) < MAX_BUF; j++) {
1889 char c = *cp++;
1890
1891 switch (c) {
1892 case VISIBLE: /* visible field */
1893 visible = 1;
1894 blanks++;
1895 break;
1896 case INVISIBLE: /* invisible field */
1897 visible = 0;
1898 blanks++;
1899 break;
1900 case '\f':
1901 while (newlines) {
1902 if (options.crlf) {
1903 if (stash('\r') < 0) {
1904 return -1;
1905 }
1906 }
1907 if (stash('\n') < 0) {
1908 return -1;
1909 }
1910 newlines--;
1911 data_without_newline = false;
1912 }
1913 if (any_3270_printable || !options.ffskip) {
1914 if (stash('\f') < 0) {
1915 return -1;
1916 }
1917 }
1918 blanks++;
1919 break;
1920 case '\0':
1921 blanks++;
1922 break;
1923 case ' ':
1924 blanks++;
1925 any_data++;
1926 data_without_newline = true;
1927 break;
1928 default:
1929 while (newlines) {
1930 if (options.crlf) {
1931 if (stash('\r') < 0) {
1932 return -1;
1933 }
1934 }
1935 if (stash('\n') < 0) {
1936 return -1;
1937 }
1938 newlines--;
1939 data_without_newline = false;
1940 }
1941 while (blanks) {
1942 if (stash(' ') < 0) {
1943 return -1;
1944 }
1945 blanks--;
1946 }
1947 any_data++;
1948 data_without_newline = true;
1949 if (!visible) {
1950 if (stash(' ') < 0) {
1951 return -1;
1952 }
1953 } else {
1954 char mb[16];
1955 int len;
1956 int j;
1957
1958 #if !defined(_WIN32) /*[*/
1959 len = unicode_to_multibyte(c, mb, sizeof(mb));
1960 #else /*][*/
1961 len = unicode_to_printer(c, mb, sizeof(mb));
1962 #endif /*]*/
1963 if (len == 0) {
1964 mb[0] = ' ';
1965 len = 1;
1966 } else {
1967 len--;
1968 }
1969 for (j = 0; j < len; j++) {
1970 if (stash(mb[j]) < 0) {
1971 return -1;
1972 }
1973 }
1974
1975 }
1976 if (visible) {
1977 any_3270_printable = true;
1978 }
1979 break;
1980 }
1981 }
1982 if (any_data || options.blanklines) {
1983 newlines++;
1984 }
1985 }
1986
1987 /* If there was data on the last line, put out a newline. */
1988 if (data_without_newline) {
1989 if (options.crlf) {
1990 if (stash('\r') < 0) {
1991 return -1;
1992 }
1993 }
1994 if (stash('\n') < 0) {
1995 return -1;
1996 }
1997 }
1998
1999 /* Clear the buffer. */
2000 memset(page_buf, '\0', MAX_BUF * sizeof(ucs4_t));
2001 #if defined(_WIN32) /*[*/
2002 if (ws_initted) {
2003 ws_flush();
2004 }
2005 #else /*][*/
2006 fflush(prfile);
2007 #endif /*]*/
2008 any_3270_output = 0;
2009
2010 return 0;
2011 }
2012
2013 int
print_eoj(void)2014 print_eoj(void)
2015 {
2016 int rc = 0;
2017
2018 /* Dump any pending 3270-mode output. */
2019 if (any_3270_output) {
2020 if (wcc_line_length) {
2021 if (dump_formatted() < 0) {
2022 rc = -1;
2023 }
2024 } else {
2025 if (dump_unformatted() < 0) {
2026 rc = -1;
2027 }
2028 }
2029 }
2030
2031 /* Dump any pending SCS-mode output. */
2032 if (any_scs_output) {
2033 if (dump_scs_line(true, false) < 0) {
2034 rc = -1;
2035 }
2036 }
2037
2038 /* Handle -ffeoj, which blindly adds a formfeed to every page. */
2039 if (options.ffeoj && !ffeoj_last) {
2040 if (scs_any) {
2041 trace_ds("Automatic SCS EOJ formfeed.\n");
2042 scs_formfeed(true);
2043 if (dump_scs_line(true, false) < 0) {
2044 rc = -1;
2045 }
2046 } else {
2047 trace_ds("Automatic 3270 %s EOJ formfeed.\n",
2048 wcc_line_length? "formatted": "unformatted");
2049 ctlr_add(0, FCORDER_FF, default_cs, default_gr);
2050 if (wcc_line_length) {
2051 if (dump_formatted() < 0) {
2052 rc = -1;
2053 }
2054 } else {
2055 if (dump_unformatted() < 0) {
2056 rc = -1;
2057 }
2058 }
2059 }
2060 ffeoj_last = true;
2061 }
2062
2063 /* Close the stream to the print process. */
2064 #if defined(_WIN32) /*[*/
2065 if (ws_initted) {
2066 trace_ds("End of print job.\n");
2067 if (options.trnpost != NULL && copyfile(options.trnpost) < 0) {
2068 rc = -1;
2069 }
2070 if (ws_endjob() < 0) {
2071 rc = -1;
2072 }
2073 ws_needpre = 1;
2074 }
2075 #else /*]*/
2076 if (prfile != NULL) {
2077 trace_ds("End of print job.\n");
2078 if (options.trnpost != NULL && copyfile(options.trnpost) < 0) {
2079 rc = -1;
2080 }
2081 rc = pclose_no_sigint(prfile);
2082 if (rc) {
2083 if (rc < 0) {
2084 errmsg("Close error on '%s': %s", options.command,
2085 strerror(errno));
2086 } else if (WIFEXITED(rc)) {
2087 errmsg("'%s' exited with status %d", options.command,
2088 WEXITSTATUS(rc));
2089 } else if (WIFSIGNALED(rc)) {
2090 errmsg("'%s' terminated by signal %d", options.command,
2091 WTERMSIG(rc));
2092 } else {
2093 errmsg("'%s' returned status %d", options.command, rc);
2094 }
2095 rc = -1;
2096 }
2097 prfile = NULL;
2098 }
2099 #endif /*]*/
2100
2101 /* Make sure the next 3270 job starts with clean conditions. */
2102 page_buf_initted = 0;
2103
2104 /* Reset the FF suprpession logic. */
2105 any_3270_printable = false;
2106
2107 return rc;
2108 }
2109
2110 void
print_unbind(void)2111 print_unbind(void)
2112 {
2113 /*
2114 * Make sure that the next SCS job starts with clean conditions.
2115 */
2116 scs_initted = false;
2117 }
2118
2119 static int
ctlr_erase(void)2120 ctlr_erase(void)
2121 {
2122 /* Dump whatever we've got so far. */
2123 /* Dump any pending 3270-mode output. */
2124 if (wcc_line_length) {
2125 if (dump_formatted() < 0) {
2126 return -1;
2127 }
2128 } else {
2129 if (dump_unformatted() < 0) {
2130 return -1;
2131 }
2132 }
2133
2134 /* Dump any pending SCS-mode output. */
2135 if (any_scs_output) {
2136 if (dump_scs_line(true, false) < 0) { /* XXX: 1st true? */
2137 return -1;
2138 }
2139 }
2140
2141 /* Make sure the buffer is clean. */
2142 memset(page_buf, '\0', MAX_BUF * sizeof(ucs4_t));
2143 any_3270_output = 0;
2144 baddr = 0;
2145 return 0;
2146 }
2147
2148 /*
2149 * Copy a -trnpre/-trnpost file to the printer. We open and read the file
2150 * for each print job, so someone can change their contents while we are
2151 * running (hopefully between print jobs).
2152 */
2153 static int
copyfile(const char * filename)2154 copyfile(const char *filename)
2155 {
2156 FILE *f;
2157 char c;
2158 int rc = 0;
2159
2160 if ((f = fopen(filename, "rb")) == NULL) {
2161 errmsg("%s: %s", filename, strerror(errno));
2162 return -1;
2163 }
2164 while ((c = fgetc(f)) != EOF) {
2165 trace_pdc((unsigned char)c);
2166 #if defined(_WIN32) /*[*/
2167 if (ws_putc(c) < 0) {
2168 #else /*][*/
2169 if (fputc(c, prfile) < 0) {
2170 errmsg("write(%s): %s", options.command, strerror(errno));
2171 #endif /*]*/
2172 rc = -1;
2173 break;
2174 }
2175 }
2176 fclose(f);
2177 return rc;
2178 }
2179