/* Copyright (C) Magna Carta Software, Inc. 1990, 1991. All Rights Reserved. C COMMUNICATIONS TOOLKIT TERMINAL.C -- Terminal emulation routines common across all terminals. */ #define CCT_DEVELOPMENT #if (defined(CCTW) || defined(_WINDOWS)) #include #endif #include #include #include #include #include #include #define KEY_PTR (char * ((*)[MAX_TERM_KEYS][MAX_SHIFT_STATES])) WORD dcol = 0, drow = 24; static short near strcount_(char *s, short parm); static short near term_c_puts(TERMINAL *t, const char *s); static short term_c_write(struct comm_port *p, WORD ch); static short term_conoutc_(struct comm_port *p, short ch); static short near term_parse_(TERMINAL *t, short ch); /* INIT_TERM -- Alter the emulated terminal. Return Value: 0 -- success; EOF -- insufficient memory for terminal numeric parameter buffer; -2 -- insufficient memory for terminal keyboard responses; -3 -- insufficient memory for terminal response strings; -4 -- insufficient memory for terminal command storage buffer; */ short init_term(COMM_PORT *p, TERMINAL *t, void (*v)(void), void (*video)(TERMINAL *)) { short ret = 0; drow = 24; dcol = 0; if (t->type != 0 || v == NULL) { if (t->tabstop != NULL) memfree(t->tabstop); if (t->cmdbuf != NULL) memfree(t->cmdbuf); if (t->tr != NULL) memfree(t->tr); if (t->keys != NULL) memfree(t->keys); if (t->num != NULL) memfree(t->num); /* IF TERMINAL TYPE IS NOT TTY THEN DO THIS */ if (t->type != 0) { t->type = 0; p->c_write = t->s_write; p->con_out = (short (*)(COMM_PORT *, short)) t->s_con_put; } } if (v != NULL) { /* ALLOCATE BUFFER FOR TEMPORARY STORAGE OF COMMAND PARAMETERS */ t->num = (short *) memcalloc(1, MAX_TERM_PARMS * sizeof(short)); if (t->num != NULL) { /* ALLOCATE ARRAY FOR KEYS */ t->keys = (char *) memcalloc(MAX_TERM_KEYS, MAX_SHIFT_STATES*(MAX_CMD_LEN+1)); if (t->keys != NULL) { /* ALLOCATE ARRAY FOR TERMINAL RESPONSE STRUCTURE */ t->tr = (TERM_RESPONSE *) memcalloc(MAX_TERM_CMDS, sizeof(TERM_RESPONSE)); if (t->tr != NULL) { t->cmdbuf = (char *) memcalloc(1, MAX_CMD_LEN); if (t->cmdbuf != NULL) { t->tabstop = (char *) memcalloc(1, t->d_cols); if (t->tabstop != NULL) { /* PERFORM THE TERMINAL-SPECIFIC INIT. */ (*(short (*)(struct terminal *))v)(t); t->crmargin = t->rmargin = t->d_cols - 1; t->cbmargin = t->bmargin = t->d_rows - 1; t->ctmargin = t->clmargin = 0; t->tmargin = t->lmargin = 0; t->txstat = p->txstat; t->port = p; p->term = t; /* SET THE VIDEO FUNCTIONS */ (*video)(t); t->vid_init = video; if (t->d_att == 0) t->d_att = ATT(7, 0); /* DETERMINE TYPE OF KEYBOARD */ enhanced_kbd = kbd_type(); /* SAVE LOW-LEVEL PORT WRITE FN. AND REPLACE WITH OURS */ t->s_write = p->c_write; /* save old port low-level write fn. */ p->c_write = term_c_write; /* fn. to send byte to port */ /* SAVE CONSOLE OUTPUT FN. AND REPLACE WITH OURS */ t->s_con_put = (short (*)(COMM_PORT *, short)) p->con_out; p->con_out = term_conoutc_; /* local echo function */ } else { ret = NO_RAM; memfree(t->cmdbuf); memfree(t->tr); memfree(t->keys); memfree(t->num); } } else { ret = NO_RAM; memfree(t->tr); memfree(t->keys); memfree(t->num); } } else { ret = NO_RAM; memfree(t->keys); memfree(t->num); } } else { ret = NO_RAM; memfree(t->num); } } else ret = NO_RAM; } return (ret); } /* STRCOUNT_ -- Count the number of instances of character '%' in string 's'. Return the number of instances. */ static short near strcount_(char *s, short parm) { short i=0; while (*s) if (*s++ == (char) parm) i++; return (i); } /* TERM_ASCII_PARSE_PARMS_ -- Parse the numeric parameters in a command string. */ short term_ascii_parse_parms_(TERMINAL *t, short *num) { WORD i; char *b = t->cmdbuf; for (i=0; i < MAX_TERM_PARMS; i++) *(num+i) = 0; i = 0; while (*b) { if (*b == (char) t->delim1) { num++; i++; } else if (isdigit(*b)) { while (isdigit(*b)) *num = (*num *= 10) + (*b++ & 0X0F); num++; i++; } if (*b) b++; } return (i); } short term_assign_cmd(TERMINAL *t, short cmd, const char *sh2t, void (*func)(struct terminal *, short), const char *st2h) { if (t == NULL) return (EOF); if (strlen(sh2t) > MAX_CMD_LEN) return (-2); if (st2h != NULL && strlen(st2h) > MAX_CMD_LEN) return (-2); strcpy(t->tr[cmd].sh2t, sh2t); /* save string from host */ if (func != NULL) t->tr[cmd].tcmd = func; if (st2h != NULL) strcpy(t->tr[cmd].st2h, st2h); return (0); } /* TERM_ASSIGN_KEY -- Assign a string to a keystroke. "key" is a WORD containing the scan code in the high byte and the ASCII code in the low byte. The string "cmd" is copied to an array with 256 rows. Only the scan code is used to assign the key to a row of the array "t->keys". */ short term_assign_key(TERMINAL *t, WORD key, WORD shift, const char *cmd) { if (strlen(cmd) > MAX_CMD_LEN) return (EOF); /* IN THE ARRAY, ENHANCED KEYS START AT ELEMENT 255, SO ADD 184 TO THEM */ if (enhanced_kbd && isekk(key)) { strcpy(t->keys + ((key >> 8)+184)*(MAX_CMD_LEN*MAX_SHIFT_STATES)+MAX_CMD_LEN*shift, cmd); } else strcpy(t->keys + (key >> 8)*(MAX_CMD_LEN*MAX_SHIFT_STATES)+MAX_CMD_LEN*shift, cmd); return (0); } /* TERM_BINARY_PARSE_PARMS_ -- Parse the numeric parameters in a command string from a binary terminal (e.g. TELEVIDEO). */ short term_binary_parse_parms_(TERMINAL *t, short *num) { WORD i; char *b = &t->cmdbuf[2]; for (i=0; i < MAX_TERM_PARMS; i++) *(num+i) = 0; i = 0; while (*b) { if (*b == (char) t->csi) { b++; continue; } *num++ = *b++; i++; } return (i); } /* TERM_C_PUTC -- Communications driver function to send a byte out of the serial device under terminal emulation. 'ch' is the character to be sent out of the port. It may be a keystroke, in which case it can consist of a scan code in the high byte and a character code in the low byte. See SCAN.H and ASCII.H for key definitions. */ short term_c_putc(TERMINAL *t, WORD ch) { short ret, scan = SCAN(ch); char *c; if (t->mode & KEYCLICK && t->key_click != NULL) (*t->key_click)(); if (isekk(ch)) scan += 184; c = t->keys + (MAX_CMD_LEN*MAX_SHIFT_STATES)*scan; if (*c != '\0') /* send string */ ret = term_c_puts(t, t->keys+(MAX_CMD_LEN*MAX_SHIFT_STATES)*scan); else ret = term_write(t, ch); /* send character */ return (ret); } /* TERM_C_PUTS -- Send a string out of the terminal port. */ static short near term_c_puts(TERMINAL *t, const char *s) { while (*s) term_write(t, *s++); return (0); } /* TERM_C_WRITE -- Driver function to send a byte out of the communications device. This function is pointed at by p->c_write (i.e. it replaces u8250_write(), and works if transmitter interrupts are enabled. It is just a 'binding' to make the terminal code independent of CCT. */ short term_c_write(COMM_PORT *p, WORD ch) { return(term_c_putc((TERMINAL *) p->term, ch)); } /* TERM_CONOUTC_ --- Under emulation, this function replaces conoutc(). It is just a 'binding' to make the terminal code independent of CCT. */ short term_conoutc_(COMM_PORT *p, short ch) { return(term_parse_((TERMINAL *) p->term, ch)); } short term_find_next_tab_stop(TERMINAL *t) { short i; for (i = t->ccol+1; i < t->crmargin; i++) if (t->tabstop[i] == 1) return (i); return (EOF); } /* TERM_PARSE_ -- Routine to parse characters received from remote and call the appropriate function to perform the desired action. Return value: 0 -- OK; EOF -- unrecognized escape sequence; */ static short near term_parse_(TERMINAL *t, short ch) { short ret = 0; static char *p; static short i, command_parsed, j, num_parms; t->ccol = (*t->cur_get_col)(t); t->crow = (*t->cur_get_row)(t); if (ch == t->csi) { t->inescape = TRUE; memset(t->cmdbuf, 0X0, MAX_CMD_LEN); p = t->cmdbuf; *p++ = (BYTE) ch; } else { /* ESCAPE CODE NOT RECEIVED */ if (!t->inescape) { if (iscntrl(ch)) ret = (*t->dispatch_ctrl)(t, ch); else { (*t->con_putc)(t, ch, t->att); ++t->ccol; if (t->ccol > t->rmargin) { if (t->crow < t->bmargin) (*t->cur_pos_)(t, 0, ++t->crow); else { (*t->cur_pos_)(t, 0, t->crow); (*t->scroll)(t, t->lmargin, t->tmargin, t->rmargin, t->bmargin, t->d_att, 1); } } else (*t->cur_pos_)(t, t->ccol, t->crow); } } else { if (t->debug) { (*t->con_put)(t, dcol, drow, ch, ATT(15, 0)); dcol = (dcol < t->d_cols) ? dcol+1 : 0; } *p = (BYTE) ch; /* t->append is TRUE for terminals that append parameters at the end of a command sequence (e.g. TV925). */ if (t->append) { if (!command_parsed) { i = (*t->parse_cmd)(t, t->cmdbuf); if (i != EOF) { num_parms = strcount_(t->tr[i].sh2t, '%'); if (num_parms) { j = 0; /* initialize j */ command_parsed = TRUE; } else { /* deal with commands with no parameters */ t->parmcount = 0; if (t->tr[i].tcmd != NULL) (*(void (*)(struct terminal *, short))t->tr[i].tcmd)(t, i); else if (t->tr[i].st2h != NULL) term_c_puts(t, t->tr[i].st2h); num_parms = command_parsed = t->inescape = FALSE; } } else { char *d = "Unknown escape sequence: "; short i=0, j=24; while (*d) (*t->con_put)(t, i++, j, *d++, ATT(15, 0)); d = t->cmdbuf; while (*d) (*t->con_put)(t, i++, j, *d++, ATT(15, 0)); t->inescape = FALSE; } } else { j++; if (j == num_parms) { t->parmcount = (*(short (*)(struct terminal *, short *))t->parse_parms)(t, t->num); if (t->tr[i].tcmd != NULL) (*(void (*)(struct terminal *, short))t->tr[i].tcmd)(t, i); else if (t->tr[i].st2h != NULL) term_c_puts(t, t->tr[i].st2h); num_parms = command_parsed = t->inescape = FALSE; } } } /* ANSI TERMINALS (PARAMETERS HAVE TRAILING DELIMITER */ else { i = (*t->parse_cmd)(t, t->cmdbuf); if (i != EOF) { /* recognize command received */ t->parmcount = (*(short (*)(struct terminal *, short *))t->parse_parms)(t, t->num); if (t->tr[i].tcmd != NULL) (*(void (*)(struct terminal *, short))t->tr[i].tcmd)(t, i); else if (t->tr[i].st2h != NULL) term_c_puts(t, t->tr[i].st2h); t->inescape = FALSE; } } if (t->debug && !t->inescape) { (*t->con_put)(t, dcol, drow, SP, ATT(15, 0)); dcol = (dcol < t->d_cols) ? dcol+1 : 0; if (dcol == 0) for (j=0; j< 80; j++) (*t->con_put)(t, dcol, drow, SP, ATT(15, 0)); #if 0 char *d = "Unknown escape sequence"; short i=0, j=24; while (*d) (*t->c_out)(t, i, j, *d++, ATT(15, 0)); #endif } else { ++p; /* add 'ch' to command buffer */ if (p - t->cmdbuf > MAX_CMD_LEN) t->inescape = FALSE; } } } #if 0 if (t->crow == 23 && orow == 21) printf("\arow= %d", t->crow); orow = t->crow; #endif return (ret); } /* TERM_PRINT_LINE_ -- Print a line of the screen in printer controller mode. */ short term_print_line_(TERMINAL *t) { short i, ch, ch1; short row = t->crow; for (i=0; i < t->ccol; i++) { ch = (*t->con_get)(t, i, row) & 0XFF; /* AND out attribute */ if (ch == NUL) continue; if (ch == XON) continue; if (ch == XOFF) continue; if (ch == t->csi) { if (ch == ESC) { ch1 = (*t->con_get)(t, i+2, row) & 0XFF; if (ch1 == '4' || ch1 == '5') i += 4; } else if (ch == CSI) { ch1 = (*t->con_get)(t, i+1, row) & 0XFF; if (ch1 == '4' || ch1 == '5') i += 3; } continue; } if (t->prn_putc != NULL) (*t->prn_putc)(ch); } return (0); } short term_set(TERMINAL *t, short parm, long val) { short ret = 0; if (t == NULL) return (EOF); switch (parm) { case TERM_COLORS: t->d_att = (short) val; break; case TERM_COLS: t->d_cols = t->rmargin = t->crmargin = (short) val; break; case TERM_KEYCLICK: if (val != 0L) { t->key_click = (void (*)(void)) val; t->mode |= KEYCLICK; } else { t->key_click = NULL; t->mode &= ~KEYCLICK; } break; case TERM_ROWS: t->d_rows = t->bmargin = t->cbmargin = (WORD) val; break; default: ret = EOF; break; } return (ret); } /* TERM_WRITE -- Send a byte out of the terminal port. Low level port I/O. (Blocking). */ short term_write(TERMINAL *t, WORD ch) { while ((*t->txstat)(t->port) == FALSE); return((*t->s_write)(t->port, ch)); }