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