1 /*
2 * Copyright (c) 1994-2010, 2013-2015, 2018-2019 Paul Mattes.
3 * Copyright (c) 2004, Don Russell.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the names of Paul Mattes, Don Russell nor their
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY PAUL MATTES AND DON RUSSELL "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PAUL MATTES OR DON RUSSELL
22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
24 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /*
32 * sf.c
33 * This module handles 3270 structured fields.
34 *
35 */
36
37 #include "globals.h"
38 #include "3270ds.h"
39 #include "appres.h"
40 #include "ctlr.h"
41
42 #include "codepage.h"
43 #include "ctlrc.h"
44 #include "ft_dft.h"
45 #include "ft_private.h"
46 #include "kybd.h"
47 #include "sf.h" /* has to come before rpq.h */
48 #include "rpq.h"
49 #include "screen.h"
50 #include "see.h"
51 #include "telnet_core.h"
52 #include "trace.h"
53
54 #define SW_3279_2 0x09
55 #define SH_3279_2 0x0c
56 #define Xr_3279_2 0x000a02e5
57 #define Yr_3279_2 0x0002006f
58
59 /* Statics */
60 static bool qr_in_progress = false;
61 static enum pds sf_read_part(unsigned char buf[], unsigned buflen);
62 static enum pds sf_erase_reset(unsigned char buf[], int buflen);
63 static enum pds sf_set_reply_mode(unsigned char buf[], int buflen);
64 static enum pds sf_create_partition(unsigned char buf[], int buflen);
65 static enum pds sf_outbound_ds(unsigned char buf[], int buflen);
66 static void query_reply_start(void);
67 static void do_query_reply(unsigned char code);
68 static void query_reply_end(void);
69
70 typedef bool qr_multi_fn_t(unsigned *subindex, bool *more);
71
72 static qr_single_fn_t do_qr_summary, do_qr_usable_area, do_qr_alpha_part,
73 do_qr_charsets, do_qr_color, do_qr_highlighting, do_qr_reply_modes,
74 do_qr_imp_part, do_qr_null;
75 static qr_single_fn_t do_qr_dbcs_asia;
76 static qr_single_fn_t do_qr_ddm;
77
78 static struct reply {
79 unsigned char code;
80 qr_single_fn_t *single_fn;
81 qr_multi_fn_t *multi_fn;
82 } replies[] = {
83 { QR_SUMMARY, do_qr_summary, NULL }, /* 0x80 */
84 { QR_USABLE_AREA, do_qr_usable_area, NULL }, /* 0x81 */
85 { QR_ALPHA_PART, do_qr_alpha_part, NULL }, /* 0x84 */
86 { QR_CHARSETS, do_qr_charsets, NULL }, /* 0x85 */
87 { QR_COLOR, do_qr_color, NULL }, /* 0x86 */
88 { QR_HIGHLIGHTING, do_qr_highlighting, NULL }, /* 0x87 */
89 { QR_REPLY_MODES, do_qr_reply_modes, NULL }, /* 0x88 */
90 { QR_DBCS_ASIA, do_qr_dbcs_asia, NULL }, /* 0x91 */
91 { QR_DDM, do_qr_ddm, NULL }, /* 0x95 */
92 { QR_RPQNAMES, do_qr_rpqnames, NULL }, /* 0xa1 */
93 { QR_IMP_PART, do_qr_imp_part, NULL }, /* 0xa6 */
94
95 /* QR_NULL must be last in the table */
96 { QR_NULL, do_qr_null, NULL }, /* 0xff */
97 };
98
99 /*
100 * NSR_ALL is the number of query replies supported, including NULL.
101 * NSR is the number of query replies supported, except for NULL.
102 */
103 #define NSR_ALL (sizeof(replies)/sizeof(struct reply))
104 #define NSR (NSR_ALL - 1)
105
106 /*
107 * Process a 3270 Write Structured Field command
108 */
109 enum pds
write_structured_field(unsigned char buf[],size_t buflen)110 write_structured_field(unsigned char buf[], size_t buflen)
111 {
112 size_t fieldlen;
113 unsigned char *cp = buf;
114 bool first = true;
115 enum pds rv = PDS_OKAY_NO_OUTPUT;
116 enum pds rv_this = PDS_OKAY_NO_OUTPUT;
117 bool bad_cmd = false;
118
119 /* Skip the WSF command itself. */
120 cp++;
121 buflen--;
122
123 /* Interpret fields. */
124 while (buflen > 0) {
125
126 if (first)
127 trace_ds(" ");
128 else
129 trace_ds("< WriteStructuredField ");
130 first = false;
131
132 /* Pick out the field length. */
133 if (buflen < 2) {
134 trace_ds("error: single byte at end of message\n");
135 return rv ? rv : PDS_BAD_CMD;
136 }
137 fieldlen = (cp[0] << 8) + cp[1];
138 if (fieldlen == 0)
139 fieldlen = buflen;
140 if (fieldlen < 3) {
141 trace_ds("error: field length %d too small\n",
142 (int)fieldlen);
143 return rv ? rv : PDS_BAD_CMD;
144 }
145 if (fieldlen > buflen) {
146 trace_ds("error: field length %d exceeds remaining "
147 "message length %d\n",
148 (int)fieldlen, (int)buflen);
149 return rv ? rv : PDS_BAD_CMD;
150 }
151
152 /* Dispatch on the ID. */
153 switch (cp[2]) {
154 case SF_READ_PART:
155 trace_ds("ReadPartition");
156 rv_this = sf_read_part(cp, (int)fieldlen);
157 break;
158 case SF_ERASE_RESET:
159 trace_ds("EraseReset");
160 rv_this = sf_erase_reset(cp, (int)fieldlen);
161 break;
162 case SF_SET_REPLY_MODE:
163 trace_ds("SetReplyMode");
164 rv_this = sf_set_reply_mode(cp, (int)fieldlen);
165 break;
166 case SF_CREATE_PART:
167 trace_ds("CreatePartition");
168 rv_this = sf_create_partition(cp, (int)fieldlen);
169 break;
170 case SF_OUTBOUND_DS:
171 trace_ds("OutboundDS");
172 rv_this = sf_outbound_ds(cp, (int)fieldlen);
173 break;
174 case SF_TRANSFER_DATA: /* File transfer data */
175 trace_ds("FileTransferData");
176 ft_dft_data(cp, (int)fieldlen);
177 break;
178 default:
179 trace_ds("unsupported ID 0x%02x\n", cp[2]);
180 rv_this = PDS_BAD_CMD;
181 break;
182 }
183
184 /*
185 * Accumulate errors or output flags.
186 * One real ugliness here is that if we have already
187 * generated some output, then we have already positively
188 * acknowledged the request, so if we fail here, we have no
189 * way to return the error indication.
190 */
191 if (rv_this < 0)
192 bad_cmd = true;
193 else
194 rv |= rv_this;
195
196 /* Skip to the next field. */
197 cp += fieldlen;
198 buflen -= fieldlen;
199 }
200 if (first)
201 trace_ds(" (null)\n");
202
203 if (bad_cmd && !rv)
204 return PDS_BAD_CMD;
205 else
206 return rv;
207 }
208
209 static enum pds
sf_read_part(unsigned char buf[],unsigned buflen)210 sf_read_part(unsigned char buf[], unsigned buflen)
211 {
212 unsigned char partition;
213 unsigned i;
214 int any = 0;
215 const char *comma = "";
216
217 if (buflen < 5) {
218 trace_ds(" error: field length %d too small\n", buflen);
219 return PDS_BAD_CMD;
220 }
221
222 partition = buf[3];
223 trace_ds("(0x%02x)", partition);
224
225 switch (buf[4]) {
226 case SF_RP_QUERY:
227 trace_ds(" Query");
228 if (partition != 0xff) {
229 trace_ds(" error: illegal partition\n");
230 return PDS_BAD_CMD;
231 }
232 trace_ds("\n");
233 query_reply_start();
234 for (i = 0; i < NSR; i++) {
235 if (dbcs || replies[i].code != QR_DBCS_ASIA) {
236 do_query_reply(replies[i].code);
237 }
238 }
239 query_reply_end();
240 break;
241 case SF_RP_QLIST:
242 trace_ds(" QueryList ");
243 if (partition != 0xff) {
244 trace_ds("error: illegal partition\n");
245 return PDS_BAD_CMD;
246 }
247 if (buflen < 6) {
248 trace_ds("error: missing request type\n");
249 return PDS_BAD_CMD;
250 }
251 query_reply_start();
252 switch (buf[5]) {
253 case SF_RPQ_LIST:
254 trace_ds("List(");
255 if (buflen < 7) {
256 trace_ds(")\n");
257 do_query_reply(QR_NULL);
258 } else {
259 for (i = 6; i < buflen; i++) {
260 trace_ds("%s%s", comma,
261 see_qcode(buf[i]));
262 comma = ",";
263 }
264 trace_ds(")\n");
265 for (i = 0; i < NSR; i++) {
266 if (memchr((char *)&buf[6],
267 (char)replies[i].code,
268 buflen-6)
269 && (dbcs ||
270 replies[i].code != QR_DBCS_ASIA)
271 ) {
272 do_query_reply(replies[i].code);
273 any++;
274 }
275 }
276 if (!any) {
277 do_query_reply(QR_NULL);
278 }
279 }
280 break;
281 case SF_RPQ_EQUIV:
282 trace_ds("Equivlent+List(");
283 for (i = 6; i < buflen; i++) {
284 trace_ds("%s%s", comma, see_qcode(buf[i]));
285 comma = ",";
286 }
287 trace_ds(")\n");
288 for (i = 0; i < NSR; i++)
289 if (dbcs || replies[i].code != QR_DBCS_ASIA) {
290 do_query_reply(replies[i].code);
291 }
292 break;
293 case SF_RPQ_ALL:
294 trace_ds("All\n");
295 for (i = 0; i < NSR; i++)
296 if (dbcs || replies[i].code != QR_DBCS_ASIA) {
297 do_query_reply(replies[i].code);
298 }
299 break;
300 default:
301 trace_ds("unknown request type 0x%02x\n", buf[5]);
302 return PDS_BAD_CMD;
303 }
304 query_reply_end();
305 break;
306 case SNA_CMD_RMA:
307 trace_ds(" ReadModifiedAll");
308 if (partition != 0x00) {
309 trace_ds(" error: illegal partition\n");
310 return PDS_BAD_CMD;
311 }
312 trace_ds("\n");
313 ctlr_read_modified(AID_QREPLY, true);
314 break;
315 case SNA_CMD_RB:
316 trace_ds(" ReadBuffer");
317 if (partition != 0x00) {
318 trace_ds(" error: illegal partition\n");
319 return PDS_BAD_CMD;
320 }
321 trace_ds("\n");
322 ctlr_read_buffer(AID_QREPLY);
323 break;
324 case SNA_CMD_RM:
325 trace_ds(" ReadModified");
326 if (partition != 0x00) {
327 trace_ds(" error: illegal partition\n");
328 return PDS_BAD_CMD;
329 }
330 trace_ds("\n");
331 ctlr_read_modified(AID_QREPLY, false);
332 break;
333 default:
334 trace_ds(" unknown type 0x%02x\n", buf[4]);
335 return PDS_BAD_CMD;
336 }
337 return PDS_OKAY_OUTPUT;
338 }
339
340 static enum pds
sf_erase_reset(unsigned char buf[],int buflen)341 sf_erase_reset(unsigned char buf[], int buflen)
342 {
343 if (buflen != 4) {
344 trace_ds(" error: wrong field length %d\n", buflen);
345 return PDS_BAD_CMD;
346 }
347
348 switch (buf[3]) {
349 case SF_ER_DEFAULT:
350 trace_ds(" Default\n");
351 ctlr_erase(false);
352 break;
353 case SF_ER_ALT:
354 trace_ds(" Alternate\n");
355 ctlr_erase(true);
356 break;
357 default:
358 trace_ds(" unknown type 0x%02x\n", buf[3]);
359 return PDS_BAD_CMD;
360 }
361 return PDS_OKAY_NO_OUTPUT;
362 }
363
364 static enum pds
sf_set_reply_mode(unsigned char buf[],int buflen)365 sf_set_reply_mode(unsigned char buf[], int buflen)
366 {
367 unsigned char partition;
368 int i;
369 const char *comma = "(";
370
371 if (buflen < 5) {
372 trace_ds(" error: wrong field length %d\n", buflen);
373 return PDS_BAD_CMD;
374 }
375
376 partition = buf[3];
377 trace_ds("(0x%02x)", partition);
378 if (partition != 0x00) {
379 trace_ds(" error: illegal partition\n");
380 return PDS_BAD_CMD;
381 }
382
383 switch (buf[4]) {
384 case SF_SRM_FIELD:
385 trace_ds(" Field\n");
386 break;
387 case SF_SRM_XFIELD:
388 trace_ds(" ExtendedField\n");
389 break;
390 case SF_SRM_CHAR:
391 trace_ds(" Character");
392 break;
393 default:
394 trace_ds(" unknown mode 0x%02x\n", buf[4]);
395 return PDS_BAD_CMD;
396 }
397 reply_mode = buf[4];
398 if (buf[4] == SF_SRM_CHAR) {
399 crm_nattr = buflen - 5;
400 for (i = 5; i < buflen; i++) {
401 crm_attr[i - 5] = buf[i];
402 trace_ds("%s%s", comma, see_efa_only(buf[i]));
403 comma = ",";
404 }
405 trace_ds("%s\n", crm_nattr ? ")" : "");
406 }
407 return PDS_OKAY_NO_OUTPUT;
408 }
409
410 static enum pds
sf_create_partition(unsigned char buf[],int buflen)411 sf_create_partition(unsigned char buf[], int buflen)
412 {
413 unsigned char pid;
414 unsigned char uom; /* unit of measure */
415 unsigned char am; /* addressing mode */
416 unsigned char flags; /* flags */
417 unsigned short h; /* height of presentation space */
418 unsigned short w; /* width of presentation space */
419 unsigned short rv; /* viewport origin row */
420 unsigned short cv; /* viewport origin column */
421 unsigned short hv; /* viewport height */
422 unsigned short wv; /* viewport width */
423 unsigned short rw; /* window origin row */
424 unsigned short cw; /* window origin column */
425 unsigned short rs; /* scroll rows */
426 /* hole */
427 unsigned short pw; /* character cell point width */
428 unsigned short ph; /* character cell point height */
429
430 static const char *bit4[16] = {
431 "0000", "0001", "0010", "0011",
432 "0100", "0101", "0110", "0111",
433 "1000", "1001", "1010", "1011",
434 "1100", "1101", "1110", "1111"
435 };
436
437 if (buflen > 3) {
438 trace_ds("(");
439
440 /* Partition. */
441 pid = buf[3];
442 trace_ds("pid=0x%02x", pid);
443 if (pid != 0x00) {
444 trace_ds(") error: illegal partition\n");
445 return PDS_BAD_CMD;
446 }
447 } else
448 pid = 0x00;
449
450 if (buflen > 4) {
451 uom = (buf[4] & 0xf0) >> 4;
452 trace_ds(",uom=B'%s'", bit4[uom]);
453 if (uom != 0x0 && uom != 0x02) {
454 trace_ds(") error: illegal units\n");
455 return PDS_BAD_CMD;
456 }
457 am = buf[4] & 0x0f;
458 trace_ds(",am=B'%s'", bit4[am]);
459 if (am > 0x2) {
460 trace_ds(") error: illegal a-mode\n");
461 return PDS_BAD_CMD;
462 }
463 } else {
464 uom = 0;
465 am = 0;
466 }
467
468 if (buflen > 5) {
469 flags = buf[5];
470 trace_ds(",flags=0x%02x", flags);
471 } else {
472 flags = 0;
473 }
474
475 if (buflen > 7) {
476 GET16(h, &buf[6]);
477 trace_ds(",h=%d", h);
478 } else
479 h = maxROWS;
480
481 if (buflen > 9) {
482 GET16(w, &buf[8]);
483 trace_ds(",w=%d", w);
484 } else
485 w = maxCOLS;
486
487 if (buflen > 11) {
488 GET16(rv, &buf[10]);
489 trace_ds(",rv=%d", rv);
490 } else
491 rv = 0;
492
493 if (buflen > 13) {
494 GET16(cv, &buf[12]);
495 trace_ds(",cv=%d", cv);
496 } else
497 cv = 0;
498
499 if (buflen > 15) {
500 GET16(hv, &buf[14]);
501 trace_ds(",hv=%d", hv);
502 } else
503 hv = (h > maxROWS)? maxROWS: h;
504
505 if (buflen > 17) {
506 GET16(wv, &buf[16]);
507 trace_ds(",wv=%d", wv);
508 } else
509 wv = (w > maxCOLS)? maxCOLS: w;
510
511 if (buflen > 19) {
512 GET16(rw, &buf[18]);
513 trace_ds(",rw=%d", rw);
514 } else
515 rw = 0;
516
517 if (buflen > 21) {
518 GET16(cw, &buf[20]);
519 trace_ds(",cw=%d", cw);
520 } else
521 cw = 0;
522
523 if (buflen > 23) {
524 GET16(rs, &buf[22]);
525 trace_ds(",rs=%d", rs);
526 } else
527 rs = (h > hv)? 1: 0;
528
529 if (buflen > 27) {
530 GET16(pw, &buf[26]);
531 trace_ds(",pw=%d", pw);
532 } else
533 pw = *char_width;
534
535 if (buflen > 29) {
536 GET16(ph, &buf[28]);
537 trace_ds(",ph=%d", ph);
538 } else
539 ph = *char_height;
540 trace_ds(")\n");
541
542 cursor_move(0);
543 buffer_addr = 0;
544
545 return PDS_OKAY_NO_OUTPUT;
546 }
547
548 static enum pds
sf_outbound_ds(unsigned char buf[],int buflen)549 sf_outbound_ds(unsigned char buf[], int buflen)
550 {
551 enum pds rv;
552
553 if (buflen < 5) {
554 trace_ds(" error: field length %d too short\n", buflen);
555 return PDS_BAD_CMD;
556 }
557
558 trace_ds("(0x%02x)", buf[3]);
559 if (buf[3] != 0x00) {
560 trace_ds(" error: illegal partition 0x%0x\n", buf[3]);
561 return PDS_BAD_CMD;
562 }
563
564 switch (buf[4]) {
565 case SNA_CMD_W:
566 trace_ds(" Write");
567 if (buflen > 5) {
568 if ((rv = ctlr_write(&buf[4], buflen-4, false)) < 0)
569 return rv;
570 } else
571 trace_ds("\n");
572 break;
573 case SNA_CMD_EW:
574 trace_ds(" EraseWrite");
575 ctlr_erase(screen_alt);
576 if (buflen > 5) {
577 if ((rv = ctlr_write(&buf[4], buflen-4, true)) < 0)
578 return rv;
579 } else
580 trace_ds("\n");
581 break;
582 case SNA_CMD_EWA:
583 trace_ds(" EraseWriteAlternate");
584 ctlr_erase(screen_alt);
585 if (buflen > 5) {
586 if ((rv = ctlr_write(&buf[4], buflen-4, true)) < 0)
587 return rv;
588 } else
589 trace_ds("\n");
590 break;
591 case SNA_CMD_EAU:
592 trace_ds(" EraseAllUnprotected\n");
593 ctlr_erase_all_unprotected();
594 break;
595 default:
596 trace_ds(" unknown type 0x%02x\n", buf[4]);
597 return PDS_BAD_CMD;
598 }
599 return PDS_OKAY_NO_OUTPUT;
600 }
601
602 static void
query_reply_start(void)603 query_reply_start(void)
604 {
605 obptr = obuf;
606 space3270out(1);
607 *obptr++ = AID_SF;
608 qr_in_progress = true;
609 }
610
611 static void
do_query_reply(unsigned char code)612 do_query_reply(unsigned char code)
613 {
614 unsigned i;
615 unsigned subindex = 0;
616 bool more = false;
617
618 /* Find the right entry in the reply table. */
619 for (i = 0; i < NSR_ALL; i++) {
620 if (replies[i].code == code)
621 break;
622 }
623 if (i >= NSR_ALL ||
624 (replies[i].single_fn == NULL && replies[i].multi_fn == NULL))
625 return;
626
627 if (qr_in_progress) {
628 trace_ds("> StructuredField\n");
629 qr_in_progress = false;
630 }
631
632 do {
633 size_t obptr0 = obptr - obuf;
634 bool full = true;
635
636 space3270out(4);
637 obptr += 2; /* skip length for now */
638 *obptr++ = SFID_QREPLY;
639 *obptr++ = code;
640
641 more = false;
642 if (replies[i].single_fn)
643 replies[i].single_fn();
644 else
645 full = replies[i].multi_fn(&subindex, &more);
646
647 if (full) {
648 size_t len;
649 unsigned char *obptr_len;
650
651 /* Fill in the length. */
652 obptr_len = obuf + obptr0;
653 len = (obptr - obuf) - obptr0;
654 SET16(obptr_len, len);
655 } else {
656 /* Back over the header. */
657 obptr -= 4;
658 }
659 } while (more);
660 }
661
662 static void
do_qr_null(void)663 do_qr_null(void)
664 {
665 trace_ds("> QueryReply(Null)\n");
666 }
667
668 static void
do_qr_summary(void)669 do_qr_summary(void)
670 {
671 unsigned i;
672 const char *comma = "";
673
674 trace_ds("> QueryReply(Summary(");
675 space3270out(NSR);
676 for (i = 0; i < NSR; i++) {
677 if (dbcs || replies[i].code != QR_DBCS_ASIA) {
678 trace_ds("%s%s", comma, see_qcode(replies[i].code));
679 comma = ",";
680 *obptr++ = replies[i].code;
681 }
682 }
683 trace_ds("))\n");
684 }
685
686 static void
do_qr_usable_area(void)687 do_qr_usable_area(void)
688 {
689 trace_ds("> QueryReply(UsableArea)\n");
690 space3270out(19);
691 *obptr++ = 0x01; /* 12/14-bit addressing */
692 *obptr++ = 0x00; /* no special character features */
693 SET16(obptr, maxCOLS); /* usable width */
694 SET16(obptr, maxROWS); /* usable height */
695 *obptr++ = 0x01; /* units (mm) */
696 SET32(obptr, Xr_3279_2); /* Xr, canned from 3279-2 */
697 SET32(obptr, Yr_3279_2); /* Yr, canned from 3279-2 */
698
699 /*
700 * If we ever implement graphics, these will
701 * need to change.
702 */
703 *obptr++ = SW_3279_2; /* AW, canned from 3279-2 */
704 *obptr++ = SH_3279_2; /* AH, canned from 3279-2 */
705
706 SET16(obptr, maxCOLS*maxROWS); /* buffer, questionable */
707 }
708
709 static void
do_qr_color(void)710 do_qr_color(void)
711 {
712 int i;
713 int color_max = 16;
714
715 trace_ds("> QueryReply(Color)\n");
716
717 space3270out(4 + 2*15);
718 *obptr++ = 0x00; /* no options */
719 *obptr++ = color_max; /* 16 colors */
720 *obptr++ = 0x00; /* default color: */
721 *obptr++ = 0xf0 + HOST_COLOR_GREEN; /* green */
722 for (i = 0xf1; i < 0xf1 + color_max - 1; i++) {
723 *obptr++ = i;
724 if (mode.m3279) {
725 *obptr++ = i;
726 } else {
727 *obptr++ = 0x00;
728 }
729 }
730
731 if (screen_has_bg_color()) {
732 /* Add background color. */
733 if (mode.m3279 && appres.qr_bg_color) {
734 space3270out(4);
735 *obptr++ = 4; /* length */
736 *obptr++ = 0x02; /* background color */
737 *obptr++ = 0x00; /* attribute */
738 *obptr++ = 0xf0; /* default color */
739 }
740 }
741 }
742
743 static void
do_qr_highlighting(void)744 do_qr_highlighting(void)
745 {
746 trace_ds("> QueryReply(Highlighting)\n");
747 space3270out(11);
748 *obptr++ = 5; /* report on 5 pairs */
749 *obptr++ = XAH_DEFAULT; /* default: */
750 *obptr++ = XAH_NORMAL; /* normal */
751 *obptr++ = XAH_BLINK; /* blink: */
752 *obptr++ = XAH_BLINK; /* blink */
753 *obptr++ = XAH_REVERSE; /* reverse: */
754 *obptr++ = XAH_REVERSE; /* reverse */
755 *obptr++ = XAH_UNDERSCORE; /* underscore: */
756 *obptr++ = XAH_UNDERSCORE; /* underscore */
757 *obptr++ = XAH_INTENSIFY; /* intensify: */
758 *obptr++ = XAH_INTENSIFY; /* intensify */
759 }
760
761 static void
do_qr_reply_modes(void)762 do_qr_reply_modes(void)
763 {
764 trace_ds("> QueryReply(ReplyModes)\n");
765 space3270out(3);
766 *obptr++ = SF_SRM_FIELD;
767 *obptr++ = SF_SRM_XFIELD;
768 *obptr++ = SF_SRM_CHAR;
769 }
770
771 static void
do_qr_dbcs_asia(void)772 do_qr_dbcs_asia(void)
773 {
774 /* XXX: Should we support this, even when not in DBCS mode? */
775 trace_ds("> QueryReply(DbcsAsia)\n");
776 space3270out(7);
777 *obptr++ = 0x00; /* flags (none) */
778 *obptr++ = 0x03; /* field length 3 */
779 *obptr++ = 0x01; /* SI/SO supported */
780 *obptr++ = 0x80; /* character set ID 0x80 */
781 *obptr++ = 0x03; /* field length 3 */
782 *obptr++ = 0x02; /* input control */
783 *obptr++ = 0x01; /* creation supported */
784 }
785
786 static void
do_qr_alpha_part(void)787 do_qr_alpha_part(void)
788 {
789 trace_ds("> QueryReply(AlphanumericPartitions)\n");
790 space3270out(4);
791 *obptr++ = 0; /* 1 partition */
792 SET16(obptr, maxROWS*maxCOLS); /* buffer space */
793 *obptr++ = 0; /* no special features */
794 }
795
796 static void
do_qr_charsets(void)797 do_qr_charsets(void)
798 {
799 trace_ds("> QueryReply(CharacterSets)\n");
800 space3270out(64);
801 if (dbcs) {
802 *obptr++ = 0x8e; /* flags: GE, CGCSGID, DBCS */
803 } else {
804 *obptr++ = 0x82; /* flags: GE, CGCSGID present */
805 }
806 *obptr++ = 0x00; /* more flags */
807 *obptr++ = SW_3279_2; /* SDW, canned from 3279-2 */
808 *obptr++ = SH_3279_2; /* SDW, canned from 3279-2 */
809 *obptr++ = 0x00; /* no load PS */
810 *obptr++ = 0x00;
811 *obptr++ = 0x00;
812 *obptr++ = 0x00;
813 if (dbcs) {
814 *obptr++ = 0x0b; /* DL (11 bytes) */
815 } else {
816 *obptr++ = 0x07; /* DL (7 bytes) */
817 }
818
819 *obptr++ = 0x00; /* SET 0: */
820 if (dbcs) {
821 *obptr++ = 0x00; /* FLAGS: non-load, single-
822 plane, single-byte */
823 } else {
824 *obptr++ = 0x10; /* FLAGS: non-loadable,
825 single-plane, single-byte,
826 no compare */
827 }
828 *obptr++ = 0x00; /* LCID 0 */
829 if (dbcs) {
830 *obptr++ = 0x00; /* SW 0 */
831 *obptr++ = 0x00; /* SH 0 */
832 *obptr++ = 0x00; /* SUBSN */
833 *obptr++ = 0x00; /* SUBSN */
834 }
835 SET32(obptr, cgcsgid); /* CGCSGID */
836
837 /* special 3270 font, includes APL */
838 *obptr++ = 0x01;/* SET 1: */
839 *obptr++ = 0x00; /* FLAGS: non-loadable, single-plane,
840 single-byte, no compare */
841 *obptr++ = 0xf1; /* LCID */
842 if (dbcs) {
843 *obptr++ = 0x00; /* SW 0 */
844 *obptr++ = 0x00; /* SH 0 */
845 *obptr++ = 0x00; /* SUBSN */
846 *obptr++ = 0x00; /* SUBSN */
847 }
848 *obptr++ = 0x03; /* CGCSGID: 3179-style APL2 */
849 *obptr++ = 0xc3;
850 *obptr++ = 0x01;
851 *obptr++ = 0x36;
852 if (dbcs) {
853 *obptr++ = 0x80; /* SET 0x80: */
854 *obptr++ = 0x20; /* FLAGS: DBCS */
855 *obptr++ = 0xf8; /* LCID: 0xf8 */
856 *obptr++ = SW_3279_2 * 2; /* SW, canned from 3279-2 */
857 *obptr++ = SH_3279_2; /* SH, canned from 3279-2 */
858 *obptr++ = 0x41; /* SUBSN */
859 *obptr++ = 0x7f; /* SUBSN */
860 SET32(obptr, cgcsgid_dbcs); /* CGCSGID */
861 }
862 }
863
864 static void
do_qr_ddm(void)865 do_qr_ddm(void)
866 {
867 int size;
868
869 if (ftc != NULL) {
870 size = ftc->dft_buffersize;
871 } else {
872 size = set_dft_buffersize(0);
873 }
874
875 trace_ds("> QueryReply(DistributedDataManagement INLIM/OUTLIM=%d)\n",
876 size);
877 space3270out(8);
878 SET16(obptr,0); /* set reserved field to 0 */
879 SET16(obptr, size); /* set inbound length limit INLIM */
880 SET16(obptr, size); /* set outbound length limit OUTLIM */
881 SET16(obptr, 0x0101); /* NSS=01, DDMSS=01 */
882 }
883
884 static void
do_qr_imp_part(void)885 do_qr_imp_part(void)
886 {
887 trace_ds("> QueryReply(ImplicitPartition)\n");
888 space3270out(13);
889 *obptr++ = 0x0; /* reserved */
890 *obptr++ = 0x0;
891 *obptr++ = 0x0b; /* length of display size */
892 *obptr++ = 0x01; /* "implicit partition size" */
893 *obptr++ = 0x00; /* reserved */
894 SET16(obptr, 80); /* implicit partition width */
895 SET16(obptr, 24); /* implicit partition height */
896 SET16(obptr, maxCOLS); /* alternate height */
897 SET16(obptr, maxROWS); /* alternate width */
898 }
899
900 static void
query_reply_end(void)901 query_reply_end(void)
902 {
903 net_output();
904 kybd_inhibit(true);
905 }
906