From:	SMTP%"crobw@cc.curtin.edu.au"  6-JUL-1993 09:03:59.98
To:	EVERHART
CC:	
Subj:	Re: Pseudo-Terminal (PTY) Code Needed

X-Newsgroups: comp.os.vms
From: crobw@cuppa.curtin.edu.au (Robert Wright)
Subject: Re: Pseudo-Terminal (PTY) Code Needed
Message-Id: <1993Jul5.023105.12391@cujo.curtin.edu.au>
Lines: 1072
Sender: news@cujo.curtin.edu.au (News Manager)
Reply-To: crobw@cc.curtin.edu.au
Organization: Curtin University Computing Centre
Date: Mon, 5 Jul 1993 02:31:05 GMT
To: Info-VAX@kl.sri.com
X-Gateway-Source-Info: USENET


Here is a summary of reponses to my, perhaps, vague query on PTYs, etc.

	Has anyone got source code for allocating/assigning a pseudo-terminal,
	and for reading/writing from/to it?

	At least that's what I think I need...

	The application has some permanent LAT terminals (some 50 or so).
	Sometimes a supervisor process needs to control these. Initially it
	needs to log them in. It should probably then detach itself from the
	pseudo-terminal, so that a real user can access the (captive)
	sub-application which starts up automagically. At some later time the
	supervisor process may need to interrupt a (probably idle) session on
	one or more of these terminals, do some housekeeping, and/or log them
	out.

	Any ideas? 

There were two fairly unkind flamings, a helpful translation of my Martian
dialect, some really useful code, and some history. My special thanks to Mike
Wright, and Nigel Arnot.

Responses follow, bracketted by ">>>>>>>>>>>>>>>>>> From:" 
                                ">>>>>>>>>>>>>>>>>> End From: "
--------------------------------------------------------------------------------
Robert J. Wright, VMS Systems Group, Curtin University, Western Australia
PSI%050529452300030::crobw
Phone:+61 9 351 7385  FAX:09-351-2673  Telex:AA-92983 "CURTIN"
--------------------------------------------------------------------------------


>>>>>>>>>>>>>>>>>> From: Jerry Leichter <leichter@lrw.com>

Yes.  Describe your current setup and what you are trying to accomplish.

This has got to win the prize for the most incomprehensible mishmash of
problem description, hypothetical design, use of magic words (pseudo-terminal,
detach, captive sub-application), etc., that I can recall seeing on INFO-VAX.
We get our share of messages from people whose native language isn't English;
or who, from the evidence of their writing, appear to have no native language
at all; and of course the MS/DOS or Unix users who ask long questions in words
that might make sense in their worlds but make no sense in VMS terms.  What's
unique about this message is that it's in reasonable English; it's just that
the English makes no sense.

Perhaps it's some strange Martian dialect.  Has Perth been invaded?  :-)

							-- Jerry

>>>>>>>>>>>>>>>>>> End From: Jerry Leichter <leichter@lrw.com>

>>>>>>>>>>>>>>>>>> From: carl@SOL1.GPS.CALTECH.EDU (Carl J Lydick)

It sounds like maybe he wants to create an interactive process associated with a
virtual terminal, have the process creates that process set up the environment,
then issue a DISCONNECT command from that process, leaving a disconnected
process still associated with the virtual terminal so that a user could then
log in and connect to that virtual terminal.
--------------------------------------------------------------------------------
Carl J Lydick | INTERnet: CARL@SOL1.GPS.CALTECH.EDU | NSI/HEPnet: SOL1::CARL

Disclaimer:  Hey, I understand VAXen and VMS.  That's what I get paid for.  My
understanding of astronomy is purely at the amateur level (or below).  So
unless what I'm saying is directly related to VAX/VMS, don't hold me or my
organization responsible for it.  If it IS related to VAX/VMS, you can try to
hold me responsible for it, but my organization had nothing to do with it.

>>>>>>>>>>>>>>>>>> End From: carl@SOL1.GPS.CALTECH.EDU (Carl J Lydick)

>>>>>>>>>>>>>>>>>> From: m3047@halcyon.com (Fred Morris)

carl@SOL1.GPS.CALTECH.EDU (Carl J Lydick) writes:

>It sounds like maybe he wants to create an interactive process associated with a
>virtual terminal, have the process creates that process set up the environment,
>then issue a DISCONNECT command from that process, leaving a disconnected
>process still associated with the virtual terminal so that a user could then
>log in and connect to that virtual terminal.

It might be a good thing in the interest of clarity if the original poster
tried this and then critiqued it.  I have to also agree that the original
post parses but the semantics are unclear;  whether this is because the
poster didn't do their homework,  or just wishes that magically somebody
is going to say "oh yeah, I have this great device driver that I wrote on
my company's time after they paid for my device driver class and I've just
been waiting to give it away for FREE, and not only that, but my buddies at
DEC will support it" -- I just can't say for sure.

To the original poster:  do you have any idea what the security
ramifications are of what you want? Do you know what CTERM is? Are you
asking for a LAT client (the part that normally runs on a LAT terminal
server) that will run on a VAX?  Do you know enough to configure your LAT
terminals as Remotes (as for printers), and then run CLI-free detached jobs?

Finally: one good reason *why* you think somebody else might have wanted
to do what you're proposing *in* the way you're proposing it; people do
breakthroughs or have jobs that watch mailboxes or locks with queued ASTs.
That's too much work, is it?

There was at one time an unsupported "PTYDRIVER" that did something along
those lines. To the best of my knowledge, it's defunct, because people
have generally concluded that there are better ways to do this. Again: do
you have a sound architectural/design reason for this, or are you just
backed into a corner?

Fred Morris
m3047@halcyon.com

>>>>>>>>>>>>>>>>>> End From: m3047@halcyon.com (Fred Morris)


>>>>>>>>>>>>>>>>>> From:	IN%"MCW@pine.circa.ufl.edu"  "WRIGHT" 15-JUN-1993 19:26:06.63

Rob:

Here's a module I use in an application we run here on our
VAXes.  It might or might not be what you're looking for.
I think I've incuded all the non-standard routines that it
calls.

Sorry about your other response.  There seems to be a couple
of idiots on the VMS list.

Mike Wright
University of Florida
mcw@pine.circa.ufl.edu


============================================================================
/*
**	File:  pty-vms.c
**
**	This files contains functions to start and control a
**	process using the VMS pseudo terminal facility.  This
**	file contains these public functions:
**
**	PtyStart	creates pseudo terminal
**	PtySpawn	spawns a subprocess for pseuod terminal
**	PtyRead		reads data from subprocess pseudo terminal
**	PtyReadText	reads text data only
**	PtyPeek		lets caller peek at next available byte
**	PtyClear	clear out all input
**	PtyWrite	writes data to subprocess pseudo terminal
**	PtyStop		stops subprocess
**	PtySignal	sets signal for incoming data
**
**	Some of the functions in this file call some non-standard
**	functions that are coded in other modules.
**
**
**	Written by:
**
**	Mike Wright
**	Center for Instructional and Research Computing Activities (CIRCA)
**	University of Florida
**	Gainesville, Florida
**	
*/


#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ssdef.h>
#include <descrip.h>
#include <clidef.h>

/* This global variable set if the subprocess is running */

int PtyActive = 0;

/*	misc. static variables used in this module */

static short int channel = 0;		/* pty channel */
static int child_pid = 0;		/* PID of child process */
static int child_status = 0;		/* status flag for subprocess */
static (*signal_function)() = NULL;	/* signal function */

/*
**	This is the format of the special page-aligned buffers
**	used by the pseudo terminal driver.
*/

struct buffer {
   short int status;			/* status word */
   short int length;			/* length word */
   char data[508];			/* data area */
};

static struct buffer *inbuf, *outbuf, *echobuf;

/* this structure is to maintain a rotating queue of input buffers */

static struct bq {
   struct bq *next;			/* to next queue element */
   struct buffer *buffer;		/* ptr to pseudo term buffer */
   int bytes_unread;			/* unread bytes in buffer */
   char *next_byte;			/* next unread byte */
} *free = NULL, *head = NULL, *tail = NULL, bq_array[6];


/*======================================================================*/
/*
**	PtyStart()
**
**	PtyStart() must be called first.  A pty device is created
**	for subsequent sub processes.
**
**	Return value:
**
**	1 	pseudo terminal creation sucessful
**	0	not
*/
/*======================================================================*/

int PtyStart(command)
char *command;				/* command to execute */
{

   int temp, status, result = 0, i;
   char *buffers, *inadr[2];

   int charbuff[3]; 

   if (ttchar (&charbuff, 12)) {
      temp = 7;

      /* get memory in pages for pseudo terminal driver use */

      status = lib$get_vm_page (&temp, &buffers);

      if (status == SS$_NORMAL) {

         /* set up funny pseudo terminal driver buffers as required */

         outbuf = buffers;
         echobuf = buffers+512;
         inbuf = buffers + 1024;

         /* set up pool of free buffers for input */

         for (i=0; i<4; i++) {
            bq_array[i].buffer = inbuf+(i*512);
            bq_array[i].next = free;
            free = &bq_array[i];
         }

         inadr[0] = buffers;
         inadr[1] = buffers + (6*512);

         /* create a pseudo terminal device */

         status = ptd$create (&channel, 0, &charbuff, 12, 
            0, 0, 0, &inadr);

         /* if all OK, spawn the subprocess */

         if (status == SS$_NORMAL) {
               start_read();
               result = 1;
         }
      }
   }

   return (result);

}

/*======================================================================*/
/*
**	PtySpawn()
**
**	Spawns a subprocess for the pseudo terminal already
**	created.
*/
/*======================================================================*/

static spawn_ast (ast_arg)
int ast_arg;				/* ast parameter (not used) */
{
   PtyActive = 0;			/* set child not running */
   sys$wake(0,0);			/* wake us up */

   /* call caller's action routine if there is one */

   if (signal_function != NULL) {
      signal_function();
   }

   return (1);				/* return to main code */
}

PtySpawn (command)
char *command;			/* command for spawned process */
{
   int status, flags, result;
   int command_desc[2], terminal_desc[2];
   char * devname(), terminal[16];

   /* convert pseudo device channel to terminal name */

   strcpy (terminal, devname(channel));

   /* redefine SYS$INPUT to pseudo terminal device */

   crelnm ("LNM$PROCESS", "SYS$INPUT", terminal);

   /* make a descriptor for the terminal name and command */

   strdesc (&terminal_desc, terminal);
   strdesc (&command_desc, command);

   /* set flags to avoid command redefinition */

   flags = CLI$M_NOWAIT | CLI$M_NOCLISYM | CLI$M_NOLOGNAM;

   status = lib$spawn (&command_desc, 
	0, &terminal_desc,
	&flags, 0, &child_pid, &child_status, 0, 
        spawn_ast, 0, 
        0, 0);

   if (status == SS$_NORMAL) {
      result = 1;
      PtyActive = 1;
   }

   else {
      result = 0;
      fprintf (stderr, "Failed to spawn subprocess\n");
      fprintf (stderr, "Status is %d\n", status);
   }

   return (result);

}


/*======================================================================*/
/*
**	PtyRead()
**
**	Read data from child process.
**
**	Return values:
**
**	+n	number of bytes copied to buffer
**	0	time out
**	-1	Process no longer active
*/
/*======================================================================*/

int PtyRead (buffer, buffer_size, seconds)
char *buffer;				/* buffer for input */
int buffer_size;			/* size of buffer */
int seconds;				/* timeout for read */
{
   int l, result;

   result = PtyActive ? 0 : -1;		/* set default result */

   /* see if there's any data available */

   if (wait_for_read(seconds)) {
      l = head->bytes_unread;		/* this is bytes still in buffer */

      /* move to user buffer, but don't overrun it */

      if (l>buffer_size) l = buffer_size;
      memcpy (buffer, head->next_byte, l);

      /* update input pointers and lengths */

      head->next_byte += l;
      head->bytes_unread -= l;

      /* if no more unread bytes, put buffer back, start reads */

      if (head->bytes_unread == 0) remove_buffer();
      result = l;
   }

   return(result);			/* return bytes read to user */

}

/*======================================================================*/
/*
**	PtyReadText()
**
**	Almost the same as PtyRead(), but this function will return
**	*only* text, no control characters.
**
**	Return values:
**
**	+n	number of bytes copied to buffer
**	0	time out, or only non-text available
**	-1	Process no longer active
*/
/*======================================================================*/

int PtyReadText (buffer, buffer_size, seconds)
{
   int l;
   char byte;

   l = PtyActive ? 0 : -1;		/* set default result */

   /* check for a text byte available */

   if (PtyPeek(&byte, seconds) == 1 && byte>=' ' && byte < 127) {

      /* is next byte is text, find out how many text bytes */

      l = memscanctrl (head->next_byte, head->bytes_unread);

      /*  copy input text to user's buffer */

      if (l > buffer_size) l = buffer_size;
      memcpy (buffer, head->next_byte, l);

      /* update input pointers and lengths */

      head->next_byte += l;
      head->bytes_unread -= l;

      /* if no more unread bytes, put buffer back */

      if (head->bytes_unread == 0) remove_buffer();

   }

   return(l);

}


/*======================================================================*/
/*
**	PtyPeek()
**
**	Lets the caller get a peek of the first available byte of
**	output from spawned application.
**
**	Returns:
**
**	1	next available byte stored
**	0	time out
**	-1	Process no longer active
*/
/*======================================================================*/

int PtyPeek (byte_buffer, seconds)
char *byte_buffer;			/* to store next byte in buffer */
int seconds;				/* max. seconds to wait */
{
   int result;

   result = PtyActive ? 0 : -1;		/* set default result */

   /* see if there's any input available */

   if (wait_for_read(seconds)) {
      *byte_buffer = *head->next_byte;
      result = 1;
   }

   return (result);

}

/*======================================================================*/
/*
**	PtyClear()
**
**	Clears out any unread stuff in the input buffers.
**
**	No input, no output.
*/
/*======================================================================*/
PtyClear ()
{

   while (head != NULL) remove_buffer();

}

/*======================================================================*/
/*
**	PtyWrite()
**
**	Writes a string of the specified length to the pseudo
**	terminal, which then becomes input to the application
**	using the pseudo terminal.
*/
/*======================================================================*/

PtyWrite (string, length)
char *string;				/* string of characters to write */
int length;				/* number of characters */
{
   int status, result;

   /* truncate a too-long string */

   if (length > 508) length = 508;

   /* copy string to page-aligned output buffer */
   
   memcpy (&outbuf->data, string, length);

   /* write the data to the pseudo terminal, read back any echo */

   status = ptd$write (channel, 0, 0, 
	outbuf, length, 
	0, 508);

   /* if normal status, and something is echoed, call input function */

/*   if ((status == SS$_NORMAL) && (echobuf->length != 0)) 
      input_function (&echobuf->data, echobuf->length); */

}

/*======================================================================*/
/*
**	PtyStop()
**
**	Stops the child process and deletes the pseudo terminal
*/
/*======================================================================*/

PtyStop ()
{

   if (PtyActive) {
      sys$delprc (&child_pid,0);	/* delete sub process */
      while (PtyActive) pause();	/* wait till process ends */
   }

}

PtySignal (func)
int (*func)();				/* caller's input signal function */
{

   signal_function = func;

}

/*======================================================================*/
/*
**	remove_buffer()
**
**	Internal routine to take the top buffer from the input queue
**	and put it in the free queue.
*/
/*======================================================================*/


static remove_buffer()
{
   struct bq *bq_temp;

   if (head != NULL) {
      sys$setast(0);			/* disable */
      bq_temp = head;			/* save top buffer */
      head = head->next;		/* take off ready queue */
      bq_temp->next = free;		/* put it on free queue */
      free = bq_temp;
      sys$setast(1);			/* enable */
      start_read();			/* start some more reads */
   }

}

/*======================================================================*/
/*
**	wait_for_read()
**
**	If nothing is in the input queue, wait for something to show up
**	or for the process to terminate or for the specified number
**	of seconds to elapse.
*/
/*======================================================================*/

static int timeout = 0;

/* AST for time out alarm */

static int alarm_ast() { timeout = 1; }

static int wait_for_read (seconds)
int seconds;				/* time to wait */
{

   if (PtyActive && head==NULL && seconds>0) {
      alarm(0);				/* cancel any alarms */
      timeout = 0;			/* clear flag */
      signal(SIGALRM, alarm_ast);	/* set signal handler for alarm */
      alarm(seconds);			/* set alarm */

      /* wait for something to happen */

      while (PtyActive && timeout == 0 && head == NULL) 
         pause();

      alarm(0);				/* cancel alarm */
   }

   /* tell caller if input data available */

   return(head != NULL);

}


/*======================================================================*/
/*
**	read_ast()
**
**	A static function for the read AST.  Entered when a read
**	to the pseudo terminal device has completed.  The parameter
**	is a pointer to the structure used to main the input
**	buffers.  If the status is OK, the completed buffer
**	is put at the end of ready buffers.
*/
/*======================================================================*/

static int read_ast (ptr)
struct bq *ptr;				/* pointer to buffer element */
{
   int status, i;

   inbuf = ptr->buffer;			/* set ptr to pseudo term buffer */
   ptr->next = NULL;

   /* check for good status and non-zero length */

   if (inbuf->status == 1 && inbuf->length != 0) {

      /* set # of bytes unread and ptr to first byte */

      ptr->bytes_unread = inbuf->length;
      ptr->next_byte = &inbuf->data;

      /* convert all data to 7 bit ASCII */

      memmask (ptr->next_byte, ptr->bytes_unread, 0x7f);

      /* put completed buffer in ready queue */

      if (head != NULL) {
         tail->next = ptr;
      }
      else {
         head = ptr;
         tail = ptr;
      }

      /* wake up the process if it's waiting */

      sys$wake(0,0);		
   }

   /* if bad status, put buffer back in queue */

   else {
      ptr->next = free;
      free = ptr;
   }

   /* call caller's action routine if there is one */

   if (signal_function != NULL) {
      signal_function();
   }

   return (1);

}

/*======================================================================*/
/*
**	start_read()
**
**	Start a read to the pseudo terminal.
*/
/*======================================================================*/

static int start_read()
{
   struct bq *ptr;
   int status;

   /* start reads for all free buffers */

   while (free != NULL) {
      sys$setast(0);			/* disable ast's */
      ptr = free;
      free = ptr->next;
      sys$setast(1);			/* enable ast's */
      status = ptd$read (0, channel, read_ast, ptr, ptr->buffer, 508);
   }

}


/*
**	File: devname.c
**
**	devname ()
**
**	This is a VMS-specific function that returns the device name
**	for a given channel.
**
**	Written by:
**
**	Mike Wright
**	Center for Instructional and Research Computing Activities (CIRCA)
**	University of Florida
**	Gainesville, Florida
**	
*/


#include <stdio.h>
#include <dvidef.h>
#include <ssdef.h>

char * devname(channel)
int channel;				/*device channel number */
{

   int status, iosb[2];   
   short int length;
   char *result = NULL;

   /* static string is used to return name to caller */

   static char device_name[65];

   /* strucure for GETDVI item list */

   struct item {
      short int length;
      short int item_code;
      char *address;
      long int return_length;
   } item[2];

   /*
   **	Set up item list to get the device name for the channel.
   **   The device name item is always returned in a 64 byte
   **   field.
   */

   item[0].length = 64;
   item[0].item_code = DVI$_DEVNAM;
   item[0].address = &device_name;
   item[0].return_length = &length;

   item[1].length = 0;
   item[1].item_code = 0;

   /* clear iosb and issue getdvi */
   
   iosb[0] = 0;
   iosb[1] = 0;
   status = sys$getdviw (0, channel, 0, &item, &iosb, 0, 0, 0);

   /* if normal status, terminate device name string and return pointer */

   if (status == SS$_NORMAL && iosb[0] == SS$_NORMAL) {
      device_name[length] = 0;
      result = &device_name;
   }

   /* return pointer to name (or NULL is there was an error) */

   return (result);

}

/*
**	File:  string-vms.c
**
**	A collection of functions to supplment the string routines
**	available for the the VMS C compiler.  The following functions
**	are in this file:
**
**	strdesc()	builds a VMS string descriptor
**	strupr()	converts a string to upper case
**	strcmpi()	case blind compare of two strings
**	memmask()	ands a mask byte to memory area
**	memscanctrl()	scans for ASCII control
**
**	Written by:
**
**	Mike Wright
**	Center for Instructional and Research Computing Activities (CIRCA)
**	University of Florida
**	Gainesville, Florida
**	
**
*/

#include <string.h>
#include <descrip.h>
#pragma builtins

/*
**	strdesc ()
**
**	This function builds a VMS string descriptor.  The user provides
**	an eight byte area for the descriptor and a string.  The
**	descriptor area is built using the passed string and its length.
**	The address of the descriptor is returned as a result.
**
**	This function makes it easier to use VMS calls that require
**	descriptors.  
*/

struct dsc$descriptor * strdesc (desc, string)
struct dsc$descriptor *desc;		/* descriptor area */
char *string;				/* null terminated string */
{

   /* build an ASCII string descriptor for the caller */

   desc->dsc$w_length = strlen(string);
   desc->dsc$b_dtype = DSC$K_DTYPE_T;
   desc->dsc$b_class = DSC$K_CLASS_S;
   desc->dsc$a_pointer = string;

   /* return the address of the descriptor */

   return (desc);

}


/*
**	strupr()
**
**	Converts a character string to upper case.
**
**	Input: 	pointer to the string.
**
**	Output:  pointer to the string.
**
*/

char * strupr (string)
char *string;				/* string to convert */
{

   struct dsc$descriptor source_desc, dest_desc;

   str$upcase (strdesc(&dest_desc,string), strdesc(&source_desc,string));

   return (string);

}

/*
**	strcmpi()
**
**	Case insensitive compare of two strings.
**
*/

char * strcmpi  (string1, string2)
char *string1, *string2;
{

   struct dsc$descriptor string1_desc, string2_desc;

   return(str$case_blind_compare (strdesc(&string1_desc, string1), 
      strdesc(&string2_desc,string2)));

}

/*
**	memmask ()
**
**	Ands a mask to an area of memory.
**
**	Input:
**
**	addr		ptr to memory
**	len		length of memory
**	mask		mask to and to bytes in memory
**
**	Output:  none
*/


void memmask (addr, len, mask)
unsigned char *addr;			/* address of memory */
int len;				/* number of bytes to mask */
int mask;				/* mask to use */
{

   register int len_r, mask_r;
   register unsigned char *p;

   /* use register variables for speed */

   len_r = len;
   mask_r = mask;
   p = addr;

   /* this loop coded for optimal VAX execution */

   if (len_r > 0) {
      while (len_r) {
         *p = *p & mask_r;
         p++;
         len_r--;
      }
   }

} /* end of memmask() */


/*
**	memscanctrl()
**
**	Scans memory for a control character, which will be an 
**	character in the range 0x00-0x1f or 0x80-0x9f.
**
**	Input:
**
**	addr		pointer to memory
**	len		length to scan
**
**	Returns:
**
**	The number of characters preceding the control character.
**	Could be equal to the length if no control characters are
**	found.
**
**	This is a VAX-specific function and uses the VAX C
**	_SCANF in line function.
*/


/*
**	Table to use for SCANF function.
**
**	Every position that has a 1 is a control character.  This
**	table is set up for ASCII with or without an eighth bit.
**	Normal usage is probably only for 7-bit ASCII.
*/

static char table0[16] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
static char table1[16] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
static char table2[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static char table3[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static char table4[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static char table5[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static char table6[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static char table7[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static char table8[16] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
static char table9[16] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
static char tablea[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static char tableb[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static char tablec[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static char tabled[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static char tablee[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static char tablef[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};


int memscanctrl (addr, len)
char *addr;				/* pointer to memory */
int len;				/* length */
{
   int remainder;

   /* remainder will be # of characters beginning at control char */

   remainder = _SCANC (len, addr, &table0, 1);

   return (len - remainder);

} /* end of memscanctrl() */

/*
**	mem7bit()
**
**	**** This is not a working function *****
*/

/* table for MOVTC instruction */

static char tt0[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
static char tt1[16] = {16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
static char tt2[16] = {32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47};
static char tt3[16] = {48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63};
static char tt4[16] = {64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79};
static char tt5[16] = {80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95};
static char tt6[16] = 
   {96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111};
static char tt7[16] = 
   {112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127};
static char tt8[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
static char tt9[16] = {16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
static char tta[16] = {32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47};
static char ttb[16] = {48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63};
static char ttc[16] = {64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79};
static char ttd[16] = {80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95};
static char tte[16] = 
   {96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111};
static char ttf[16] = 
   {112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127};

int mem7bit (addr, len)
char *addr;
int len;
{

/*   _MOVTC (len, addr, 0, table0, len, addr); */

}

>>>>>>>>>>>>>>>>>> End From:	IN%"MCW@pine.circa.ufl.edu"  "WRIGHT" 15-JUN-1993 19:26:06.63

>>>>>>>>>>>>>>>>>> From: SYSMGR@IPG.PH.KCL.AC.UK

Fred morris wrote:

> There was at one time an unsupported "PTYDRIVER" that did something along
> those lines. To the best of my knowledge, it's defunct, because people
> have generally concluded that there are better ways to do this. Again: do
> you have a sound architectural/design reason for this, or are you just
> backed into a corner?

No, It's defunct because DEC took the basic idea on board and bought out
their own Pseudoterminal driver: see chapter 9 of the VMS IO user's reference
manual (provided your doc. set is up to date). The original PTY driver suffered 
from maintainance problems because DEC hadn't documented the terminal
driver port/class interface. In fact they now have, but since there is a 
free pseudoterminal driver with VMS fully supported by DEC, the user
community stopped supporting its homebrew version and modified the programs
that used it to use DEC's pseudoterminal driver instead.

The original PTYDRIVER probably still works. If it doesn't then it shouldn't
be hard to fix given that DEC have published the terminal port/class
interface specification, and it would represent a good jumping-off point
if someone wanted to experiment with things that aren't easy to accomplish
with the DEC driver. (Not sure what that might be, but never mind :-)

By the way: you can disconnect someone else's terminal given SHARE privilege.

	Yours,
		Nigel Arnot

		NRA%ipg.ph.kcl.ac.uk@nsfnet-relay.ac.uk   (internet)
		NRA%uk.ac.kcl.ph.ipg@ukacrl.bitnet        (bitnet)

>>>>>>>>>>>>>>>>>> From: SYSMGR@IPG.PH.KCL.AC.UK
--
 /---------------------------------------------------------------------------\
| Rob Wright                | Wright_RJ@cupid.curtin.edu.au                   |
| Curtin University         | R.Wright@icarus.curtin.edu.au                   |
| Computing Centre          | psi%050529452300070:Wright_RJ                   |
| Perth, Western Australia  | Voice (24 hours):(09)351-7385  FAX: 09-351-2673 |
|-----------------------------------------------------------------------------|
|    A nation which can not trust its intellectuals does not trust itself.    |
 \---------------------------------------------------------------------------/

