1 /* $Id: main.c,v 1.110 2018/07/26 00:26:52 tom Exp $ */
2
3 /*
4 VTTEST.C
5
6 Written November 1983 - July 1984 by Per Lindberg,
7 Stockholm University Computer Center (QZ), Sweden.
8
9 THE MAD PROGRAMMER STRIKES AGAIN!
10
11 Copyright (c) 1984, Per Lindberg
12 All rights reserved.
13
14 Redistribution and use in source and binary forms, with or without
15 modification, are permitted provided that the following conditions are
16 met:
17 1. Redistributions of source code must retain the above copyright
18 notice, this list of conditions and the following disclaimer.
19 2. Redistributions in binary form must reproduce the above copyright
20 notice, this list of conditions and the following disclaimer in
21 the documentation and/or other materials provided with the
22 distribution.
23 3. Neither the name of Per Lindberg nor the names of contributors may
24 be used to endorse or promote products derived from this software
25 without specific prior written permission.
26
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40 #include <vttest.h>
41 #include <ttymodes.h>
42 #include <esc.h>
43
44 /* *INDENT-EQLS* */
45 FILE *log_fp = 0;
46 int brkrd;
47 int reading;
48 int log_disabled = FALSE;
49 int max_lines = 24;
50 int max_cols = 132;
51 int min_cols = 80;
52 int input_8bits = FALSE;
53 int output_8bits = FALSE;
54 int slow_motion = FALSE;
55 int tty_speed = DEFAULT_SPEED; /* nominal speed, for padding */
56 int use_padding = FALSE;
57 jmp_buf intrenv;
58
59 static char empty[1];
60 static char *current_menu = empty;
61
62 static void
usage(void)63 usage(void)
64 {
65 fprintf(stderr,
66 "Usage: vttest [-l] [-p] [-s] [-8] [-f font] [24x80.132]\n");
67 exit(EXIT_FAILURE);
68 }
69
70 int
main(int argc,char * argv[])71 main(int argc, char *argv[])
72 {
73 /* *INDENT-OFF* */
74 static MENU mainmenu[] = {
75 { "Exit", 0 },
76 { "Test of cursor movements", tst_movements },
77 { "Test of screen features", tst_screen },
78 { "Test of character sets", tst_characters },
79 { "Test of double-sized characters", tst_doublesize },
80 { "Test of keyboard", tst_keyboard },
81 { "Test of terminal reports", tst_reports },
82 { "Test of VT52 mode", tst_vt52 },
83 { "Test of VT102 features (Insert/Delete Char/Line)", tst_insdel },
84 { "Test of known bugs", tst_bugs },
85 { "Test of reset and self-test", tst_rst },
86 { "Test non-VT100 (e.g., VT220, XTERM) terminals", tst_nonvt100 },
87 { "Modify test-parameters", tst_setup },
88 { "", 0 }
89 };
90 /* *INDENT-ON* */
91
92 while (argc-- > 1) {
93 const char *opt = *++argv;
94 if (*opt == '-') {
95 while (*++opt != '\0') {
96 switch (*opt) {
97 case 'f':
98 if (!*++opt) {
99 if (argc-- < 1)
100 usage();
101 opt = *++argv;
102 }
103 setup_softchars(opt);
104 opt = "?";
105 break;
106 case 'l':
107 enable_logging();
108 break;
109 case 'p':
110 use_padding = TRUE;
111 break;
112 case 's':
113 slow_motion = TRUE;
114 break;
115 case '8':
116 output_8bits = TRUE;
117 break;
118 default:
119 usage();
120 }
121 }
122 } else {
123 /*
124 * Allow user to specify geometry of terminal to accommodate quasi-VT100
125 * terminals such as Linux console and xterm.
126 */
127 char *p = argv[0];
128 char *q;
129 int values[3], n;
130
131 for (n = 0; n < 3; n++) {
132 int m;
133
134 if (!*p)
135 break;
136 if ((m = (int) strtol(p, &q, 10)) > 0) {
137 values[n] = m;
138 p = q;
139 if (*p)
140 p++;
141 } else {
142 break;
143 }
144 }
145 switch (n) {
146 case 3:
147 max_cols = values[2];
148 /* FALLTHRU */
149 case 2:
150 min_cols = values[1];
151 /* FALLTHRU */
152 case 1:
153 max_lines = values[0];
154 break;
155 }
156 if ((max_cols < min_cols) || (n == 0)) {
157 usage();
158 }
159 }
160 }
161
162 #ifdef UNIX
163 initterminal(setjmp(intrenv));
164 signal(SIGINT, onbrk);
165 signal(SIGTERM, onterm);
166 reading = FALSE;
167 #else
168 initterminal(0);
169 #endif
170 do {
171 vt_clear(2);
172 __(title(0), printf("VT100 test program, version %d.%d", RELEASE, PATCHLEVEL));
173 #ifdef PATCH_DATE
174 if (PATCH_DATE)
175 printf(" (%d)", PATCH_DATE);
176 #endif
177
178 title(1);
179 if (max_lines != 24
180 || min_cols != 80
181 || max_cols != 132)
182 printf("Screen size %dx%d (%d max) ", max_lines, min_cols, max_cols);
183 if (tty_speed != DEFAULT_SPEED)
184 printf("Line speed %dbd ", tty_speed);
185 if (use_padding)
186 printf(" (padded)");
187
188 __(title(2), println("Choose test type:"));
189 } while (menu(mainmenu));
190 bye();
191 return EXIT_SUCCESS;
192 }
193
194 int
tst_movements(MENU_ARGS)195 tst_movements(MENU_ARGS)
196 {
197 /* Test of:
198 CUF (Cursor Forward)
199 CUB (Cursor Backward)
200 CUD (Cursor Down) IND (Index) NEL (Next Line)
201 CUU (Cursor Up) RI (Reverse Index)
202 CUP (Cursor Position) HVP (Horizontal and Vertical Position)
203 ED (Erase in Display)
204 EL (Erase in Line)
205 DECALN (Screen Alignment Display)
206 DECAWM (Autowrap)
207 <CR> <BS>
208 Cursor control characters inside CSI sequences
209 */
210
211 int i, row, col, pass, width;
212 const char *ctext = "This is a correct sentence";
213
214 set_tty_crmod(TRUE); /* want to disable tab/space conversion */
215
216 for (pass = 0; pass <= 1; pass++) {
217 int hlfxtra;
218 int inner_l, inner_r;
219
220 if (pass == 0) {
221 deccolm(FALSE);
222 width = min_cols;
223 } else {
224 deccolm(TRUE);
225 width = max_cols;
226 }
227
228 /* Compute left/right columns for a 60-column box centered in 'width' */
229 inner_l = (width - 60) / 2;
230 inner_r = 61 + inner_l;
231 hlfxtra = (width - 80) / 2;
232
233 if (LOG_ENABLED)
234 fprintf(log_fp, "tst_movements box(%d cols)\n", pass ? max_cols : min_cols);
235
236 decaln();
237 cup(9, inner_l);
238 ed(1);
239 cup(18, 60 + hlfxtra);
240 ed(0);
241 el(1);
242 cup(9, inner_r);
243 el(0);
244 /* 132: 36..97 */
245 /* 80: 10..71 */
246 for (row = 10; row <= 16; row++) {
247 cup(row, inner_l);
248 el(1);
249 cup(row, inner_r);
250 el(0);
251 }
252 cup(17, 30);
253 el(2);
254 for (col = 1; col <= width; col++) {
255 hvp(max_lines, col);
256 printf("*");
257 hvp(1, col);
258 printf("*");
259 }
260 cup(2, 2);
261 for (row = 2; row <= max_lines - 1; row++) {
262 printf("+");
263 cub(1);
264 ind();
265 }
266 cup(max_lines - 1, width - 1);
267 for (row = max_lines - 1; row >= 2; row--) {
268 printf("+");
269 cub(1);
270 ri();
271 }
272 cup(2, 1);
273 for (row = 2; row <= max_lines - 1; row++) {
274 printf("*");
275 cup(row, width);
276 printf("*");
277 cub(10);
278 if (row < 10)
279 nel();
280 else
281 printf("\n");
282 }
283 cup(2, 10);
284 cub(42 + hlfxtra);
285 cuf(2);
286 for (col = 3; col <= width - 2; col++) {
287 printf("+");
288 cuf(0);
289 cub(2);
290 cuf(1);
291 }
292 cup(max_lines - 1, inner_r - 1);
293 cuf(42 + hlfxtra);
294 cub(2);
295 for (col = width - 2; col >= 3; col--) {
296 printf("+");
297 cub(1);
298 cuf(1);
299 cub(0);
300 printf("%c", 8);
301 }
302 cup(1, 1);
303 cuu(10);
304 cuu(1);
305 cuu(0);
306 cup(max_lines, width);
307 cud(10);
308 cud(1);
309 cud(0);
310
311 cup(10, 2 + inner_l);
312 for (row = 10; row <= 15; row++) {
313 for (col = 2 + inner_l; col <= inner_r - 2; col++)
314 printf(" ");
315 cud(1);
316 cub(58);
317 }
318 cuu(5);
319 cuf(1);
320 printf("The screen should be cleared, and have an unbroken bor-");
321 cup(12, inner_l + 3);
322 printf("der of *'s and +'s around the edge, and exactly in the");
323 cup(13, inner_l + 3);
324 printf("middle there should be a frame of E's around this text");
325 cup(14, inner_l + 3);
326 printf("with one (1) free position around it. ");
327 holdit();
328 }
329 deccolm(FALSE);
330
331 /* DECAWM demo */
332 for (pass = 0; pass <= 1; pass++) {
333 static char on_left[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
334 static char on_right[] = "abcdefghijklmnopqrstuvwxyz";
335 int height = sizeof(on_left) - 1;
336 int region = max_lines - 6;
337
338 if (LOG_ENABLED)
339 fprintf(log_fp, "tst_movements wrap(%d cols)\n", pass ? max_cols : min_cols);
340
341 /* note: DECCOLM clears the screen */
342 if (pass == 0) {
343 deccolm(FALSE);
344 width = min_cols;
345 } else {
346 deccolm(TRUE);
347 width = max_cols;
348 }
349
350 println("Test of autowrap, mixing control and print characters.");
351 println("The left/right margins should have letters in order:");
352
353 decstbm(3, region + 3);
354 decom(TRUE); /* this also homes the cursor */
355 for (i = 0; i < height; ++i) {
356 switch (i % 4) {
357 case 0:
358 /* draw characters as-is, for reference */
359 __(cup(region + 1, 1), printf("%c", on_left[i]));
360 __(cup(region + 1, width), printf("%c", on_right[i]));
361 printf("\n");
362 break;
363 case 1:
364 /* simple wrapping */
365 __(cup(region, width), printf("%c%c", on_right[i - 1], on_left[i]));
366 /* backspace at right margin */
367 __(cup(region + 1, width), printf("%c%c %c",
368 on_left[i], BS, on_right[i]));
369 printf("\n");
370 break;
371 case 2:
372 /* tab to right margin */
373 __(cup(region + 1, width), printf("%c%c%c%c%c%c",
374 on_left[i], BS, BS,
375 TAB, TAB, on_right[i]));
376 __(cup(region + 1, 2), printf("%c%c\n", BS, on_left[i]));
377 break;
378 default:
379 /* newline at right margin */
380 __(cup(region + 1, width), printf("\n"));
381 __(cup(region, 1), printf("%c", on_left[i]));
382 __(cup(region, width), printf("%c", on_right[i]));
383 break;
384 }
385 }
386 decom(FALSE);
387 decstbm(0, 0);
388 cup(max_lines - 2, 1);
389 holdit();
390 }
391 deccolm(FALSE); /* 80 cols */
392
393 if (LOG_ENABLED)
394 fprintf(log_fp, "tst_movements cursor-controls in ESC sequences\n");
395
396 vt_clear(2);
397 vt_move(1, 1);
398 println("Test of cursor-control characters inside ESC sequences.");
399 println("Below should be four identical lines:");
400 println("");
401 println("A B C D E F G H I");
402 for (i = 1; i < 10; i++) {
403 printf("%c", '@' + i);
404 do_csi("2%cC", BS); /* Two forward, one backspace */
405 }
406 println("");
407 /* Now put CR in CUF sequence. */
408 printf("A ");
409 for (i = 2; i < 10; i++)
410 printf("%s%c%dC%c", csi_output(), CR, 2 * i - 2, '@' + i);
411 println("");
412 /* Now put VT in CUU sequence. */
413 rm("20");
414 for (i = 1; i < 10; i++) {
415 printf("%c ", '@' + i);
416 do_csi("1\013A");
417 }
418 println("");
419 println("");
420 holdit();
421
422 if (LOG_ENABLED)
423 fprintf(log_fp, "tst_movements leading zeros in ESC sequences\n");
424
425 vt_clear(2);
426 vt_move(1, 1);
427 println("Test of leading zeros in ESC sequences.");
428 printf("Two lines below you should see the sentence \"%s\".", ctext);
429 for (col = 1; *ctext; col++)
430 printf("%s00000000004;00000000%dH%c", csi_output(), col, *ctext++);
431 cup(20, 1);
432
433 restore_ttymodes();
434 return MENU_HOLD;
435 }
436
437 /* Scrolling test (used also in color-testing) */
438 void
do_scrolling(void)439 do_scrolling(void)
440 {
441 int soft, first, last, down, i, pass;
442
443 ed(2);
444 decom(TRUE); /* Origin mode (relative) */
445 for (soft = -1; soft <= 0; soft++) {
446 if (soft)
447 decsclm(TRUE);
448 else
449 decsclm(FALSE);
450 for (pass = 0; pass < 2; ++pass) {
451 if (pass == 0) {
452 first = max_lines / 2;
453 last = first + 1;
454 } else {
455 first = 1;
456 last = max_lines;
457 }
458 decstbm(first, last);
459 ed(2);
460 for (down = 0; down >= -1; down--) {
461 if (down)
462 cuu(max_lines);
463 else
464 cud(max_lines);
465 for (i = 1; i <= max_lines + 5; i++) {
466 printf("%s scroll %s region [%d..%d] size %d Line %d\n",
467 soft ? "Soft" : "Jump",
468 down ? "down" : "up",
469 first, last, last - first + 1, i);
470 if (down) {
471 ri();
472 ri();
473 } else if (soft)
474 extra_padding(10);
475 }
476 }
477 holdit();
478 }
479 }
480 }
481
482 int
tst_screen(MENU_ARGS)483 tst_screen(MENU_ARGS)
484 {
485 /* Test of:
486 - DECSTBM (Set Top and Bottom Margins)
487 - TBC (Tabulation Clear)
488 - HTS (Horizontal Tabulation Set)
489 - SM RM (Set/Reset mode): - 80/132 chars
490 . - Origin: Relative/absolute
491 . - Scroll: Smooth/jump
492 . - Wraparound
493 - SGR (Select Graphic Rendition)
494 - SM RM (Set/Reset Mode) - Inverse
495 - DECSC (Save Cursor)
496 - DECRC (Restore Cursor)
497 */
498
499 int i, j, cset, row, col, background;
500
501 static const char *tststr = "*qx`";
502 static const char *attr[5] =
503 {
504 ";0", ";1", ";4", ";5", ";7"
505 };
506
507 set_tty_crmod(TRUE); /* want to disable tab/space conversion */
508
509 cup(1, 1);
510 decawm(TRUE); /* DECAWM: Wrap Around ON */
511 for (col = 1; col <= min_cols * 2; col++)
512 printf("*");
513 decawm(FALSE); /* DECAWM: Wrap Around OFF */
514 cup(3, 1);
515 for (col = 1; col <= min_cols * 2; col++)
516 printf("*");
517 decawm(TRUE); /* DECAWM: Wrap Around ON */
518 cup(5, 1);
519 println("This should be three identical lines of *'s completely filling");
520 println("the top of the screen without any empty lines between.");
521 println("(Test of WRAP AROUND mode setting.)");
522 holdit();
523
524 ed(2);
525 tbc(3);
526 cup(1, 1);
527 for (col = 1; col <= min_cols - 2; col += 3) {
528 cuf(3);
529 hts();
530 }
531 cup(1, 4);
532 for (col = 4; col <= min_cols - 2; col += 6) {
533 tbc(0);
534 cuf(6);
535 }
536 cup(1, 7);
537 tbc(1);
538 tbc(2); /* no-op */
539 cup(1, 1);
540 for (col = 1; col <= min_cols - 2; col += 6)
541 printf("%c*", TAB);
542 cup(2, 2);
543 for (col = 2; col <= min_cols - 2; col += 6)
544 printf(" *");
545 cup(4, 1);
546 println("Test of TAB setting/resetting. These two lines");
547 printf("should look the same. ");
548 holdit();
549 for (background = 0; background <= 1; background++) {
550 if (background)
551 decscnm(FALSE);
552 else
553 decscnm(TRUE);
554 deccolm(TRUE); /* 132 cols */
555 ed(2); /* VT100 clears screen on SM3/RM3, but not obviously, so... */
556 cup(1, 1);
557 tbc(3);
558 for (col = 1; col <= max_cols; col += TABWIDTH) {
559 cuf(TABWIDTH);
560 hts();
561 }
562 cup(1, 1);
563 for (col = 1; col <= max_cols; col += 10)
564 printf("%.*s", (max_cols > col) ? (max_cols - col) : 10, "1234567890");
565 for (row = 3; row <= 20; row++) {
566 cup(row, row);
567 printf("This is %d column mode, %s background.", max_cols,
568 background ? "dark" : "light");
569 }
570 holdit();
571 deccolm(FALSE); /* 80 cols */
572 ed(2); /* VT100 clears screen on SM3/RM3, but not obviously, so... */
573 cup(1, 1);
574 for (col = 1; col <= min_cols; col += 10)
575 printf("%.*s", (min_cols > col) ? (min_cols - col) : 10, "1234567890");
576 for (row = 3; row <= 20; row++) {
577 cup(row, row);
578 printf("This is %d column mode, %s background.", min_cols,
579 background ? "dark" : "light");
580 }
581 holdit();
582 }
583 do_scrolling();
584 ed(2);
585 decstbm(max_lines - 1, max_lines);
586 printf(
587 "\nOrigin mode test. This line should be at the bottom of the screen.");
588 cup(1, 1);
589 printf("%s",
590 "This line should be the one above the bottom of the screen. ");
591 holdit();
592 ed(2);
593 decom(FALSE); /* Origin mode (absolute) */
594 cup(max_lines, 1);
595 printf(
596 "Origin mode test. This line should be at the bottom of the screen.");
597 cup(1, 1);
598 printf("%s", "This line should be at the top of the screen. ");
599 holdit();
600 decstbm(1, max_lines);
601
602 ed(2);
603 /* *INDENT-OFF* */
604 cup( 1,20); printf("Graphic rendition test pattern:");
605 cup( 4, 1); sgr("0"); printf("vanilla");
606 cup( 4,40); sgr("0;1"); printf("bold");
607 cup( 6, 6); sgr(";4"); printf("underline");
608 cup( 6,45);sgr(";1");sgr("4");printf("bold underline");
609 cup( 8, 1); sgr("0;5"); printf("blink");
610 cup( 8,40); sgr("0;5;1"); printf("bold blink");
611 cup(10, 6); sgr("0;4;5"); printf("underline blink");
612 cup(10,45); sgr("0;1;4;5"); printf("bold underline blink");
613 cup(12, 1); sgr("1;4;5;0;7"); printf("negative");
614 cup(12,40); sgr("0;1;7"); printf("bold negative");
615 cup(14, 6); sgr("0;4;7"); printf("underline negative");
616 cup(14,45); sgr("0;1;4;7"); printf("bold underline negative");
617 cup(16, 1); sgr("1;4;;5;7"); printf("blink negative");
618 cup(16,40); sgr("0;1;5;7"); printf("bold blink negative");
619 cup(18, 6); sgr("0;4;5;7"); printf("underline blink negative");
620 cup(18,45); sgr("0;1;4;5;7"); printf("bold underline blink negative");
621 /* *INDENT-ON* */
622
623 sgr("");
624
625 decscnm(FALSE); /* Inverse video off */
626 cup(max_lines - 1, 1);
627 el(0);
628 printf("Dark background. ");
629 holdit();
630
631 decscnm(TRUE); /* Inverse video */
632 cup(max_lines - 1, 1);
633 el(0);
634 printf("Light background. ");
635 holdit();
636
637 decscnm(FALSE);
638
639 ed(2);
640 /* *INDENT-OFF* */
641 cup(8,12); printf("normal");
642 cup(8,24); printf("bold");
643 cup(8,36); printf("underscored");
644 cup(8,48); printf("blinking");
645 cup(8,60); printf("reversed");
646 cup(10,1); printf("stars:");
647 cup(12,1); printf("line:");
648 cup(14,1); printf("x'es:");
649 cup(16,1); printf("diamonds:");
650 /* *INDENT-ON* */
651
652 for (cset = 0; cset <= 3; cset++) {
653 for (i = 0; i <= 4; i++) {
654 cup(10 + 2 * cset, 12 + 12 * i);
655 sgr(attr[i]);
656 if (cset == 0 || cset == 2)
657 scs_normal();
658 else
659 scs_graphics();
660 for (j = 0; j <= 4; j++) {
661 printf("%c", tststr[cset]);
662 }
663 decsc();
664 cup(cset + 1, i + 1);
665 sgr("");
666 scs_normal();
667 printf("A");
668 decrc();
669 for (j = 0; j <= 4; j++) {
670 printf("%c", tststr[cset]);
671 }
672 }
673 }
674
675 sgr("0");
676 scs_normal();
677 cup(21, 1);
678 println("Test of the SAVE/RESTORE CURSOR feature. There should");
679 println("be ten characters of each flavour, and a rectangle");
680 println("of 5 x 4 A's filling the top left of the screen.");
681
682 restore_ttymodes();
683 return MENU_HOLD;
684 }
685
686 int
tst_doublesize(MENU_ARGS)687 tst_doublesize(MENU_ARGS)
688 {
689 /* Test of:
690 DECSWL (Single Width Line)
691 DECDWL (Double Width Line)
692 DECDHL (Double Height Line) (also implicit double width)
693 */
694
695 int col, i, w;
696
697 /* Print the test pattern in both 80 and 132 character width */
698
699 for (w = 0; w <= 1; w++) {
700 int w1 = 13 * w;
701
702 ed(2);
703 cup(1, 1);
704 if (w) {
705 deccolm(TRUE);
706 printf("%3d column mode", max_cols);
707 } else {
708 deccolm(FALSE);
709 printf("%3d column mode", min_cols);
710 }
711
712 cup(5, 3 + 2 * w1);
713 printf("v------- left margin");
714
715 cup(7, 3 + 2 * w1);
716 printf("This is a normal-sized line");
717
718 decdhl(0);
719 decdhl(1);
720 decdwl();
721 decswl();
722 cup(9, 2 + w1);
723 printf("This is a Double-width line");
724
725 decswl();
726 decdhl(0);
727 decdhl(1);
728 decdwl();
729
730 cup(11, 2 + w1);
731 decdwl();
732 decswl();
733 decdhl(1);
734 decdhl(0);
735 printf("This is a Double-width-and-height line");
736
737 cup(12, 2 + w1);
738 decdwl();
739 decswl();
740 decdhl(0);
741 decdhl(1);
742 printf("This is a Double-width-and-height line");
743
744 cup(14, 2 + w1);
745 decdwl();
746 decswl();
747 decdhl(1);
748 decdhl(0);
749 el(2);
750 printf("This is another such line");
751
752 cup(15, 2 + w1);
753 decdwl();
754 decswl();
755 decdhl(0);
756 decdhl(1);
757 printf("This is another such line");
758
759 cup(17, 3 + 2 * w1);
760 printf("^------- left margin");
761
762 cup(21, 1);
763 printf("This is not a double-width line");
764 for (i = 0; i <= 1; i++) {
765 cup(21, 6);
766 if (i) {
767 printf("**is**");
768 decdwl();
769 } else {
770 printf("is not");
771 decswl();
772 }
773 cup(max_lines - 1, 1);
774 holdit();
775 }
776 }
777 /* Set vanilla tabs for next test */
778 cup(1, 1);
779 tbc(3);
780 for (col = 1; col <= max_cols; col += TABWIDTH) {
781 cuf(TABWIDTH);
782 hts();
783 }
784 deccolm(FALSE);
785 ed(2);
786 /* *INDENT-OFF* */
787 scs_graphics();
788 cup( 8,1); decdhl(0); printf("lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk");
789 cup( 9,1); decdhl(1); printf("lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk");
790 cup(10,1); decdhl(0); printf("x%c%c%c%c%cx",9,9,9,9,9);
791 cup(11,1); decdhl(1); printf("x%c%c%c%c%cx",9,9,9,9,9);
792 cup(12,1); decdhl(0); printf("x%c%c%c%c%cx",9,9,9,9,9);
793 cup(13,1); decdhl(1); printf("x%c%c%c%c%cx",9,9,9,9,9);
794 scs(1, '0'); /* should look the same as scs_graphics() */
795 cup(14,1); decdhl(0); printf("x x");
796 cup(15,1); decdhl(1); printf("x x");
797 cup(16,1); decdhl(0); printf("mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj");
798 cup(17,1); decdhl(1); printf("mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj");
799 scs_normal();
800 /* *INDENT-ON* */
801
802 sgr("1;5");
803 cup(12, 3);
804 printf("* The mad programmer strikes again * ");
805
806 cup(13, 3);
807 printf("%c", 9);
808
809 cub(6);
810 printf("* The mad programmer strikes again *");
811 sgr("0");
812
813 cup(max_lines - 2, 1);
814 println("Another test pattern... a frame with blinking bold text,");
815 printf("all in double-height double-width size. ");
816 holdit();
817
818 decstbm(8, max_lines); /* Absolute origin mode, so cursor is set at (1,1) */
819 cup(8, 1);
820 for (i = 1; i <= 12; i++)
821 ri();
822 decstbm(0, 0); /* No scroll region */
823 cup(1, 1);
824 printf("%s", "Exactly half of the box should remain. ");
825 return MENU_HOLD;
826 }
827
828 int
tst_insdel(MENU_ARGS)829 tst_insdel(MENU_ARGS)
830 {
831 /* Test of:
832 SM/RM(4) (= IRM (Insertion/replacement mode))
833 ICH (Insert Character)
834 DCH (Delete character)
835 IL (Insert line)
836 DL (Delete line)
837 */
838
839 int i, row, col, sw, dblchr, scr132;
840
841 for (scr132 = 0; scr132 <= 1; scr132++) {
842 if (scr132) {
843 deccolm(TRUE);
844 sw = max_cols;
845 } else {
846 deccolm(FALSE);
847 sw = min_cols;
848 }
849 ed(2);
850 cup(1, 1);
851 for (row = 1; row <= max_lines; row++) {
852 cup(row, 1);
853 for (col = 1; col <= sw; col++)
854 printf("%c", 'A' - 1 + row);
855 }
856 cup(4, 1);
857 printf("Screen accordion test (Insert & Delete Line). ");
858 holdit();
859
860 ri();
861 el(2);
862 decstbm(2, max_lines - 1);
863 decom(TRUE);
864 cup(1, 1);
865 for (row = 1; row <= max_lines; row++) {
866 il(row);
867 dl(row);
868 }
869 decom(FALSE);
870 decstbm(0, 0);
871 cup(2, 1);
872 printf("Top line: A's, bottom line: %c's, this line, nothing more. ",
873 'A' - 1 + max_lines);
874 holdit();
875 cup(2, 1);
876 ed(0);
877 cup(1, 2);
878 printf("B");
879 cub(1);
880 sm("4");
881 for (col = 2; col <= sw - 1; col++)
882 printf("*");
883 rm("4");
884 cup(4, 1);
885 printf("Test of 'Insert Mode'. The top line should be 'A*** ... ***B'. ");
886 holdit();
887 ri();
888 el(2);
889 cup(1, 2);
890 dch(sw - 2);
891 cup(4, 1);
892 printf("Test of 'Delete Character'. The top line should be 'AB'. ");
893 holdit();
894
895 for (dblchr = 1; dblchr <= 2; dblchr++) {
896 ed(2);
897 for (row = 1; row <= max_lines; row++) {
898 cup(row, 1);
899 if (dblchr == 2)
900 decdwl();
901 for (col = 1; col <= sw / dblchr; col++)
902 printf("%c", 'A' - 1 + row);
903 cup(row, sw / dblchr - row);
904 dch(row);
905 }
906 cup(4, 1);
907 println("The right column should be staggered ");
908 printf("by one. ");
909 holdit();
910 }
911 ed(2);
912 cup(1, 1);
913 println("If your terminal has the ANSI 'Insert Character' function");
914 println("(the VT102 does not), then you should see a line like this");
915 println(" A B C D E F G H I J K L M N O P Q R S T U V W X Y Z");
916 println("below:");
917 println("");
918 for (i = 'Z'; i >= 'A'; i--) {
919 printf("%c%c", i, BS);
920 ich(2);
921 }
922 cup(10, 1);
923 holdit();
924
925 if (sw == max_cols)
926 deccolm(FALSE);
927 }
928 return MENU_NOHOLD;
929 }
930
931 /* Test of some known VT100 bugs and misfeatures */
932
933 int
tst_bugs(MENU_ARGS)934 tst_bugs(MENU_ARGS)
935 {
936 int i;
937 /* *INDENT-OFF* */
938 static MENU menutable[] = {
939 { "Exit to main menu", 0 },
940 { "Bug A: Smooth scroll to jump scroll", bug_a },
941 { "Bug B: Scrolling region", bug_b },
942 { "Bug C: Wide to narrow screen", bug_c },
943 { "Bug D: Narrow to wide screen", bug_d },
944 { "Bug E: Cursor move from double- to single-wide line", bug_e },
945 { "Bug F: Column mode escape sequence", bug_f },
946 { "Wrap around with cursor addressing", bug_w },
947 { "Erase right half of double width lines", bug_l },
948 { "Funny scroll regions", bug_s },
949 /* Add more here */
950 { "", 0 }
951 };
952 /* *INDENT-ON* */
953
954 static const char *hmsg[] =
955 {
956 "Test of known bugs in the DEC VT100 series. The numbering of some of",
957 "the bugs (A-F) refers to the article 'VT100 MAGIC' by Sami Tabih in",
958 "the 'Proceedings of the DEC Users Society' at St. Louis, Missouri, May",
959 "1983. To understand some of the tests, you have to look at the source",
960 "code or the article. Of course, a good VT100-compatible terminal",
961 "should not have these bugs (or have some means of disabling them)! If",
962 "a bug appears, you might want to RESET the terminal before continuing",
963 "the test. There is a test of the RESET function in the main menu.",
964 ""};
965
966 do {
967 vt_clear(2);
968 vt_move(1, 1);
969 for (i = 0; *hmsg[i]; i++)
970 println(hmsg[i]);
971 println("");
972 println(" Choose bug test number:");
973 } while (menu2(menutable, i + 1));
974 return MENU_NOHOLD;
975 }
976
977 /* Bug A: Smooth scroll to jump scroll */
978
979 int
bug_a(MENU_ARGS)980 bug_a(MENU_ARGS)
981 {
982 int i;
983
984 cup(10, 1);
985 println("This is a test of the VT100 'Scroll while toggle softscroll'");
986 println("bug. The cursor may disappear, or move UP the screen, or");
987 println("multiple copies of some lines may appear.");
988 holdit();
989
990 /* Invoke the bug */
991
992 esc("[24H"); /* Simplified cursor movement */
993 decsclm(FALSE);
994 for (i = 1; i <= 20; i++)
995 printf("\n");
996
997 decsclm(TRUE);
998 for (i = 1; i <= 10; i++)
999 printf("\n");
1000
1001 decsclm(FALSE);
1002 for (i = 1; i <= 5; i++)
1003 printf("\n");
1004
1005 /* That should be enough to show the bug. But we'll try another way: */
1006 decsclm(TRUE); /* Set soft scroll */
1007 nel(); /* "NextLine", move down */
1008 decsclm(FALSE); /* Reset soft scroll */
1009 nel(); /* "NextLine", move down */
1010 for (i = 1; i <= 10; i++) { /* Show the bug */
1011 printf("Softscroll bug test, line %d. ", i);
1012 holdit();
1013 }
1014 println("That should have been enough to show the bug, if present.");
1015 return MENU_HOLD;
1016 }
1017
1018 /* Bug B: Scrolling region */
1019
1020 int
bug_b(MENU_ARGS)1021 bug_b(MENU_ARGS)
1022 {
1023 char c;
1024
1025 decaln();
1026
1027 cup(1, 1);
1028 el(0);
1029 printf("Line 11 should be double-wide, line 12 should be cleared.");
1030
1031 cup(2, 1);
1032 el(0);
1033 printf("Then, the letters A-P should be written at the beginning");
1034
1035 cup(3, 1);
1036 el(0);
1037 printf("of lines 12-%d, and the empty line and A-E are scrolled away.", max_lines);
1038
1039 cup(4, 1);
1040 el(0);
1041 printf("If the bug is present, some lines are confused, look at K-P.");
1042
1043 cup(11, 1);
1044 decdwl();
1045 decstbm(12, max_lines);
1046
1047 cup(12, 1);
1048 el(0);
1049 printf("Here we go... ");
1050 holdit();
1051
1052 cup(12, 1);
1053 ri(); /* Bug comes here */
1054 for (c = 'A'; c <= 'P'; c++)
1055 printf("%c\n", c); /* Bug shows here */
1056 holdit();
1057 decstbm(0, 0); /* No scr. region */
1058 return MENU_NOHOLD;
1059 }
1060
1061 /* Bug C: Wide to narrow screen */
1062
1063 int
bug_c(MENU_ARGS)1064 bug_c(MENU_ARGS)
1065 {
1066 deccolm(TRUE); /* 132 column mode */
1067 cup(1, 81);
1068 deccolm(FALSE); /* 80 column mode */
1069 cup(12, 5);
1070 printf("Except for this line, the screen should be blank. ");
1071 return MENU_HOLD;
1072 }
1073
1074 /* Bug D: Narrow to wide screen */
1075
1076 int
bug_d(MENU_ARGS)1077 bug_d(MENU_ARGS)
1078 {
1079 int i;
1080 char result;
1081 /* Make the bug appear */
1082 do {
1083 cup(14, 1);
1084
1085 /* The original code in the article says
1086 * PRINT ESC$; "[13;1H"; CHR$(10%);
1087 * but I guess a cup(14,1); would do.
1088 * (To output a pure LF might be tricky).
1089 */
1090
1091 deccolm(TRUE); /* Make the bug visible */
1092
1093 cup(1, 9);
1094 decdwl();
1095 println("You should see blinking text at the bottom line.");
1096
1097 cup(3, 9);
1098 decdwl();
1099 println("Enter 0 to exit, 1 to try to invoke the bug again.");
1100
1101 cup(max_lines, 9);
1102 decdwl();
1103 sgr("1;5;7");
1104 printf("If you can see this then the bug did not appear.");
1105 sgr("");
1106
1107 cup(4, 9);
1108 decdwl();
1109 result = inchar();
1110 readnl();
1111 deccolm(FALSE);
1112 } while (result == '1');
1113 decsclm(TRUE); /* Syrup scroll */
1114 cup(max_lines - 1, 1);
1115 for (i = 1; i <= 5; i++)
1116 println("If the bug is present, this should make things much worse!");
1117 holdit();
1118 decsclm(FALSE); /* Jump scroll */
1119 return MENU_NOHOLD;
1120 }
1121
1122 /* Bug E: Cursor move from double- to single-wide line */
1123
1124 int
bug_e(MENU_ARGS)1125 bug_e(MENU_ARGS)
1126 {
1127 int i;
1128 static const char *rend[2] =
1129 {"m", "7m"};
1130 deccolm(TRUE);
1131 cup(1, 1);
1132 decdwl();
1133 println("This test should put an 'X' at line 3 column 100.");
1134 for (i = 1; i <= 12; i++)
1135 printf("1234567890%s%s", csi_output(), rend[i & 1]);
1136 cup(1, 1); /* The bug appears when we jump from a double-wide line */
1137 cup(3, 100); /* to a single-wide line, column > 66. */
1138 printf("X");
1139 cup(4, max_cols / 2);
1140 printf("! !");
1141 cup(5, 1);
1142 printf("--------------------------- The 'X' should NOT be above here -");
1143 printf("---+------------ but above here -----+");
1144 cup(10, 1);
1145 decdwl();
1146 holdit();
1147 deccolm(FALSE);
1148 return MENU_NOHOLD;
1149 }
1150
1151 /* Bug F: Column mode escape sequence */
1152
1153 int
bug_f(MENU_ARGS)1154 bug_f(MENU_ARGS)
1155 {
1156 /*
1157 * VT100 "toggle origin mode, forget rest" bug. If you try to set
1158 * (or clear) parameters and one of them is the "origin mode"
1159 * ("?6") parameter, parameters that appear after the "?6"
1160 * remain unaffected. This is also true on CIT-101 terminals.
1161 */
1162 decscnm(TRUE); /* Set reverse mode */
1163 deccolm(TRUE); /* Set 132 column mode */
1164 println("Test VT100 'Toggle origin mode, forget rest' bug, part 1.");
1165 printf("The screen should be in reverse, %d column mode.\n", max_cols);
1166 holdit();
1167 ed(2);
1168 rm("?6;5;3"); /* Reset (origin, reverse, 132 col) */
1169 println("Test VT100 'Toggle origin mode, forget rest' bug, part 2.\n");
1170 printf("The screen should be in non-reverse, %d column mode.\n", min_cols);
1171 return MENU_HOLD;
1172 }
1173
1174 /* Bug W:
1175 * The dreaded "wraparound" bug! You CUP to col 80, write a char,
1176 * CUP to another line in col 80, write a char. And the brain-damaged
1177 * terminal thinks that "Hokay, so he's written a char in col 80, so
1178 * I stay in col 80 and wait for next character. Let's see now, here
1179 * comes another character, and I'm still in col 80, so I must make
1180 * a NewLine first." -- It doesn't clear that "still in col 80" flag
1181 * on a CUP. Argh!
1182 */
1183
1184 int
bug_w(MENU_ARGS)1185 bug_w(MENU_ARGS)
1186 {
1187 int row, col;
1188
1189 cup(16, 1);
1190 println(" This illustrates the \"wrap around bug\" which exists on a");
1191 println(" standard VT100. At the top of the screen there should be");
1192 println(" a row of +'s, and the rightmost column should be filled");
1193 println(" with *'s. But if the bug is present, some of the *'s may");
1194 println(" be placed in other places, e.g. in the leftmost column,");
1195 println(" and the top line of +'s may be scrolled away.");
1196
1197 cup(1, 1);
1198 for (col = 1; col <= min_cols - 1; col++)
1199 printf("+");
1200 for (row = 1; row <= max_lines; row++) {
1201 hvp(row, min_cols);
1202 printf("*");
1203 }
1204 cup(max_lines, 1);
1205 return MENU_HOLD;
1206 }
1207
1208 /* Bug L:
1209 * Check if the right half of double-width lines comes back
1210 * when a line is first set to single-width, filled with stuff,
1211 * set to double-width, and finally reset to single-width.
1212 *
1213 * A VT100 has this misfeature, and many others. Foo!
1214 */
1215
1216 int
bug_l(MENU_ARGS)1217 bug_l(MENU_ARGS)
1218 {
1219 cup(15, 1);
1220 printf("This-is-a-long-line-This-is-a-long-line-");
1221 printf("This-is-a-long-line-This-is-a-long-line-");
1222 cup(1, 1);
1223 printf("This is a test of what happens to the right half of double-width");
1224 println(" lines.");
1225 printf("A common misfeature is that the right half does not come back");
1226 println(" when a long");
1227 printf("single-width line is set to double-width and then reset to");
1228 println(" single-width.");
1229
1230 cup(5, 1);
1231 println("Now the line below should contain 80 characters in single width.");
1232 holdit();
1233 cup(15, 1);
1234 decdwl();
1235 cup(8, 1);
1236 println("Now the line below should contain 40 characters in double width.");
1237 holdit();
1238 cup(15, 1);
1239 decswl();
1240 cup(11, 1);
1241 println("Now the line below should contain 80 characters in single width.");
1242 holdit();
1243
1244 /* ...and in 132 column mode */
1245
1246 deccolm(TRUE);
1247 ed(2);
1248 cup(15, 1);
1249 printf("This-is-a-long-line-This-is-a-long-line-");
1250 printf("This-is-a-long-line-This-is-a-long-line-");
1251 printf("This-is-a-long-line-This-is-a-long-line-");
1252 printf("ending-here-");
1253
1254 cup(1, 1);
1255 printf("This is the same test in %d column mode.", max_cols);
1256
1257 cup(5, 1);
1258 printf("Now the line below should contain %d characters in single width.\n", max_cols);
1259 holdit();
1260
1261 cup(15, 1);
1262 decdwl();
1263
1264 cup(8, 1);
1265 printf("Now the line below should contain %d characters in double width.\n",
1266 max_cols / 2);
1267 holdit();
1268
1269 cup(15, 1);
1270 decswl();
1271
1272 cup(11, 1);
1273 printf("Now the line below should contain %d characters in single width.\n", max_cols);
1274
1275 holdit();
1276 deccolm(FALSE);
1277 return MENU_NOHOLD;
1278 }
1279
1280 int
bug_s(MENU_ARGS)1281 bug_s(MENU_ARGS)
1282 {
1283 int i;
1284 decstbm(20, 10); /* 20-10=-10, < 2, so no scroll region. */
1285 cup(1, 1);
1286 for (i = 1; i <= 20; i++)
1287 printf("This is 20 lines of text (line %d), no scroll region.\n", i);
1288 holdit();
1289 ed(2);
1290 decstbm(0, 1); /* Should be interpreted as decstbm(1,1) = none */
1291 cup(1, 1);
1292 for (i = 1; i <= 20; i++)
1293 printf("This is 20 lines of text (line %d), no scroll region.\n", i);
1294 holdit();
1295 decstbm(0, 0); /* No scroll region (just in case...) */
1296 return MENU_NOHOLD;
1297 }
1298
1299 void
initterminal(int pn)1300 initterminal(int pn)
1301 {
1302 init_ttymodes(pn);
1303 setup_terminal("");
1304 }
1305
1306 /* Set up my personal prejudices */
1307 int
setup_terminal(MENU_ARGS)1308 setup_terminal(MENU_ARGS)
1309 {
1310 if (LOG_ENABLED)
1311 fprintf(log_fp, "Setup Terminal with test-defaults\n");
1312
1313 default_level(); /* Enter ANSI mode (if in VT52 mode) */
1314 decckm(FALSE); /* cursor keys normal */
1315 deccolm(FALSE); /* 80 col mode */
1316 decsclm(FALSE); /* Jump scroll */
1317 decscnm(FALSE); /* Normal screen */
1318 decom(FALSE); /* Absolute origin mode */
1319 decawm(TRUE); /* Wrap around on */
1320 decarm(FALSE); /* Auto repeat off */
1321 sm("?40"); /* Enable 80/132 switch (xterm) */
1322 rm("?45"); /* Disable reverse wrap (xterm) */
1323 decstbm(0, 0); /* No scroll region */
1324 sgr("0"); /* Normal character attributes */
1325
1326 return MENU_NOHOLD;
1327 }
1328
1329 void
bye(void)1330 bye(void)
1331 {
1332 /* Force my personal prejudices upon the poor luser */
1333 if (LOG_ENABLED)
1334 fprintf(log_fp, "Cleanup & exit\n");
1335
1336 default_level(); /* Enter ANSI mode (if in VT52 mode) */
1337 decckm(FALSE); /* cursor keys normal */
1338 deccolm(FALSE); /* 80 col mode */
1339 decscnm(FALSE); /* Normal screen */
1340 decom(FALSE); /* Absolute origin mode */
1341 decawm(TRUE); /* Wrap around on */
1342 decarm(TRUE); /* Auto repeat on */
1343 decstbm(0, 0); /* No scroll region */
1344 sgr("0"); /* Normal character attributes */
1345
1346 /* Say goodbye */
1347
1348 vt_clear(2);
1349 vt_move(12, 30);
1350 printf("That's all, folks!\n");
1351 printf("\n\n\n");
1352 inflush();
1353 close_tty();
1354 exit(EXIT_SUCCESS);
1355 }
1356
1357 #ifdef UNIX
1358 RETSIGTYPE
onbrk(SIG_ARGS GCC_UNUSED)1359 onbrk(SIG_ARGS GCC_UNUSED)
1360 {
1361 signal(SIGINT, onbrk);
1362 if (reading) {
1363 brkrd = TRUE;
1364 #ifdef HAVE_ALARM
1365 alarm(0);
1366 #endif
1367 } else {
1368 longjmp(intrenv, 1);
1369 }
1370 }
1371
1372 RETSIGTYPE
onterm(SIG_ARGS GCC_UNUSED)1373 onterm(SIG_ARGS GCC_UNUSED)
1374 {
1375 signal(SIGTERM, onterm);
1376 longjmp(intrenv, 1);
1377 }
1378 #endif
1379
1380 int
scanto(const char * str,int * pos,int toc)1381 scanto(const char *str, int *pos, int toc)
1382 {
1383 char c;
1384 int result = 0;
1385
1386 while (toc != (c = str[(*pos)])) {
1387 *pos += 1;
1388 if (isdigit(CharOf(c)))
1389 result = result * 10 + c - '0';
1390 else
1391 break;
1392 }
1393 if (c == toc) {
1394 *pos += 1; /* point past delimiter */
1395 return (result);
1396 }
1397 return (0);
1398 }
1399
1400 int
scan_any(char * str,int * pos,int toc)1401 scan_any(char *str, int *pos, int toc)
1402 {
1403 int save = *pos;
1404 int value = scanto(str, pos, ';');
1405 if (value == 0) {
1406 *pos = save;
1407 value = scanto(str, pos, toc);
1408 if (str[*pos] != '\0')
1409 value = 0;
1410 }
1411 return value;
1412 }
1413
1414 static const char *
push_menu(int number)1415 push_menu(int number)
1416 {
1417 const char *saved = current_menu;
1418 current_menu = (char *) malloc(strlen(saved) + 10);
1419 sprintf(current_menu, "%s%s%d", saved, *saved ? "." : "", number);
1420 return saved;
1421 }
1422
1423 static void
pop_menu(const char * saved)1424 pop_menu(const char *saved)
1425 {
1426 if (current_menu && *current_menu)
1427 free(current_menu);
1428 current_menu = (char *) saved;
1429 }
1430
1431 #define end_of_menu(table, number) \
1432 (table[number].description[0] == '\0')
1433
1434 static void
show_entry(MENU * table,int number)1435 show_entry(MENU *table, int number)
1436 {
1437 printf(" %d%c %s\n", number,
1438 (table[number].dispatch == not_impl) ? '*' : '.',
1439 table[number].description);
1440 }
1441
1442 static int
next_menu(MENU * table,int top,int size)1443 next_menu(MENU *table, int top, int size)
1444 {
1445 int last;
1446 int next = top + size;
1447 for (last = top; last <= next && !end_of_menu(table, last); ++last) {
1448 ;
1449 }
1450 return (last >= next) ? next : top;
1451 }
1452
1453 static int
prev_menu(int top,int size)1454 prev_menu(int top, int size)
1455 {
1456 return (top > 1) ? (top - size) : top;
1457 }
1458
1459 int
menu2(MENU * table,int top)1460 menu2(MENU *table, int top)
1461 {
1462 int i, tablesize, choice;
1463 char c;
1464 char storage[BUFSIZ];
1465 int pagesize = max_lines - 7 - TITLE_LINE;
1466 int pagetop = 1;
1467 int redraw = FALSE;
1468
1469 tablesize = 0;
1470 for (i = 0; !end_of_menu(table, i); i++) {
1471 tablesize++;
1472 }
1473 tablesize--;
1474
1475 for (;;) {
1476 vt_move(top, 1);
1477 vt_clear(0);
1478
1479 println("");
1480 show_entry(table, 0);
1481 for (i = 0; i < pagesize; i++) {
1482 int j = pagetop + i;
1483 if (end_of_menu(table, j))
1484 break;
1485 show_entry(table, pagetop + i);
1486 }
1487
1488 printf("\n Enter choice number (0 - %d): ", tablesize);
1489 for (;;) {
1490 char *s = storage;
1491 inputline(s);
1492 choice = 0;
1493 redraw = FALSE;
1494 while ((c = *s++) != '\0') {
1495 if (c == '*') {
1496 choice = -1;
1497 break;
1498 } else if (c == '?') {
1499 redraw = TRUE;
1500 break;
1501 } else if (tablesize > pagesize && c == 'n') {
1502 pagetop = next_menu(table, pagetop, pagesize);
1503 redraw = TRUE;
1504 break;
1505 } else if (tablesize > pagesize && c == 'p') {
1506 pagetop = prev_menu(pagetop, pagesize);
1507 redraw = TRUE;
1508 break;
1509 } else if (c >= '0' && c <= '9') {
1510 choice = 10 * choice + c - '0';
1511 } else {
1512 choice = tablesize + 1;
1513 break;
1514 }
1515 }
1516
1517 if (redraw)
1518 break;
1519
1520 if (choice < 0) {
1521 for (choice = 0; choice <= tablesize; choice++) {
1522 vt_clear(2);
1523 if (table[choice].dispatch != 0) {
1524 const char *save = push_menu(choice);
1525 const char *name = table[choice].description;
1526 if (LOG_ENABLED)
1527 fprintf(log_fp, "Menu %s: %s\n", current_menu, name);
1528 if ((*table[choice].dispatch) (name) == MENU_HOLD)
1529 holdit();
1530 pop_menu(save);
1531 }
1532 }
1533 return 1;
1534 } else if (choice <= tablesize) {
1535 vt_clear(2);
1536 if (table[choice].dispatch != 0) {
1537 const char *save = push_menu(choice);
1538 const char *name = table[choice].description;
1539 if (LOG_ENABLED)
1540 fprintf(log_fp, "Menu %s: %s\n", current_menu, name);
1541 if ((*table[choice].dispatch) (name) != MENU_NOHOLD)
1542 holdit();
1543 pop_menu(save);
1544 }
1545 return (table[choice].dispatch != 0);
1546 }
1547 printf(" Bad choice, try again: ");
1548 }
1549 }
1550 }
1551
1552 int
menu(MENU * table)1553 menu(MENU *table)
1554 {
1555 return menu2(table, 6);
1556 }
1557
1558 /*
1559 * Return updated row-number based on the number of characters printed to the
1560 * screen, e.g., for test_report_ops() to handle very long results.
1561 */
1562 int
chrprint2(const char * s,int row,int col)1563 chrprint2(const char *s, int row, int col)
1564 {
1565 int i;
1566 int result = row;
1567 int tracks = col;
1568 char temp[80];
1569
1570 vt_hilite(TRUE);
1571 printf(" ");
1572 tracks += 1;
1573
1574 for (i = 0; s[i] != '\0'; i++) {
1575 int c = (unsigned char) s[i];
1576 int step;
1577 if (c <= ' ' || c >= '\177') {
1578 sprintf(temp, "<%d> ", c);
1579 } else {
1580 sprintf(temp, "%c ", c);
1581 }
1582 step = (int) strlen(temp);
1583 tracks += step;
1584 if ((tracks >= min_cols) && (col > 1)) {
1585 vt_move(++result, col);
1586 tracks = col + step;
1587 }
1588 fputs(temp, stdout);
1589 }
1590
1591 vt_hilite(FALSE);
1592 return result + 1;
1593 }
1594
1595 /*
1596 * Returns a pointer past the prefix, or null if no match is found
1597 */
1598 char *
skip_prefix(const char * prefix,char * input)1599 skip_prefix(const char *prefix, char *input)
1600 {
1601 while (*prefix != '\0') {
1602 if (*prefix++ != *input++)
1603 return 0;
1604 }
1605 return input;
1606 }
1607
1608 char *
skip_csi(char * input)1609 skip_csi(char *input)
1610 {
1611 if ((unsigned char) *input == CSI) {
1612 return input + 1;
1613 }
1614 return skip_prefix(csi_input(), input);
1615 }
1616
1617 char *
skip_dcs(char * input)1618 skip_dcs(char *input)
1619 {
1620 if ((unsigned char) *input == DCS) {
1621 return input + 1;
1622 }
1623 return skip_prefix(dcs_input(), input);
1624 }
1625
1626 char *
skip_ss3(char * input)1627 skip_ss3(char *input)
1628 {
1629 if ((unsigned char) *input == SS3) {
1630 return input + 1;
1631 }
1632 return skip_prefix(ss3_input(), input);
1633 }
1634
1635 /*
1636 * Variant with const params
1637 */
1638 const char *
skip_prefix_2(const char * prefix,const char * input)1639 skip_prefix_2(const char *prefix, const char *input)
1640 {
1641 while (*prefix != '\0') {
1642 if (*prefix++ != *input++)
1643 return 0;
1644 }
1645 return input;
1646 }
1647
1648 const char *
skip_csi_2(const char * input)1649 skip_csi_2(const char *input)
1650 {
1651 if ((unsigned char) *input == CSI) {
1652 return input + 1;
1653 }
1654 return skip_prefix_2(csi_input(), input);
1655 }
1656
1657 const char *
skip_dcs_2(const char * input)1658 skip_dcs_2(const char *input)
1659 {
1660 if ((unsigned char) *input == DCS) {
1661 return input + 1;
1662 }
1663 return skip_prefix_2(dcs_input(), input);
1664 }
1665
1666 const char *
skip_ss3_2(const char * input)1667 skip_ss3_2(const char *input)
1668 {
1669 if ((unsigned char) *input == SS3) {
1670 return input + 1;
1671 }
1672 return skip_prefix_2(ss3_input(), input);
1673 }
1674
1675 /*
1676 * Returns a pointer past digits, or null if none are found
1677 */
1678 char *
skip_digits(char * src)1679 skip_digits(char *src)
1680 {
1681 char *base = src;
1682 while (*src != '\0' && isdigit(CharOf(*src)))
1683 src++;
1684 return (base == src) ? 0 : src;
1685 }
1686
1687 #define xdigitOf(c) \
1688 (((c) >= '0' && (c) <= '9') \
1689 ? ((c) - '0') \
1690 : (((c) >= 'A' && (c) <= 'F') \
1691 ? ((c) + 10 - 'A') \
1692 : ((c) + 10 - 'a')))
1693
1694 char *
skip_xdigits(char * src,int * value)1695 skip_xdigits(char *src, int *value)
1696 {
1697 char *base = src;
1698 *value = 0;
1699 while (*src != '\0' && isxdigit(CharOf(*src))) {
1700 int ch = CharOf(*src);
1701 *value <<= 4;
1702 *value += xdigitOf(ch);
1703 src++;
1704 }
1705 return (base == src) ? 0 : src;
1706 }
1707
1708 const char *
skip_digits_2(const char * src)1709 skip_digits_2(const char *src)
1710 {
1711 const char *base = src;
1712 while (*src != '\0' && isdigit(CharOf(*src)))
1713 src++;
1714 return (base == src) ? 0 : src;
1715 }
1716
1717 /*
1718 * Strip the string terminator (ST) from the given string, returning true if
1719 * we did this.
1720 */
1721 int
strip_suffix(char * src,const char * suffix)1722 strip_suffix(char *src, const char *suffix)
1723 {
1724 int have = (int) strlen(src);
1725 int want = (int) strlen(suffix);
1726 if (have > want) {
1727 have -= want;
1728 if (!strcmp(src + have, suffix)) {
1729 src[have] = '\0';
1730 return TRUE;
1731 }
1732 }
1733 return FALSE;
1734 }
1735
1736 /*
1737 * Strip the string terminator (ST) from the given string, returning true if
1738 * we did this.
1739 */
1740 int
strip_terminator(char * src)1741 strip_terminator(char *src)
1742 {
1743 int ok = strip_suffix(src, st_input());
1744 if (!ok) {
1745 int have = (int) strlen(src);
1746 if (have > 0 && (unsigned char) src[have - 1] == ST) {
1747 ok = TRUE;
1748 src[--have] = '\0';
1749 }
1750 }
1751 if (!ok && LOG_ENABLED)
1752 fprintf(log_fp, "Missing ST\n");
1753 return ok;
1754 }
1755
1756 /* Parse the contents of a report from DECRQSS, returning the data as well */
1757 int
parse_decrqss(char * report,const char * func)1758 parse_decrqss(char *report, const char *func)
1759 {
1760 int code = -1;
1761 char *parse = report;
1762
1763 if ((parse = skip_dcs(parse)) != 0
1764 && strip_terminator(parse)
1765 && strip_suffix(parse, func)) {
1766 if (!strncmp(parse, "1$r", (size_t) 3))
1767 code = 1;
1768 else if (!strncmp(parse, "0$r", (size_t) 3))
1769 code = 0;
1770 }
1771
1772 if (code >= 0) {
1773 while ((*report++ = parse[3]) != '\0')
1774 parse++;
1775 }
1776 return code;
1777 }
1778
1779 int
title(int offset)1780 title(int offset)
1781 {
1782 vt_move(TITLE_LINE + offset, 10);
1783 if (offset == 0 && *current_menu)
1784 printf("Menu %s: ", current_menu);
1785 return 1; /* used for indenting */
1786 }
1787
1788 static void
my_vfprintf(FILE * fp,va_list ap,const char * fmt)1789 my_vfprintf(FILE *fp, va_list ap, const char *fmt)
1790 {
1791 while (*fmt != '\0') {
1792 if (*fmt == '%') {
1793 switch (*++fmt) {
1794 case 'c':
1795 fputc(va_arg(ap, int), fp);
1796 break;
1797 case 'x':
1798 fprintf(fp, "%x", va_arg(ap, int));
1799 break;
1800 case 'd':
1801 fprintf(fp, "%d", va_arg(ap, int));
1802 break;
1803 case 's':
1804 fputs(va_arg(ap, char *), fp);
1805 break;
1806 }
1807 } else if (*fmt != '\n') {
1808 fputc(*fmt, fp);
1809 }
1810 fmt++;
1811 }
1812 }
1813
1814 /*
1815 * Show a test-result, optionally logging it as well.
1816 */
1817 void
show_result(const char * fmt,...)1818 show_result(const char *fmt,...)
1819 {
1820 va_list ap;
1821
1822 if (*fmt != ' ')
1823 fputc(' ', stdout);
1824 va_start(ap, fmt);
1825 my_vfprintf(stdout, ap, fmt);
1826 va_end(ap);
1827
1828 if (LOG_ENABLED) {
1829 fputs("Result: ", log_fp);
1830 va_start(ap, fmt);
1831 my_vfprintf(log_fp, ap, fmt);
1832 va_end(ap);
1833 fputc('\n', log_fp);
1834 }
1835 }
1836
1837 /*
1838 * Use this to make some complex stuff (such as scrolling) slow enough to see.
1839 */
1840 void
slowly(void)1841 slowly(void)
1842 {
1843 if (slow_motion) {
1844 #ifdef HAVE_USLEEP
1845 fflush(stdout);
1846 zleep(100);
1847 #endif
1848 }
1849 }
1850
1851 /*
1852 * Bypass normal logging for control sequences that are used only to format
1853 * the test results.
1854 */
1855 void
vt_clear(int code)1856 vt_clear(int code)
1857 {
1858 log_disabled++;
1859 ed(code);
1860 log_disabled--;
1861 }
1862
1863 void
vt_el(int code)1864 vt_el(int code)
1865 {
1866 log_disabled++;
1867 el(code);
1868 log_disabled--;
1869 }
1870
1871 int
vt_move(int row,int col)1872 vt_move(int row, int col)
1873 {
1874 log_disabled++;
1875 cup(row, col);
1876 log_disabled--;
1877 return 1;
1878 }
1879
1880 void
vt_hilite(int flag)1881 vt_hilite(int flag)
1882 {
1883 log_disabled++;
1884 sgr(flag ? "7" : "");
1885 log_disabled--;
1886 }
1887