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