ftp.nice.ch/pub/next/unix/editor/me.3.10.N.bs.tar.gz#/me-3.10.N.bs/src/vms.c

This is vms.c in view mode; [Download] [Up]

/*
 *  Advanced VMS terminal driver
 *
 *  Consults the system's terminal tables for both DEC terminals,
 *  foreign and user defined terminals.
 *
 *  Author:  Curtis Smith
 *  Update history:
 *	14-Jul-1987, first revision.
 *	02-Jan-1988, by J. A. Lomicka: Code addition for interpreting
 *		LK201 function keys, application keypad and cursor
 *		position reports.
 *	09-Apr-1988, second revision.
 *		Major changes:
 *		1) H files removed; replaced with globalvalues.
 *		2) Terminal now left in ECHO mode. Read call
 *		disables echo.
 *		3) TYPAHD macro now performs correctly.
 *		4) $sres variable now accepts NORMAL and WIDE.
 *		5) Writes to screen now use QIO without waiting.
 *		This gives a slight increase in performance.
 *		Had to make some QIOW a different event flag number.
 *		6) Function keys, special keys and arrow keys
 *		are now bound using the following table:
 *			FNa-DOWN FNn-5    FN0-     FNA-F10  FNN-E1
 *			FNb-LEFT FNo-6    FN1-F1   FNB-F11  FNO-E2
 *			FNc-RITE FNp-7    FN2-F2   FNC-F12  FNP-E3
 *			FNd-UP   FNq-8    FN3-F3   FND-F13  FNQ-E4
 *			FNe-PF1  FNr-9    FN4-F4   FNE-F14  FNR-E5
 *			FNf-PF2  FNs-.    FN5-F5   FNF-F15  FNS-E6
 *			FNg-PF3  FNt-ENT  FN6-F6   FNG-F16  FNT-
 *			FNh-PF4  FNu-,    FN7-F7   FNH-F17  FNU-
 *			FNi-0    FNv--    FN8-F8   FNI-F18  FNV-
 *			FNj-1    FNw-     FN9-F9   FNJ-F19  FNW-
 *			FNk-2    FNx-              FNK-F20  FNX-
 *			FNl-3    FNy-              FNL-     FNY-
 *			FNm-4    FNz-              FNM-     FNZ-
 *		See ebind.h for key bindings.
 *	2-dec-88 Curtis Smith
 *	- These have been rebound to the new machine independant bindings
 */
 
/** Standard include files **/
#include <stdio.h>			/* Standard I/O package		*/
#include "estruct.h"			/* Emacs' structures		*/
#include "etype.h"
#include "edef.h"			/* Emacs' definitions		*/
#include	"elang.h"

#if	VMS
/***
 *  nothing  -  The nothing function.
 *
 *  This function is used as a placeholder for unimplemented functions,
 *  or to compile an empty module (which VMS doesn't like).
 *
 *  Nothing returned.
 ***/
nothing()
{
	/* Nothing */
}

#if VMSVT

/** Parameters **/
#define NKEYENT		128		/* Number of keymap entries	*/

/** VMS's include files **/
#include <descrip.h>			/* Descriptor definitions	*/

/** VMS's global values **/
globalvalue IO$M_NOECHO;		/* Suppress echo on read	*/
globalvalue IO$M_NOFORMAT;		/* Suppress formatting on write	*/
globalvalue IO$M_TIMED;			/* Timeout on input		*/
globalvalue IO$M_TYPEAHDCNT;		/* Get typeahead count		*/
globalvalue IO$_READLBLK;		/* Read logical block		*/
globalvalue IO$_SENSEMODE;		/* Sense mode of terminal	*/
globalvalue IO$_SETMODE;		/* Set mode of terminal		*/
globalvalue IO$_WRITELBLK;		/* Write logical block		*/
globalvalue SMG$K_BEGIN_REVERSE;	/* Begin reverse video		*/
globalvalue SMG$K_COLUMNS;		/* Number of columns narrow	*/
globalvalue SMG$K_END_REVERSE;		/* End reverse video		*/
globalvalue SMG$K_ERASE_TO_END_LINE;	/* Erase to end of line		*/
globalvalue SMG$K_ERASE_WHOLE_DISPLAY;	/* Erase entire screen		*/
globalvalue SMG$K_KEY_0;		/* 0 key			*/
globalvalue SMG$K_KEY_1;		/* 1 key			*/
globalvalue SMG$K_KEY_2;		/* 2 key			*/
globalvalue SMG$K_KEY_3;		/* 3 key			*/
globalvalue SMG$K_KEY_4;		/* 4 key			*/
globalvalue SMG$K_KEY_5;		/* 5 key			*/
globalvalue SMG$K_KEY_6;		/* 6 key			*/
globalvalue SMG$K_KEY_7;		/* 7 key			*/
globalvalue SMG$K_KEY_8;		/* 8 key			*/
globalvalue SMG$K_KEY_9;		/* 9 key			*/
globalvalue SMG$K_KEY_COMMA;		/* Comma key			*/
globalvalue SMG$K_KEY_DOWN_ARROW;	/* Down arrow key		*/
globalvalue SMG$K_KEY_ENTER;		/* Enter key			*/
globalvalue SMG$K_KEY_E1;		/* E1 key			*/
globalvalue SMG$K_KEY_E2;		/* E2 key			*/
globalvalue SMG$K_KEY_E3;		/* E3 key			*/
globalvalue SMG$K_KEY_E4;		/* E4 key			*/
globalvalue SMG$K_KEY_E5;		/* E5 key			*/
globalvalue SMG$K_KEY_E6;		/* E6 key			*/
globalvalue SMG$K_KEY_F1;		/* F1 key			*/
globalvalue SMG$K_KEY_F2;		/* F2 key			*/
globalvalue SMG$K_KEY_F3;		/* F3 key			*/
globalvalue SMG$K_KEY_F4;		/* F4 key			*/
globalvalue SMG$K_KEY_F5;		/* F5 key			*/
globalvalue SMG$K_KEY_F6;		/* F6 key			*/
globalvalue SMG$K_KEY_F7;		/* F7 key			*/
globalvalue SMG$K_KEY_F8;		/* F8 key			*/
globalvalue SMG$K_KEY_F9;		/* F9 key			*/
globalvalue SMG$K_KEY_F10;		/* F10 key			*/
globalvalue SMG$K_KEY_F11;		/* F11 key			*/
globalvalue SMG$K_KEY_F12;		/* F12 key			*/
globalvalue SMG$K_KEY_F13;		/* F13 key			*/
globalvalue SMG$K_KEY_F14;		/* F14 key			*/
globalvalue SMG$K_KEY_F15;		/* F15 key			*/
globalvalue SMG$K_KEY_F16;		/* F16 key			*/
globalvalue SMG$K_KEY_F17;		/* F17 key			*/
globalvalue SMG$K_KEY_F18;		/* F18 key			*/
globalvalue SMG$K_KEY_F19;		/* F19 key			*/
globalvalue SMG$K_KEY_F20;		/* F20 key			*/
globalvalue SMG$K_KEY_LEFT_ARROW;	/* Left arrow key		*/
globalvalue SMG$K_KEY_MINUS;		/* Minus key			*/
globalvalue SMG$K_KEY_PERIOD;		/* Period key			*/
globalvalue SMG$K_KEY_PF1;		/* PF1 key			*/
globalvalue SMG$K_KEY_PF2;		/* PF2 key			*/
globalvalue SMG$K_KEY_PF3;		/* PF3 key			*/
globalvalue SMG$K_KEY_PF4;		/* PF4 key			*/
globalvalue SMG$K_KEY_RIGHT_ARROW;	/* Right arrow key		*/
globalvalue SMG$K_KEY_UP_ARROW;		/* Up arrow key			*/
globalvalue SMG$K_SET_CURSOR_ABS;	/* Set cursor			*/
globalvalue SMG$K_WIDE_SCREEN_COLUMNS;	/* Number of columns wide	*/
globalvalue SMG$K_WIDTH_NARROW;		/* Set terminal to narrow mode	*/
globalvalue SMG$K_WIDTH_WIDE;		/* Set terminal to wide mode	*/
globalvalue SS$_TIMEOUT;		/* Status if read timed-out	*/
globalvalue TT2$M_PASTHRU;		/* Passthru mode in t_extend	*/

/** I/O information block definitions **/
struct iosb {				/* I/O status block		*/
	short i_cond;			/* Condition value		*/
	short i_xfer;			/* Transfer count		*/
	long i_info;			/* Device information		*/
};
struct termchar {			/* Terminal characteristics	*/
	char t_class;			/* Terminal class		*/
	char t_type;			/* Terminal type		*/
	short t_width;			/* Terminal width in characters	*/
	long t_mandl;			/* Terminal's mode and length	*/
	long t_extend;			/* Extended characteristics	*/
};
struct tahd {				/* Typeahead count		*/
	short t_count;			/* Number of character waiting	*/
	char t_first;			/* Next character available	*/
	char t_resv1;			/* Reserved by DEC		*/
	long t_resv2;			/* Reserved by DEC		*/
};

/** Type definitions **/
struct keyent {				/* Key mapping entry		*/
	struct keyent * samlvl;		/* Character on same level	*/
	struct keyent * nxtlvl;		/* Character on next level	*/
	char ch;			/* Character			*/
	int code;			/* Resulting keycode		*/
};

/** Command success **/
#define SUCCESS(x)	((x)&1)		/* TRUE if successful		*/
#define FAILURE(x)	(((x)&1)==0)	/* TRUE if unsuccessful		*/

/** Values to manage the screen **/
static int termtype;			/* Handle to pass to SMG	*/
static short channel;			/* Terminal I/O channel		*/
static struct termchar oldmode;		/* Old terminal modes		*/
static struct termchar newmode;		/* New terminal modes		*/
static char * begin_reverse;		/* Begin reverse video		*/
static char * end_reverse;		/* End reverse video		*/
static char * erase_to_end_line;	/* Erase to end of line		*/
static char * erase_whole_display;	/* Erase whole display		*/
static char * width_narrow;		/* Set narrow size screen	*/
static char * width_wide;		/* Set wide size screen		*/
static int narrow_char;			/* Number of characters narrow	*/
static int wide_char;			/* Number of characters wide	*/
static char inbuf[64];			/* Input buffer			*/
static char * inbufh = inbuf;		/* Head of input buffer		*/
static char * inbuft = inbuf;		/* Tail of input buffer		*/
static char outbuf[1024];		/* Output buffer		*/
static char * outbuft = outbuf;		/* Tail of output buffer	*/
static char keyseq[256];		/* Prefix escape sequence table	*/
static struct keyent keymap[NKEYENT];	/* Key map			*/
static struct keyent * nxtkey = keymap;	/* Next free key entry		*/

/** Forward references **/
int vmsopen(), vmsclose(), vmsgetc(), vmsputc(), vmsflush();
int vmsmove(), vmseeol(), vmseeop(), vmsbeep(), vmsrev(), vmscres();

/** Terminal dispatch table **/
TERM term = {
	72 - 1,				/* Max number of rows allowable */
	/* Filled in */ - 1,		/* Current number of rows used	*/
	160,				/* Max number of columns	*/
	/* Filled in */ 0,		/* Current number of columns	*/
	64,				/* Min margin for extended lines*/
	8,				/* Size of scroll region	*/
	100,				/* # times thru update to pause */
	vmsopen,			/* Open terminal at the start	*/
	vmsclose,			/* Close terminal at end	*/
	nothing,			/* Open keyboard		*/
	nothing,			/* Close keyboard		*/
	vmsgetc,			/* Get character from keyboard	*/
	vmsputc,			/* Put character to display	*/
	vmsflush,			/* Flush output buffers		*/
	vmsmove,			/* Move cursor, origin 0	*/
	vmseeol,			/* Erase to end of line		*/
	vmseeop,			/* Erase to end of page		*/
	vmsbeep,			/* Beep				*/
	vmsrev,				/* Set reverse video state	*/
	vmscres				/* Change screen resolution	*/
#if COLOR
	,
	nothing,			/* Set forground color		*/
	nothing				/* Set background color		*/
#endif /* COLOR */
};

/***
 *  vmsmove  -  Move the cursor (0 origin)
 *
 *  vmsmove calls to the SMG run-time library to produce a character
 *  sequence to position the cursor.  If the sequence cannot be made,
 *  a string "OOPS" is produced instead, much like the termcap library
 *  under UNIX.  In the case of "OOPS", the user will soon know that
 *  his terminal entry is incorrect.
 *
 *  Nothing returned.
 ***/
vmsmove(row, column)
int row;				/* Row position			*/
int column;				/* Column position		*/
{
	char buffer[32];
	int rlen, status;
	
	static int code = SMG$K_SET_CURSOR_ABS;
	static int len = sizeof(buffer);
	static int arg[3] = { 2 };

	/* SMG assumes the row/column positions	are 1 based. */
	arg[1] = row + 1;
	arg[2] = column + 1;

	/* Call to SMG for the sequence */
	status = SMG$GET_TERM_DATA(&termtype, &code, &len, &rlen, buffer, arg);
	if (SUCCESS(status)) {
		buffer[rlen] = '\0';
		vmsputs(buffer);
	} else
		vmsputs("OOPS");
}

/***
 *  vmscres  -  Change screen resolution
 *
 *  vmscres changes the screen resolution of the current window.
 *  Allowable sizes are NORMAL and WIDE.
 *
 *  Nothing returned
 ***/
vmscres(value)
char * value;				/* Value to set			*/
{
	int width;

	/* Skip if not supported */
	if (width_wide == NULL || width_narrow == NULL)
		return;

	/* Check value */
	if (strcmp(value, "WIDE") == 0) {
		width = wide_char;
		vmsputs(width_wide);
	} else if (strcmp(value, "NORMAL") == 0) {
		width = narrow_char;
		vmsputs(width_narrow);
	}

	/* Change width */
	oldmode.t_width = newmode.t_width = width;
	newwidth(TRUE, width);

	/* Set resolution variable */
	strcpy(sres, value);
}

/***
 *  vmsrev  -  Set the reverse video status
 *
 *  vmsrev either sets or resets the reverse video state, based on the
 *  boolean argument.  This function is only called if the revexist
 *  boolean variable is set to TRUE.  Otherwise there is no reverse
 *  video available.
 *
 *  Nothing returned.
 ***/
vmsrev(status)
int status;				/* TRUE if setting reverse	*/
{
	vmsputs(status ? begin_reverse : end_reverse);
}

/***
 *  vmseeol  -  Erase to end of line
 *
 *  When this function is called, the lines worth of text after the
 *  cursor is erased.  This function is only called if the eolexist
 *  boolean variable is set to TRUE.  Otherwise the display manager
 *  will produce enough spaces to erase the line.
 *
 *  Nothing returned.
 ***/
vmseeol()
{
	vmsputs(erase_to_end_line);
}

/***
 *  vmseeop  -  Erase to end of page (clear screen)
 *
 *  vmseeop really should be called vmsclear because it really should
 *  be an erase screen function.  When called, this routine will send
 *  the erase entire screen sequence to the output.
 *
 *  Nothing returned.
 ***/
vmseeop()
{
	vmsputs(erase_whole_display);
}

/***
 *  vmsbeep  -  Ring the bell
 *
 *  vmsbeep send a bell character to the output.  It might be possible
 *  in the future to include the NOISY definition and attempt to flash
 *  the screen, perhaps using LIGHT_SCREEN and DARK_SCREEN.
 *
 *  Nothing returned.
 ***/
vmsbeep()
{
	vmsputc('\007');
}

/***
 *  vmsgetstr  -  Get an SMG string capability by name
 *
 *  vmsgetstr attempts to obtain the escape sequence for a particular
 *  job from the SMG library.  Most sequences do not require a parameter
 *  with the sequence, others do.  In order to obtain the definition
 *  without knowing ahead of time whether ornot the definition has a
 *  parameter, we call SMG once with a parameter and if that fails, we
 *  try again without one.  If both attempts fail, we will return the
 *  NULL string.
 *
 *  Storage for the sequence comes from a local pool.
 *
 *  Returns:	Escape sequence
 *		NULL	No escape sequence available
 ***/ 
char * vmsgetstr(code)
int code;				/* Request code			*/
{
	char * result;
	int rlen, status;
	
	static char seq[1024];
	static char * buffer = seq;
	static int len = sizeof(seq);
	static int arg[2] = { 1, 1 };

	/* Get sequence with one parameter */
	status = SMG$GET_TERM_DATA(&termtype, &code, &len, &rlen, buffer, arg);
	if (FAILURE(status)) {
		/* Try again with zero parameters */
		status = SMG$GET_TERM_DATA(&termtype, &code, &len, &rlen, buffer);
		if (FAILURE(status))
			return NULL;
	}

	/* Check for empty result */
	if (rlen == 0)
		return NULL;
	
	/* Save current position so we can return it to caller */
	result = buffer;
	buffer[rlen++] = '\0';
	buffer += rlen;

	/* Return capability to user */
	return result;
}

/***
 *  vmsgetnum  -  Get numerical constant from SMG
 *
 *  vmsgetnum attempts to get a numerical constant from the SMG package.
 *  If the constant cannot be found, -1 is returned.
 ***/
int vmsgetnum(code)
int code;				/* SMG code			*/
{
	int status, result;

	/* Call SMG for code translation */
	status = SMG$GET_NUMERIC_DATA(&termtype, &code, &result);
	return FAILURE(status) ? -1 : result;
}

/***
 *  vmsaddkey  -  Add key to key map
 *
 *  vmsaddkey adds a new escape sequence to the sequence table.
 *  I am not going to try to explain this table to you in detail.
 *  However, in short, it creates a tree which can easily be transversed
 *  to see if input is in a sequence which can be translated to a
 *  function key (arrows and find/select/do etc. are treated like
 *  function keys).  If the sequence is ambiguous or duplicated,
 *  it is silently ignored.
 *
 *  Nothing returned
 ***/
vmsaddkey(code, fn)
int code;				/* SMG key code			*/
int fn;				/* Resulting keycode		*/
{
	char * seq;
	int first;
	struct keyent * cur, * nxtcur;
	
	/* Skip on NULL sequence */
	seq = vmsgetstr(code);
	if (seq == NULL)
		return;
	
	/* If no keys defined, go directly to insert mode */
	first = 1;
	if (nxtkey != keymap) {
		
		/* Start at top of key map */
		cur = keymap;
		
		/* Loop until matches exhast */
		while (*seq) {
			
			/* Do we match current character */
			if (*seq == cur->ch) {
				
				/* Advance to next level */
				seq++;
				cur = cur->nxtlvl;
				first = 0;
			} else {
				
				/* Try next character on same level */
				nxtcur = cur->samlvl;
				
				/* Stop if no more */
				if (nxtcur)
					cur = nxtcur;
				else
					break;
			}
		}
	}
	
	/* Check for room in keymap */
	if (strlen(seq) > NKEYENT - (nxtkey - keymap))
		return;
		
	/* If first character if sequence is inserted, add to prefix table */
	if (first)
		keyseq[(unsigned char) *seq] = 1;
		
	/* If characters are left over, insert them into list */
	for (first = 1; *seq; first = 0) {
		
		/* Make new entry */
		nxtkey->ch = *seq++;
		nxtkey->code = fn;
		
		/* If root, nothing to do */
		if (nxtkey != keymap) {
			
			/* Set first to samlvl, others to nxtlvl */
			if (first)
				cur->samlvl = nxtkey;
			else
				cur->nxtlvl = nxtkey;
		}

		/* Advance to next key */
		cur = nxtkey++;
	}
}

/***
 *  vmscap  -  Get capabilities from VMS's SMG library
 *
 *  vmscap retrives all the necessary capabilities from the SMG
 *  library to operate microEmacs.  If an insufficent number of
 *  capabilities are found for the particular terminal, an error
 *  status is returned.
 *
 *  Returns:	0 if okay, <>0 if error
 ***/
int vmscap()
{
	char * set_cursor_abs;
	int status;
	
	/* Start SMG package */
	status = SMG$INIT_TERM_TABLE_BY_TYPE(&oldmode.t_type, &termtype);
	if (FAILURE(status)) {
		printf(TEXT189);
/*                     "Cannot find entry for terminal type.\n" */
		printf(TEXT190);
/*                     "Check terminal type with \"SHOW TERMINAL\" or\n" */
		printf(TEXT191);
/*                     "try setting with \"SET TERMINAL/INQUIRE\"\n" */
		return 1;
	}
		
	/* Get reverse video */
	begin_reverse = vmsgetstr(SMG$K_BEGIN_REVERSE);
	end_reverse = vmsgetstr(SMG$K_END_REVERSE);
	revexist = begin_reverse != NULL && end_reverse != NULL;
	
	/* Get erase to end of line */
	erase_to_end_line = vmsgetstr(SMG$K_ERASE_TO_END_LINE);
	eolexist = erase_to_end_line != NULL;
	
	/* Get more neat stuff */
	erase_whole_display = vmsgetstr(SMG$K_ERASE_WHOLE_DISPLAY);
	width_wide = vmsgetstr(SMG$K_WIDTH_WIDE);
	width_narrow = vmsgetstr(SMG$K_WIDTH_NARROW);
	narrow_char = vmsgetnum(SMG$K_COLUMNS);
	wide_char = vmsgetnum(SMG$K_WIDE_SCREEN_COLUMNS);
	set_cursor_abs = vmsgetstr(SMG$K_SET_CURSOR_ABS);

	/* Disable resoultion if unreasonable */
	if (narrow_char < 10 || wide_char < 10) {
		width_wide = width_narrow = NULL;
		strcpy(sres, "NORMAL");
	} else
		/* Kludge resolution */
		strcpy(sres, oldmode.t_width == wide_char ? "WIDE" : "NORMAL");

	/* Check for minimal operations */
	if (set_cursor_abs == NULL || erase_whole_display == NULL) {
		printf(TEXT192);
/*                     "The terminal type does not have enough power to run\n" */
		printf(TEXT193);
/*                     "MicroEMACS.  Try a different terminal or check\n" */
		printf(TEXT194);
/*                     "type with \"SHOW TERMINAL\".\n" */
		return 1;
	}
	
	/* Add function keys to keymapping table */
	vmsaddkey(SMG$K_KEY_DOWN_ARROW, 'N');
	vmsaddkey(SMG$K_KEY_LEFT_ARROW, 'B');
	vmsaddkey(SMG$K_KEY_RIGHT_ARROW, 'F');
	vmsaddkey(SMG$K_KEY_UP_ARROW, 'P');
	vmsaddkey(SMG$K_KEY_PF1, '1');
	vmsaddkey(SMG$K_KEY_PF2, '2');
	vmsaddkey(SMG$K_KEY_PF3, '3');
	vmsaddkey(SMG$K_KEY_PF4, '4');
	vmsaddkey(SMG$K_KEY_0, ALTD | '0');
	vmsaddkey(SMG$K_KEY_1, ALTD | '1');
	vmsaddkey(SMG$K_KEY_2, ALTD | '2');
	vmsaddkey(SMG$K_KEY_3, ALTD | '3');
	vmsaddkey(SMG$K_KEY_4, ALTD | '4');
	vmsaddkey(SMG$K_KEY_5, ALTD | '5');
	vmsaddkey(SMG$K_KEY_6, ALTD | '6');
	vmsaddkey(SMG$K_KEY_7, ALTD | '7');
	vmsaddkey(SMG$K_KEY_8, ALTD | '8');
	vmsaddkey(SMG$K_KEY_9, ALTD | '9');
	vmsaddkey(SMG$K_KEY_PERIOD, SHFT|'.');
	vmsaddkey(SMG$K_KEY_ENTER, SHFT|CTRL|'M');
	vmsaddkey(SMG$K_KEY_COMMA, SHFT|',');
	vmsaddkey(SMG$K_KEY_MINUS, SHFT|'-');
	vmsaddkey(SMG$K_KEY_F1, '1');
	vmsaddkey(SMG$K_KEY_F2, '2');
	vmsaddkey(SMG$K_KEY_F3, '3');
	vmsaddkey(SMG$K_KEY_F4, '4');
	vmsaddkey(SMG$K_KEY_F5, '5');
	vmsaddkey(SMG$K_KEY_F6, '6');
	vmsaddkey(SMG$K_KEY_F7, '7');
	vmsaddkey(SMG$K_KEY_F8, '8');
	vmsaddkey(SMG$K_KEY_F9, '9');
	vmsaddkey(SMG$K_KEY_F10, '0');
	vmsaddkey(SMG$K_KEY_F11, SHFT | '1');
	vmsaddkey(SMG$K_KEY_F12, SHFT | '2');
	vmsaddkey(SMG$K_KEY_F13, SHFT | '3');
	vmsaddkey(SMG$K_KEY_F14, SHFT | '4');
	vmsaddkey(SMG$K_KEY_F15, SHFT | '5');
	vmsaddkey(SMG$K_KEY_F16, SHFT | '6');
	vmsaddkey(SMG$K_KEY_F17, SHFT | '7');
	vmsaddkey(SMG$K_KEY_F18, SHFT | '8');
	vmsaddkey(SMG$K_KEY_F19, SHFT | '9');
	vmsaddkey(SMG$K_KEY_F20, SHFT | '0');
	vmsaddkey(SMG$K_KEY_E1, CTRL | 'S');
	vmsaddkey(SMG$K_KEY_E2, 'C');
	vmsaddkey(SMG$K_KEY_E3, 'D');
	vmsaddkey(SMG$K_KEY_E4, ALTD | 'S');
	vmsaddkey(SMG$K_KEY_E5, 'Z');
	vmsaddkey(SMG$K_KEY_E6, 'V');

	/* Everything okay */
	return 0;
}

/***
 *  vmsgtty - Get terminal type from system control block
 *
 *  vmsgtty obtains the terminal's information such as flags modes,
 *  baud rate, size etc. and stores it in the structure block.
 *  If the block cannot be obtainedm, an error condition is returned.
 *
 *  Returns:	Status
 ***/
int vmsgtty(tc)
struct termchar * tc;			/* Terminal characteristics	*/
{
	int status;
	struct iosb io;
	
	/* Get terminal characteristics */
	status = SYS$QIOW(1, channel, IO$_SENSEMODE, &io, 0, 0,
		tc, sizeof(*tc), 0, 0, 0, 0);
	
	/* Check status */
	return FAILURE(status) && FAILURE(io.i_cond);
}

/***
 *  vmsstty - Set terminal control block
 *
 *  vmsstty takes a previous vmsgtty with modifications, and stores
 *  this as the current terminal control block.
 *
 *  Returns:	Status
 ***/
int vmsstty(tc)
struct termchar * tc;			/* Terminal characteristics	*/
{
	int status;
	struct iosb io;
	
	/* Set terminal characteristics */
	status = SYS$QIOW(1, channel, IO$_SETMODE, &io, 0, 0,
		tc, sizeof(*tc), 0, 0, 0, 0);
	
	/* Check status */
	return FAILURE(status) && FAILURE(io.i_cond);
}

/***
 *  vmsclose  -  Close the connection to the terminal
 *
 *  vmsclose resets the terminal to the original state and cuts the
 *  connection.  No further operations should be done after closing.
 *
 *  Nothing returned.
 ***/
vmsclose()
{
	/* Flush pending output */
	vmsflush();

	/* Do this stupid thing for synchronization */
	SYS$QIOW(1, channel, IO$_WRITELBLK | IO$M_NOFORMAT,
		0, 0, 0, " \b", 2, 0, 0, 0, 0);

	/* Reset terminal to original modes */
	vmsstty(&oldmode);
	
	/* Release channel */
	SYS$DASSGN(channel);
}

/***
 *  vmsopen  -  Get terminal type and open terminal
 *
 *  Nothing returned
 ***/
vmsopen()
{
	$DESCRIPTOR(name, "TT");
	int status;
	
	/* Open channel to terminal */
	status = SYS$ASSIGN(&name, &channel, 0, 0);
	if (FAILURE(status)) {
		printf(TEXT195);
/*                     "Cannot open channel to terminal.\n" */
		exit(1);
	}
	
	/* Get terminal type */
	if (vmsgtty(&oldmode)) {
		printf(TEXT196);
/*                     "Cannot obtain terminal settings.\n" */
		goto error;
	}
	
	/* Get SMG */
	if (vmscap())
		goto error;
	
	/* Set sizes */
	term.t_nrow = ((unsigned int) oldmode.t_mandl >> 24) - 1;
	term.t_ncol = oldmode.t_width;

	/* Set new terminal modes */
	newmode = oldmode;
	newmode.t_extend |= TT2$M_PASTHRU;
	if (vmsstty(&newmode)) {
		printf(TEXT197);
/*                     "Cannot modify terminal settings.\n" */
		goto error;
	}

	/* Finished! */
	return;

	/* Make sure terminal deassigned */	
error:
	/* Release channel */
	SYS$DASSGN(channel);
	exit(1);
}

/***
 *  vmsflush  -  Flush output buffer
 *
 *  vmsflush causes all queued output characters to be written to the
 *  terminal's screen.  We will use SYS$QIO because we don't need to
 *  wait and because it dramaticly increases performance.
 *
 *  Nothing returned.
 ***/
vmsflush()
{
	int len;
	
	/* Compute size of request */
	len = outbuft - outbuf;
	
	/* Skip if zero */
	if (len) {
		SYS$QIO(0, channel, IO$_WRITELBLK | IO$M_NOFORMAT,
			0, 0, 0, outbuf, len, 0, 0, 0, 0);
			
		/* Reset buffer positions */
		outbuft = outbuf;
	}
}

/***
 *  vmsputc  -  Send a character to the screen
 *
 *  vmsputc queues character into a buffer for screen output.  When the
 *  buffer is full the flush routine is called.  This is help speed
 *  things up by avoiding millions of system calls.
 *
 *  Nothing returned.
 ***/
vmsputc(ch)
char ch;				/* Character to add		*/
{
	/* Check for overflow */
	if (outbuft == &outbuf[sizeof(outbuf)])
		vmsflush();
		
	/* Add character to buffer */
	*outbuft++ = ch;
}

/***
 *  vmsputs  -  Send a string to vmsputc
 *
 *  vmsputs is a short-cut routine to handle sending a string of characters
 *  to the character output routine.  A check is made for a NULL string,
 *  while is considered valid.  A NULL string will produce no output.
 *
 *  Nothing returned.
 ***/
vmsputs(string)
char * string;				/* String to write		*/
{
	if (string)
		while (*string)
			vmsputc(*string++);
}

/***
 *  vmsgchar  -  Get character directly from VMS
 *
 *  vmsgchar is the lowest level of retrieving character from VMS.
 *  The argument timed specifies where to wait for a character for
 *  a short period (1 > wait >= 2 seconds) or indefinately.  The short
 *  period version is used when obtaining a escape sequence.
 *
 *  Returns:  character or 0 if no character is available.
 ***/
char vmsgchar(timed)
int timed;				/* TRUE if being timed		*/
{
	char ch;
	int status, op;
	struct iosb io;
	
	/* Make operation */
	op = (timed ? IO$M_TIMED : 0) | IO$_READLBLK | IO$M_NOECHO;
	
	/* Get next character */
	status = SYS$QIOW(1, channel, op, &io, 0, 0, &ch, 1, 2, 0, 0, 0);
		
	/* Fatal error */
	if (FAILURE(status) || FAILURE(io.i_cond)) {

		/* Check for time-out */
		if (io.i_cond == SS$_TIMEOUT)
			return 0;

		/* Real I/O error occured */
		printf(TEXT198, status, io.i_cond);
/*                     "I/O error (%d,%d)\n" */
		SYS$DASSGN(channel);
		exit(1);
	}
	
	/* Return next character */
	return ch;
}

/***
 *  vmsqin  -  Queue character for input
 *
 *  vmsqin queues the character into the input buffer for later
 *  reading.  This routine will mostly be used by mouse support
 *  and other escape sequence processing.
 *
 *  Nothing returned.
 ***/
vmsqin(ch)
char ch;				/* Character to add		*/
{
	/* Check for overflow */
	if (inbuft == &inbuf[sizeof(inbuf)]) {
		
		/* Annoy user */
		vmsbeep();
		return;
	}
	
	/* Add character */
	*inbuft++ = ch;
}

/***
 *  vmsgcook  -  Get characters from input device
 *
 *  vmsgcook "cooks" input from the input device and places them into
 *  the input queue.
 *
 *  Nothing returned.
 ***/
vmsgcook()
{
	char ch;
	struct keyent * cur;
	
	/* Get first character untimed */
	ch = vmsgchar(0);
	vmsqin(ch);
	
	/* Skip if the key isn't a special leading escape sequence */
	if (keyseq[(unsigned char) ch] == 0)
		return;
		
	/* Start translating */
	cur = keymap;
	while (cur) {
		if (cur->ch == ch) {
			/* Is this the end */
			if (cur->nxtlvl == NULL) {
				/* Replace all character with new sequence */
				inbuft = inbuf;
				vmsqin(0);
				vmsqin(cur->code);
				return;
			} else {
				/* Advance to next level */
				cur = cur->nxtlvl;
			
				/* Get next character, timed */
				ch = vmsgchar(1);
				if (ch == '\0')
					return;

				/* Queue character */
				vmsqin(ch);
			}
		} else
			/* Try next character on same level */
			cur = cur->samlvl;
	}
}

/***
 *  vmsgetc  -  Get a character
 *
 *  vmsgetc obtains input from the character input queue.  If the queue
 *  is empty, a call to vmsgcook() is called to fill the input queue.
 *
 *  Returns:	character
 ***/
int vmsgetc()
{
	char ch;

	/* Loop until character found */
	while (1) {
	
		/* Get input from buffer, if available */
		if (inbufh != inbuft) {
			ch = *inbufh++;
			if (inbufh == inbuft)
				inbufh = inbuft = inbuf;
			break;
		} else
	
			/* Fill input buffer */
			vmsgcook();
	}
	
	/* Return next character */
	return (int) ch;
}

#if FLABEL
/***
 *  fnclabel  -  Label function keys
 *
 *  Currently, VMS does not have function key labeling.
 *
 *  Returns:	status.
 ***/
int fnclabel(flag, n)
int flag;				/* TRUE if default		*/
int num;				/* Numerical argument		*/
{
	/* On machines with no function keys...don't bother */
	return TRUE;
}
#endif /* FLABEL */

/***
 *  spal  -  Set palette type
 *
 *  spal sets the palette colors for the 8 colors available.  Currently,
 *  there is nothing here, but some DEC terminals, (VT240 and VT340) have
 *  a color palette which is available under the graphics modes.
 *  Further, a foreign terminal could also change color registers.
 *
 *  Nothing returned
 ***/
spal()
{
	/* Nothing */
}

#endif /* VMSVT */

#if TYPEAH
/***
 *  typahead  -  Check for pending input
 *
 *  typahead check the input buffer for pending input.  If input exists
 *  TRUE is returned.  This routine is used mostly by the display
 *  update routine to avoid redrawing the entire display when it
 *  doesn't need to do so.
 *
 *  Returns:  boolean.
 ***/
int typahead()
{
	int status;
	struct iosb io;
	struct tahd tbuf;

	/* Check for buffered characters */
	if (inbufh != inbuft)
		return TRUE;

	/* Call to system for character count */
	status = SYS$QIOW(1, channel, IO$_SENSEMODE | IO$M_TYPEAHDCNT,
		&io, 0, 0, &tbuf, sizeof(tbuf), 0, 0, 0, 0);
	if (FAILURE(status) || FAILURE(io.i_cond))
		return FALSE;
	return tbuf.t_count;
}
#endif /* TYPEAH */

/***
 *  vmsdcl  -  Execute or invoke a DCL
 *
 *  vmsdcl without an argument will invoke a new DCL as a new process
 *  and attach to it.  When the DCL returns, emacs will regain control.
 *  With an argument, the DCL starts and begins executing the command
 *  line.
 *
 *  Returns:  status.
 ***/
int vmsdcl(command)
char * command;				/* Command to execute		*/
{
	int result, status;
	struct dsc$descriptor desc, * dp;
	
	/* Set up descriptor */
	if (command) {
		desc.dsc$a_pointer = command;
		desc.dsc$w_length = strlen(command);
		desc.dsc$b_dtype = DSC$K_DTYPE_T;
		desc.dsc$b_class = DSC$K_CLASS_S;
		dp = &desc;
	} else 
		dp = NULL;
	
	/* Turn off raw input */
	vmsstty(&oldmode);

	/* Call DCL */
	status = LIB$SPAWN(dp, 0, 0, 0, 0, 0, &result, 0, 0, 0);

	/* Return to raw input */
	vmsstty(&newmode);
	
	/* Return status */
	return SUCCESS(status) && SUCCESS(result);
}		

/***
 *  spawncli  -  Spawn a new DCL
 *
 *  spawncli reliquishes control of the terminal and passes it on to
 *  a newly created DCL process.  When the DCL process finishes, the
 *  screen is redrawen and emacs regains control.
 *
 *  Returns:  status
 ***/
int spawncli(flag, num)
int flag;				/* TRUE if default		*/
int num;				/* Numerical argument		*/
{
	/* Restrict usage */
	if (restflag)
		return resterr();
		
	/* Move to last line and announce */
	vmsmove(term.t_nrow, 0);
	vmsputs(TEXT199);
/*              "[Starting DCL]\r\n" */
	vmsflush();

	/* Redraw screen on return */	
	sgarbf = TRUE;
	
	/* Start command */
	return vmsdcl(NULL);
}

/***
 *  spawn  -  Spawn a command
 *
 *  spawncli reliquishes control of the terminal and passes it on to
 *  a newly created DCL process.  When the DCL process finishes, the
 *  screen is redrawen and emacs regains control.
 *
 *  Returns:  status
 ***/
int spawn(flag, num)
int flag;				/* TRUE if default		*/
int num;				/* Numerical argument		*/
{
	char line[NLINE];
	int status;
	
	/* Restrict usage */
	if (restflag)
		return resterr();
		
	/* Ask for command */
	status = mlreply("!", line, sizeof(line));
	if (status != TRUE)
		return status;
		
	/* Move to last line and announce */
	vmsmove(term.t_nrow, 0);
	vmsputs(TEXT200);
/*              "[Calling DCL]\r\n" */
	vmsflush();
	
	/* Run command */
	status = vmsdcl(line);
	
	/* Wait for conformation */
	vmsputs(TEXT6);
/*              "\r\n\n[END]" */
	vmsflush();
	vmsgchar();

	/* Redraw screen on return */	
	sgarbf = TRUE;
	return status;
}

/***
 *  execprg  -  Execute a program without using command interpretter
 *
 *  execprg is the same as spawn because there is no reasonable way
 *  of avoiding the command interpretter.  This routine is mostly for
 *  smaller systems.
 *
 *  Returns:  status
 ***/
int execprg(flag, num)
int flag;				/* TRUE if default		*/
int num;				/* Numerical argument		*/
{
	return spawn(flag, num);
}

/***
 *  pipecmd   -  Pipe a one line command into a window
 *
 *  pipecmd take a single command and places output into a buffer.
 *  This is accomplished on non-UNIX machines by sending the result
 *  of the command interpretter into a temporary file, then reading
 *  the file into the buffer.
 *
 *  This command is not implemented current, but could be implemented
 *  and will be once I get some manuals.
 *
 *  Returns:  status
 ***/
int pipecmd(flag, num)
int flag;				/* TRUE if default		*/
int num;				/* Numerical argument		*/
{
	mlwrite(TEXT201);
/*              "[Not available yet under VMS]" */
	return FALSE;
}

/***
 *  filter  -  Filter buffer through an external command
 *
 *  filter take a buffer and pipes into a program.  This is accomplished
 *  under non-UNIX systems by writing the buffer to a temporary  file
 *  and running the command with the temporary file as input and another
 *  temporary file as output.  Once the command completes, the buffer
 *  is replaced by the contents of the second temporary file and
 *  all temporary files are removed.
 *
 *  This command is currently not implemented, but can be implemented
 *  and will be once I get the manuals.
 *
 *  Returns:  status.
 ***/
int filter(flag, num)
int flag;				/* TRUE if default		*/
int num;				/* Numerical argument		*/
{
	mlwrite(TEXT201);
/*              "[Not available yet under VMS]" */
	return FALSE;
}

/* return a system dependant string with the current time */

char *PASCAL NEAR timeset()

{
	register char *sp;	/* temp string pointer */
	char buf[16];		/* time data buffer */
	extern char *ctime();

	time(buf);
	sp = ctime(buf);
	sp[strlen(sp)-1] = 0;
	return(sp);
}
#else
vmshello()
{
}
#endif

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.