1 /*
2  * VMS terminal driver
3  *
4  * Uses SMG to lookup capabilities of the terminal (see SYS$SYSTEM:SMGTERMS.TXT
5  * and SYS$SYSTEM:TERMTABLE.TXT)
6  *
7  * Originally written by Curtis Smith in 1987, this module has been rewritten
8  * by Tom Dickey and Clark Morgan to:
9  *
10  *   -- simplify it,
11  *   -- extend it to support a wider variety of terminal types,
12  *   -- add VT220 keyboard mappings,
13  *   -- support syntax highlighting,
14  *   -- support wide and narrow screen resolutions,
15  *   -- support visual bells.
16  *
17  * $Id: vmsvt.c,v 1.69 2010/02/14 18:43:48 tom Exp $
18  *
19  */
20 
21 #include	"estruct.h"	/* Emacs' structures            */
22 #include	"edef.h"	/* Emacs' definitions           */
23 #include	"nefunc.h"	/* f_backchar_to_bol            */
24 
25 #if DISP_VMSVT
26 
27 #include	<descrip.h>	/* Descriptor definitions       */
28 #include	<iodef.h>	/* to get IO$_SENSEMODE         */
29 #include	<ttdef.h>	/* to get TT$_UNKNOWN           */
30 #include	<stsdef.h>	/* to get $VMS_STATUS_SUCCESS   */
31 
32 #include	<starlet.h>
33 #include	<smgtrmptr.h>	/* to get SMG$K_??? definitions */
34 #include	<smg$routines.h>
35 #include	<ssdef.h>
36 
37 #include	<descrip.h>
38 #include	<iodef.h>
39 #include	<ttdef.h>
40 #include	<tt2def.h>
41 
42 /* function prototypes needed for the dispatch table */
43 static OUTC_DCL vmsvt_putc(int c);
44 static int vmsvt_typahead(void);
45 static void vmsvt_flush(void);
46 static void vmsvt_scrollregion(int top, int bot);
47 static void vmsvt_scroll_reg(int from, int to, int n);
48 static void vmsvt_scroll_delins(int from, int to, int n);
49 
50 /* SMG stuff (just like termcap) */
51 static int already_open;
52 static int termtype;
53 static char *tc_SO;		/* begin standout (reverse) */
54 static char *tc_SE;		/* end standout (reverse) */
55 static char *erase_to_end_line;
56 static char *erase_whole_display;
57 
58 static char *delete_line;
59 static char *insert_line;
60 static char *scroll_forw;
61 static char *scroll_back;
62 static char *scroll_regn;
63 
64 static char *set_narrow;
65 static char *set_wide;
66 
67 static long wide_cols;
68 static long narrow_cols;
69 
70 #if OPT_VIDEO_ATTRS
71 static char *tc_MD;		/* begin bold */
72 static char *tc_ME;		/* end bold */
73 static char *tc_US;		/* begin underscore */
74 static char *tc_UE;		/* end underscore */
75 #endif
76 
77 #if OPT_FLASH
78 static char *dark_on;
79 static char *dark_off;
80 #endif
81 /* *INDENT-OFF* */
82 struct vmskeyseqs {
83     char *seq;
84     int code;
85 };
86 static struct vmskeyseqs vt100seqs[] =
87 {
88     { "\033[A",   KEY_Up },
89     { "\033[B",   KEY_Down },
90     { "\033[C",   KEY_Right },
91     { "\033[D",   KEY_Left },
92     { "\033OP",   KEY_KP_F1 },
93     { "\033OQ",   KEY_KP_F2 },
94     { "\033OR",   KEY_KP_F3 },
95     { "\033OS",   KEY_KP_F4 },
96     /* these are transmitted by xterm and similar programs */
97     { "\033[11~", KEY_F1 },
98     { "\033[12~", KEY_F2 },
99     { "\033[13~", KEY_F3 },
100     { "\033[14~", KEY_F4 },
101     { "\033[15~", KEY_F5 },
102     /* vt220 keys (F1-F5 do not transmit control sequences) include vt100 */
103     { "\033[17~", KEY_F6 },
104     { "\033[18~", KEY_F7 },
105     { "\033[19~", KEY_F8 },
106     { "\033[20~", KEY_F9 },
107     { "\033[21~", KEY_F10 },
108     { "\033[23~", KEY_F11 },
109     { "\033[24~", KEY_F12 },
110     { "\033[25~", KEY_F13 },
111     { "\033[26~", KEY_F14 },
112     { "\033[28~", KEY_F15 },
113     { "\033[29~", KEY_F16 },
114     { "\033[31~", KEY_F17 },
115     { "\033[32~", KEY_F18 },
116     { "\033[33~", KEY_F19 },
117     { "\033[34~", KEY_F20 },
118     { "\033[3~",  KEY_Delete },	/* aka "Remove" */
119     { "\033[6~",  KEY_Next },
120     { "\033[5~",  KEY_Prior },	/* aka "Prev" */
121     { "\033[1~",  KEY_Find },
122     { "\033[2~",  KEY_Insert },
123     { "\033[4~",  KEY_Select },
124     /* 8-bit codes are the same, substituting the first two characters */
125     { "\233A",    KEY_Up },
126     { "\233B",    KEY_Down },
127     { "\233C",    KEY_Right },
128     { "\233D",    KEY_Left },
129     { "\217P",    KEY_KP_F1 },
130     { "\217Q",    KEY_KP_F2 },
131     { "\217R",    KEY_KP_F3 },
132     { "\217S",    KEY_KP_F4 },
133     /* these are transmitted by xterm */
134     { "\233[11~", KEY_F1 },
135     { "\233[12~", KEY_F2 },
136     { "\233[13~", KEY_F3 },
137     { "\233[14~", KEY_F4 },
138     { "\233[15~", KEY_F5 },
139     /* vt220 keys (F1-F5 do not transmit control sequences) include vt100 */
140     { "\23317~",  KEY_F6 },
141     { "\23318~",  KEY_F7 },
142     { "\23319~",  KEY_F8 },
143     { "\23320~",  KEY_F9 },
144     { "\23321~",  KEY_F10 },
145     { "\23323~",  KEY_F11 },
146     { "\23324~",  KEY_F12 },
147     { "\23325~",  KEY_F13 },
148     { "\23326~",  KEY_F14 },
149     { "\23328~",  KEY_F15 },
150     { "\23329~",  KEY_F16 },
151     { "\23331~",  KEY_F17 },
152     { "\23332~",  KEY_F18 },
153     { "\23333~",  KEY_F19 },
154     { "\23334~",  KEY_F20 },
155     { "\2333~",   KEY_Delete },	/* aka "Remove" */
156     { "\2336~",   KEY_Next },
157     { "\2335~",   KEY_Prior },	/* aka "Prev" */
158     { "\2331~",   KEY_Find },
159     { "\2332~",   KEY_Insert },
160     { "\2334~",   KEY_Select },
161 };
162 static struct vmskeyseqs vt52seqs[] =
163 {
164     { "\33A",     KEY_Up },
165     { "\33B",     KEY_Down },
166     { "\33C",     KEY_Right },
167     { "\33D",     KEY_Left },
168     { "\33P",     KEY_KP_F1 },
169     { "\33Q",     KEY_KP_F2 },
170     { "\33R",     KEY_KP_F3 },
171     { "\33S",     KEY_KP_F4 },
172 };
173 /* *INDENT-ON* */
174 
175 typedef struct {
176     USHORT status;		/* I/O completion status */
177     USHORT count;		/* byte transfer count   */
178     int dev_dep_data;		/* device-dependent data */
179 } QIO_SB;			/* This is a QIO I/O Status Block */
180 
181 #define NIBUF	1024		/* Input buffer size            */
182 #define NOBUF	1024		/* MM says big buffers win!     */
183 #define EFN	0		/* Event flag                   */
184 
185 static char obuf[NOBUF];	/* Output buffer                */
186 static int nobuf;		/* # of bytes in above          */
187 
188 static char ibuf[NIBUF];	/* Input buffer                 */
189 static int nibuf;		/* # of bytes in above          */
190 
191 static int ibufi;		/* Read index                   */
192 
193 /*
194  * Public data also used by spawn.c
195  */
196 int oldmode[3];			/* Old TTY mode bits            */
197 int newmode[3];			/* New TTY mode bits            */
198 short iochan;			/* TTY I/O channel              */
199 
200 /***
201  *  Send a string to vmsvt_putc
202  *
203  *  Nothing returned
204  ***/
205 static void
vmsvt_puts(char * string)206 vmsvt_puts(char *string)	/* String to write              */
207 {
208     if (string)
209 	while (*string != EOS)
210 	    vmsvt_putc(*string++);
211 }
212 
213 /***
214  *  2-argument request, e.g., for cursor movement.
215  *
216  *  Nothing returned
217  ***/
218 static void
vmsvt_tgoto(request_code,parm1,parm2)219 vmsvt_tgoto(request_code, parm1, parm2)
220 {
221     char buffer[32];
222     int ret_length;
223     static int max_buffer_length = sizeof(buffer);
224     static int arg_list[3] =
225     {2};
226 
227     register int i;
228 
229     if (!already_open) {
230 	printf("\n");
231 	return;
232     }
233 
234     /* Set the arguments into the arg_list array
235      */
236     arg_list[1] = parm1;
237     arg_list[2] = parm2;
238 
239     if (!$VMS_STATUS_SUCCESS(smg$get_term_data(&termtype,
240 					       &request_code,
241 					       &max_buffer_length,
242 					       &ret_length,
243 					       buffer,
244 					       arg_list))) {
245 	vmsvt_puts("OOPS");
246 	return;
247     } else if (ret_length > 0) {
248 	char *cp = buffer;
249 	while (ret_length-- > 0)
250 	    vmsvt_putc(*cp++);
251     }
252 }
253 
254 static void
vmsvt_move(int row,int col)255 vmsvt_move(int row, int col)
256 {
257     vmsvt_tgoto(SMG$K_SET_CURSOR_ABS, row + 1, col + 1);
258 }
259 
260 #if OPT_VIDEO_ATTRS
261 static void
vmsvt_attr(UINT attr)262 vmsvt_attr(UINT attr)
263 {
264 #define VA_SGR (VASEL|VAREV|VAUL|VAITAL|VABOLD)
265     /* *INDENT-OFF* */
266     static const struct {
267 	char	**start;
268 	char	**end;
269 	UINT	mask;
270     } tbl[] = {
271 	{ &tc_SO, &tc_SE, VASEL|VAREV },
272 	{ &tc_US, &tc_UE, VAUL },
273 	{ &tc_US, &tc_UE, VAITAL },
274 	{ &tc_MD, &tc_ME, VABOLD },
275     };
276     /* *INDENT-ON* */
277 
278     static UINT last;
279 
280     attr = VATTRIB(attr);
281     if (attr & VASPCOL) {
282 	attr = VCOLORATTR((VCOLORNUM(attr) & (NCOLORS - 1)));
283     } else {
284 	attr &= ~(VAML | VAMLFOC);
285     }
286 
287     if (attr != last) {
288 	register size_t n;
289 	register char *s;
290 	UINT diff = attr ^ last;
291 	int ends = FALSE;
292 
293 	/* turn OFF old attributes */
294 	for (n = 0; n < TABLESIZE(tbl); n++) {
295 	    if ((tbl[n].mask & diff) != 0
296 		&& (tbl[n].mask & attr) == 0
297 		&& (s = *(tbl[n].end)) != 0) {
298 		vmsvt_puts(s);
299 #if OPT_COLOR
300 		if (!ends)	/* do this once */
301 		    reinitialize_colors();
302 #endif
303 		ends = TRUE;
304 		diff &= ~(tbl[n].mask);
305 	    }
306 	}
307 
308 	/* turn ON new attributes */
309 	for (n = 0; n < TABLESIZE(tbl); n++) {
310 	    if ((tbl[n].mask & diff) != 0
311 		&& (tbl[n].mask & attr) != 0
312 		&& (s = *(tbl[n].start)) != 0) {
313 		vmsvt_puts(s);
314 		diff &= ~(tbl[n].mask);
315 	    }
316 	}
317 
318 	if (tc_SO != 0 && tc_SE != 0) {
319 	    if (ends && (attr & (VAREV | VASEL))) {
320 		vmsvt_puts(tc_SO);
321 	    } else if (diff & VA_SGR) {		/* we didn't find it */
322 		vmsvt_puts(tc_SE);
323 	    }
324 	}
325 #if OPT_COLOR
326 	if (attr & VACOLOR)
327 	    tcapfcol(VCOLORNUM(attr));
328 	else if (given_fcolor != gfcolor)
329 	    tcapfcol(gfcolor);
330 #endif
331 	last = attr;
332     }
333 }
334 
335 #else /* highlighting is a minimum attribute */
336 
337 /*
338  * Change reverse video status.
339  * FALSE = normal video, TRUE = reverse video
340  */
341 static void
tcaprev(UINT state)342 tcaprev(UINT state)
343 {
344     static int revstate = -1;
345 
346     if (state == revstate)
347 	return;
348     revstate = state;
349     if (state)
350 	vmsvt_puts(tc_SO);
351     else
352 	vmsvt_puts(tc_SE);
353 }
354 
355 #endif /* OPT_VIDEO_ATTRS */
356 
357 static void
vmsvt_eeol(void)358 vmsvt_eeol(void)
359 {
360     vmsvt_puts(erase_to_end_line);
361 }
362 
363 static void
vmsvt_eeop(void)364 vmsvt_eeop(void)
365 {
366     vmsvt_puts(erase_whole_display);
367 }
368 
369 static void
cache_capabilities(void)370 cache_capabilities(void)
371 {
372     /* *INDENT-OFF* */
373     static struct caps_struct {
374 	long  code;
375 	char **cap_string;
376     } tbl[] = {
377 	{ SMG$K_BEGIN_REVERSE,	     &tc_SO },
378 	{ SMG$K_END_REVERSE,	     &tc_SE },
379 #if OPT_VIDEO_ATTRS
380 	{ SMG$K_BEGIN_BOLD,	     &tc_MD },
381 	{ SMG$K_END_BOLD,	     &tc_ME },
382 	{ SMG$K_BEGIN_UNDERSCORE,    &tc_US },
383 	{ SMG$K_END_UNDERSCORE,	     &tc_UE },
384 #endif
385 #if OPT_FLASH
386 	{ SMG$K_DARK_SCREEN,	     &dark_on },
387 	{ SMG$K_LIGHT_SCREEN,	     &dark_off },
388 #endif
389 	{ SMG$K_ERASE_TO_END_LINE,   &erase_to_end_line },
390 	{ SMG$K_ERASE_WHOLE_DISPLAY, &erase_whole_display },
391 	{ SMG$K_INSERT_LINE,	     &insert_line },	/* al */
392 	{ SMG$K_DELETE_LINE,	     &delete_line },	/* dl */
393 	{ SMG$K_SCROLL_FORWARD,	     &scroll_forw },	/* SF */
394 	{ SMG$K_SCROLL_REVERSE,	     &scroll_back },	/* SR */
395 	{ SMG$K_SET_SCROLL_REGION,   &scroll_regn },	/* CS */
396 	{ SMG$K_WIDTH_NARROW,	     &set_narrow },	/* 80 columns */
397 	{ SMG$K_WIDTH_WIDE,	     &set_wide },	/* 132 columns */
398     };
399     /* *INDENT-ON* */
400 
401     char *buf;
402     struct caps_struct *csp;
403     int i, buf_len, ret_len;
404     long rqst_code;
405     static char storage[1024];
406 
407     for (i = 0, buf = storage, csp = tbl; i < TABLESIZE(tbl); i++, csp++) {
408 	buf_len = (storage + sizeof(storage)) - buf;
409 	if (!$VMS_STATUS_SUCCESS(smg$get_term_data(&termtype,
410 						   &csp->code,
411 						   &buf_len,
412 						   &ret_len,
413 						   buf)) || ret_len == 0) {
414 	    *(csp->cap_string) = NULL;
415 	} else {
416 	    buf[ret_len] = 0;
417 	    *(csp->cap_string) = buf;
418 	    buf += ret_len + 1;
419 	}
420     }
421     rqst_code = SMG$K_COLUMNS;
422     if (!$VMS_STATUS_SUCCESS(smg$get_numeric_data(&termtype,
423 						  &rqst_code,
424 						  &narrow_cols))) {
425 	narrow_cols = 80;
426     }
427     rqst_code = SMG$K_WIDE_SCREEN_COLUMNS;
428     if (!$VMS_STATUS_SUCCESS(smg$get_numeric_data(&termtype,
429 						  &rqst_code,
430 						  &wide_cols))) {
431 	wide_cols = 132;
432     }
433     term.maxcols = wide_cols;
434 }
435 
436 /** I/O information block definitions **/
437 struct iosb {			/* I/O status block                     */
438     short i_cond;		/* Condition value                      */
439     short i_xfer;		/* Transfer count                       */
440     long i_info;		/* Device information                   */
441 };
442 struct termchar {		/* Terminal characteristics             */
443     char t_class;		/* Terminal class                       */
444     char t_type;		/* Terminal type                        */
445     short t_width;		/* Terminal width in characters         */
446     long t_mandl;		/* Terminal's mode and length           */
447     long t_extend;		/* Extended terminal characteristics    */
448 };
449 static struct termchar tc;	/* Terminal characteristics             */
450 
451 static void
get_terminal_type(void)452 get_terminal_type(void)
453 {
454     short fd;
455     int status, deassign_status;
456     struct iosb iostatus;
457     $DESCRIPTOR(devnam, "SYS$COMMAND");
458 
459     /* Assign input to a channel */
460     if (!$VMS_STATUS_SUCCESS(status = sys$assign(&devnam, &fd, 0, 0)))
461 	tidy_exit(status);
462 
463     /* Get terminal characteristics */
464     status = sys$qiow(0,	/* Wait on event flag zero      */
465 		      fd,	/* Channel to input terminal    */
466 		      IO$_SENSEMODE,	/* Get current characteristic   */
467 		      &iostatus,	/* Status after operation       */
468 		      0, 0,	/* No AST service               */
469 		      &tc,	/* Terminal characteristics buf */
470 		      sizeof(tc),	/* Size of the buffer           */
471 		      0, 0, 0, 0);	/* P3-P6 unused                 */
472 
473     /* De-assign the input device */
474     if (!$VMS_STATUS_SUCCESS(deassign_status = sys$dassgn(fd)))
475 	tidy_exit(deassign_status);
476 
477     /* Jump out if bad qiow status */
478     if (!$VMS_STATUS_SUCCESS(status))
479 	tidy_exit(status);
480     if ((iostatus.i_cond & 1) == 0)
481 	tidy_exit(iostatus.i_cond);
482 }
483 
484 static void
vmsvt_open(void)485 vmsvt_open(void)
486 {
487     QIO_SB iosb;
488     int status;
489     $DESCRIPTOR(odsc, "SYS$COMMAND");
490 
491     int i, keyseq_tablesize;
492     struct vmskeyseqs *keyseqs;
493 
494     if (!already_open) {
495 	already_open = TRUE;
496 
497 	get_terminal_type();
498 	if (tc.t_type == TT$_UNKNOWN) {
499 	    printf("Terminal type is unknown!\n");
500 	    printf("Set your terminal type using $ SET TERMINAL/INQUIRE\n");
501 	    tidy_exit(3);
502 	}
503 	if (tc.t_type != TT$_VT52) {
504 	    keyseqs = vt100seqs;
505 	    keyseq_tablesize = TABLESIZE(vt100seqs);
506 	} else {
507 	    keyseqs = vt52seqs;
508 	    keyseq_tablesize = TABLESIZE(vt52seqs);
509 	}
510 
511 	/* Access the system terminal definition table for the          */
512 	/* information of the terminal type returned by IO$_SENSEMODE   */
513 	if (!$VMS_STATUS_SUCCESS(smg$init_term_table_by_type(&tc.t_type, &termtype)))
514 	    return;
515 
516 	/* Set sizes */
517 	term.rows = ((UINT) tc.t_mandl >> 24);
518 	term.cols = tc.t_width;
519 
520 	if (term.maxrows < term.rows)
521 	    term.maxrows = term.rows;
522 
523 	if (term.maxcols < term.cols)
524 	    term.maxcols = term.cols;
525 
526 	cache_capabilities();
527 
528 	revexist = (tc_SO != NULL && tc_SE != NULL);
529 	eolexist = erase_whole_display != NULL;
530 
531 	/*
532 	 * I tried 'vmsgetstr()' for a VT100 terminal and it had no codes
533 	 * for insert_line or for delete_line.  (Have to work up a test for
534 	 * that) - TD
535 	 */
536 
537 	if (scroll_regn && scroll_back) {
538 	    if (scroll_forw == NULL)	/* assume '\n' scrolls forward */
539 		scroll_forw = "\n";
540 	    term.scroll = vmsvt_scroll_reg;
541 	} else if (delete_line && insert_line)
542 	    term.scroll = vmsvt_scroll_delins;
543 	else
544 	    term.scroll = nullterm_scroll;
545 
546 	/* Set resolution */
547 	(void) strcpy(screen_desc,
548 		      (term.cols == narrow_cols)
549 		      ? "NORMAL"
550 		      : "WIDE");
551 
552 	/* Open terminal I/O drivers */
553 	status = sys$assign(&odsc, &iochan, 0, 0);
554 	if (status != SS$_NORMAL)
555 	    tidy_exit(status);
556 	status = sys$qiow(EFN, iochan, IO$_SENSEMODE, &iosb, 0, 0,
557 			  oldmode, sizeof(oldmode), 0, 0, 0, 0);
558 	if (status != SS$_NORMAL
559 	    || iosb.status != SS$_NORMAL)
560 	    tidy_exit(status);
561 	newmode[0] = oldmode[0];
562 	newmode[1] = oldmode[1];
563 	newmode[1] |= TT$M_NOBRDCST;	/* turn on no-broadcast */
564 	newmode[1] &= ~TT$M_TTSYNC;
565 	newmode[1] &= ~TT$M_ESCAPE;	/* turn off escape-processing */
566 	newmode[1] &= ~TT$M_HOSTSYNC;
567 	newmode[1] &= ~TT$M_NOTYPEAHD;	/* turn off no-typeahead */
568 	newmode[2] = oldmode[2];
569 	newmode[2] |= TT2$M_PASTHRU;	/* turn on pass-through */
570 	newmode[2] |= TT2$M_ALTYPEAHD;	/* turn on big typeahead buffer */
571 	status = sys$qiow(EFN, iochan, IO$_SETMODE, &iosb, 0, 0,
572 			  newmode, sizeof(newmode), 0, 0, 0, 0);
573 	if (status != SS$_NORMAL
574 	    || iosb.status != SS$_NORMAL)
575 	    tidy_exit(status);
576 	term.rows = (newmode[1] >> 24);
577 	term.cols = newmode[0] >> 16;
578 
579 	/* make sure backspace is bound to backspace */
580 	asciitbl[backspc] = &f_backchar_to_bol;
581 
582 	/* Set predefined keys */
583 	for (i = keyseq_tablesize; i--;)
584 	    addtosysmap(keyseqs[i].seq, strlen(keyseqs[i].seq), keyseqs[i].code);
585     }
586 }
587 
588 /* copied/adapted from 'tcap.c' 19-apr-1993 dickey@software.org */
589 
590 /* move howmany lines starting at from to to */
591 static void
vmsvt_scroll_reg(int from,int to,int n)592 vmsvt_scroll_reg(int from, int to, int n)
593 {
594     int i;
595     if (to == from)
596 	return;
597     if (to < from) {
598 	vmsvt_scrollregion(to, from + n - 1);
599 	vmsvt_move(from + n - 1, 0);
600 	for (i = from - to; i > 0; i--)
601 	    vmsvt_puts(scroll_forw);
602     } else {			/* from < to */
603 	vmsvt_scrollregion(from, to + n - 1);
604 	vmsvt_move(from, 0);
605 	for (i = to - from; i > 0; i--)
606 	    vmsvt_puts(scroll_back);
607     }
608     vmsvt_scrollregion(0, term.rows - 1);
609 }
610 
611 /*
612 OPT_PRETTIER_SCROLL is prettier but slower -- it scrolls
613 		a line at a time instead of all at once.
614 */
615 
616 /* move howmany lines starting at from to to */
617 static void
vmsvt_scroll_delins(int from,int to,int n)618 vmsvt_scroll_delins(int from, int to, int n)
619 {
620     int i;
621     if (to == from)
622 	return;
623     /* patch: should make this more like 'tcap.c', or merge logic somehow */
624 #if OPT_PRETTIER_SCROLL
625     if (absol(from - to) > 1) {
626 	vmsvt_scroll_delins(from, (from < to) ? to - 1 : to + 1, n);
627 	if (from < to)
628 	    from = to - 1;
629 	else
630 	    from = to + 1;
631     }
632 #endif
633     if (to < from) {
634 	vmsvt_move(to, 0);
635 	for (i = from - to; i > 0; i--)
636 	    vmsvt_puts(delete_line);
637 	vmsvt_move(to + n, 0);
638 	for (i = from - to; i > 0; i--)
639 	    vmsvt_puts(insert_line);
640     } else {
641 	vmsvt_move(from + n, 0);
642 	for (i = to - from; i > 0; i--)
643 	    vmsvt_puts(delete_line);
644 	vmsvt_move(from, 0);
645 	for (i = to - from; i > 0; i--)
646 	    vmsvt_puts(insert_line);
647     }
648 }
649 
650 /* cs is set up just like cm, so we use tgoto... */
651 static void
vmsvt_scrollregion(int top,int bot)652 vmsvt_scrollregion(int top, int bot)
653 {
654     vmsvt_tgoto(SMG$K_SET_SCROLL_REGION, top + 1, bot + 1);
655 }
656 
657 /***
658  *  Change screen resolution
659  *
660  *  support these values:   WIDE -> 132 columns, NORMAL -> 80 columns
661  *
662  *  T -> if resolution successfully changed, F otherwise.
663  ***/
664 static int
vmsvt_cres(const char * res)665 vmsvt_cres(const char *res)
666 {
667     char buf[NLINE];
668     int rc = FALSE;
669 
670     if (tc.t_type == TT$_VT52) {
671 	mlforce("[sres not supported for VT52-style terminals]");
672 	return (rc);
673     }
674 
675     strcpy(buf, res);
676     mkupper(buf);
677     if (strcmp(buf, "WIDE") == 0 && set_wide != 0) {
678 	vmsvt_puts(set_wide);
679 	term.cols = wide_cols;
680 	rc = TRUE;
681     } else if (strcmp(buf, "NORMAL") == 0 && set_narrow != 0) {
682 	vmsvt_puts(set_narrow);
683 	term.cols = narrow_cols;
684 	rc = TRUE;
685     } else
686 	mlforce("[invalid sres value (use NORMAL or WIDE)]");
687     if (rc)
688 	newwidth(1, term.cols);
689     return (rc);
690 }
691 
692 static void
vmsvt_close(void)693 vmsvt_close(void)
694 {
695     if (already_open) {
696 	/*
697 	 * Note: this code used to check for errors when closing the output,
698 	 * but it didn't work properly (left the screen set in 1-line mode)
699 	 * when I was running as system manager, so I took out the error
700 	 * checking -- T.Dickey 94/7/15.
701 	 */
702 	int status;
703 	QIO_SB iosb;
704 
705 	if (tc.t_type != TT$_VT52) {
706 	    /*
707 	     * Restore terminal width to previous state (if necessary) and then
708 	     * cleanup as usual.
709 	     */
710 	    if (tc.t_width != term.cols)
711 		vmsvt_cres((tc.t_width == narrow_cols) ? "NORMAL" : "WIDE");
712 	}
713 	vmsvt_flush();
714 	status = sys$qiow(EFN, iochan, IO$_SETMODE, &iosb, 0, 0,
715 			  oldmode, sizeof(oldmode), 0, 0, 0, 0);
716 	if (status != SS$_IVCHAN)
717 	    (void) sys$dassgn(iochan);
718 
719 	already_open = FALSE;
720     }
721 }
722 
723 /***
724  *  Ring the bell
725  *
726  *  Nothing returned
727  ***/
728 static void
vmsvt_beep(void)729 vmsvt_beep(void)
730 {
731 #if OPT_FLASH
732     int hit = 0;
733 
734     if (global_g_val(GMDFLASH) && dark_off != NULL && dark_on != NULL) {
735 	hit = 1;
736 	vmsvt_puts(dark_off);
737 	term.flush();
738 	catnap(200, FALSE);
739 	vmsvt_puts(dark_on);
740     }
741     if (!hit && tc.t_type != TT$_VT52) {
742 	/* *INDENT-OFF* */
743 	static char *seq[][2] = {
744 	    { NULL, NULL },		       /* vtflash = off */
745 	    { VTFLASH_NORMSEQ, VTFLASH_REVSEQ }, /* reverse */
746 	    { VTFLASH_REVSEQ, VTFLASH_NORMSEQ }, /* normal	*/
747 	};
748 	/* *INDENT-ON* */
749 
750 	char *str1, *str2;
751 	int val;
752 
753 	val = global_g_val(GVAL_VTFLASH);
754 	str1 = seq[val][0];
755 	if (str1) {
756 	    str2 = seq[val][1];
757 	    vmsvt_puts(str1);
758 	    term.flush();
759 	    catnap(200, FALSE);
760 	    vmsvt_puts(str2);
761 	    hit = 1;
762 	}
763     }
764     if (!hit)
765 #endif
766 	vmsvt_putc(BEL);
767 }
768 
769 static void
read_vms_tty(int length)770 read_vms_tty(int length)
771 {
772     int status;
773     QIO_SB iosb;
774     int term[2] =
775     {0, 0};
776     unsigned mask = (IO$_READVBLK
777 		     | IO$M_NOECHO
778 		     | IO$M_NOFILTR
779 		     | IO$M_TRMNOECHO);
780 
781     status = sys$qiow(EFN, iochan,
782 		      ((length == 1)
783 		       ? mask
784 		       : mask | IO$M_TIMED),
785 		      &iosb, 0, 0, ibuf, length, 0, term, 0, 0);
786 
787     if (status != SS$_NORMAL)
788 	tidy_exit(status);
789     if (iosb.status == SS$_ENDOFFILE)
790 	tidy_exit(status);
791 
792     nibuf = iosb.count;
793     ibufi = 0;
794 }
795 
796 /*
797  * Read a character from the terminal, performing no editing and doing no echo
798  * at all.  More complex in VMS than almost anyplace else, which figures.
799  */
800 static int
vmsvt_getc(void)801 vmsvt_getc(void)
802 {
803     while (ibufi >= nibuf) {
804 	if (!vmsvt_typahead())
805 	    read_vms_tty(1);
806     }
807     return (ibuf[ibufi++] & 0xFF);	/* Allow multinational  */
808 }
809 
810 /*
811  * Write a character to the display. On VMS, terminal output is buffered, and
812  * we just put the characters in the big array, after checking for overflow.
813  */
814 static OUTC_DCL
vmsvt_putc(int c)815 vmsvt_putc(int c)
816 {
817     if (nobuf >= NOBUF)
818 	vmsvt_flush();
819     obuf[nobuf++] = c;
820     OUTC_RET c;
821 }
822 
823 static int
vmsvt_typahead(void)824 vmsvt_typahead(void)
825 {
826     if (ibufi >= nibuf) {
827 	read_vms_tty(NIBUF);
828 	return (nibuf > 0);
829     }
830     return TRUE;
831 }
832 
833 /*
834  * Flush terminal buffer. Does real work where the terminal output is buffered
835  * up. A no-operation on systems where byte at a time terminal I/O is done.
836  */
837 static void
vmsvt_flush(void)838 vmsvt_flush(void)
839 {
840     QIO_SB iosb;
841 
842     if (nobuf != 0) {
843 	(void) sys$qiow(EFN, iochan, IO$_WRITELBLK | IO$M_NOFORMAT,
844 			&iosb, 0, 0, obuf, nobuf, 0, 0, 0, 0);
845 	nobuf = 0;
846     }
847 }
848 
849 static void
vmsvt_clean(int f)850 vmsvt_clean(int f)
851 {
852     if (f)
853 	term.openup();
854 
855     term.flush();
856     term.close();
857     term.kclose();
858 }
859 
860 static void
vmsvt_unclean(void)861 vmsvt_unclean(void)
862 {
863 }
864 
865 /* Dispatch table. */
866 TERM term =
867 {
868     24,				/* Max number of rows allowable */
869 				/* Filled in */ 0,
870 				/* Current number of rows used  */
871     132,			/* Max number of columns        */
872 				/* Filled in */ 0,
873 				/* Current number of columns    */
874     dumb_set_encoding,
875     dumb_get_encoding,
876     vmsvt_open,			/* Open terminal at the start   */
877     vmsvt_close,		/* Close terminal at end        */
878     nullterm_kopen,		/* Open keyboard                */
879     nullterm_kclose,		/* Close keyboard               */
880     vmsvt_clean,		/* cleanup keyboard             */
881     vmsvt_unclean,		/* uncleanup keyboard           */
882     nullterm_openup,
883     vmsvt_getc,			/* Get character from keyboard  */
884     vmsvt_putc,			/* Put character to display     */
885     vmsvt_typahead,		/* char ready for reading       */
886     vmsvt_flush,		/* Flush output buffers         */
887     vmsvt_move,			/* Move cursor, origin 0        */
888     vmsvt_eeol,			/* Erase to end of line         */
889     vmsvt_eeop,			/* Erase to end of page         */
890     vmsvt_beep,			/* Beep                         */
891 #if OPT_VIDEO_ATTRS
892     vmsvt_attr,			/* Set attribute video state    */
893 #else
894     vmsvt_rev,			/* Set reverse video state      */
895 #endif
896     vmsvt_cres,			/* Change screen resolution     */
897     nullterm_setfore,		/* N/A: Set foreground color    */
898     nullterm_setback,		/* N/A: Set background color    */
899     nullterm_setpal,		/* N/A: Set palette colors      */
900     nullterm_setccol,
901     nullterm_scroll,		/* set at init-time             */
902     nullterm_pflush,
903     nullterm_icursor,
904     nullterm_settitle,
905     nullterm_watchfd,
906     nullterm_unwatchfd,
907     nullterm_cursorvis,
908     nullterm_mopen,
909     nullterm_mclose,
910     nullterm_mevent,
911 };
912 
913 #endif
914