Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!mnetor!uunet!seismo!rutgers!labrea!decwrl!decvax!ucbvax!ENGVAX.SCG.HAC.COM!KVC
From: KVC@ENGVAX.SCG.HAC.COM (Kevin Carosso)
Newsgroups: comp.os.vms
Subject: CMU pseudo-terminal drivers (part 1 of 3)  [DCL archive format]
Message-ID: <8707200032.AA02560@ucbvax.Berkeley.EDU>
Date: Thu, 16-Jul-87 19:50:00 EDT
Article-I.D.: ucbvax.8707200032.AA02560
Posted: Thu Jul 16 19:50:00 1987
Date-Received: Mon, 20-Jul-87 03:44:10 EDT
Sender: daemon@ucbvax.BERKELEY.EDU
Distribution: world
Organization: The ARPA Internet
Lines: 872

$ show default
$ check_sum = 1515454846
$ write sys$output "Creating 000_README.TXT"
$ create 000_README.TXT
$ DECK/DOLLARS="$*$*EOD*$*$"
These files make up a pseudo terminal driver for VAX/VMS.  This
driver runs under VMS version 4.x.  It will not work under VMS prior
to 4.  See PSEUDO.DOC for documentation and NOTES.TXT for my
additional comments and observations.

This new version fixes up several major bugs, including:

  o  ^S^Y system crasher
  o  randomly munged  and  characters
  o  SHOW DEVICE stack dumps
  o  system crashes due to attempted use of the TPA0: device
  o  terminal device name changed from PTAn: to TPAn: because
     VMS uses PTA for MSCP tapes.

This driver was originally developed at Carnegie-Mellon University and has
made the rounds before as the CMU PTY driver.

        /Kevin Carosso              kvc@engvax.scg.hac.com
         Hughes Aircraft Co.    or  kvc%engvax@oberon.usc.edu

         December 9, 1986
$*$*EOD*$*$
$ checksum 000_README.TXT
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ check_sum = 1780435178
$ write sys$output "Creating DESCRIP.MMS"
$ create DESCRIP.MMS
$ DECK/DOLLARS="$*$*EOD*$*$"
! Build the pseudo-terminal utility, PYDRIVER and TPDRIVER.
!
INCLUDE pub_root:[lib.mms]include.mms

DESTDIR = $(PRIVDIR)
distribution_files = 000_readme.txt, descrip.mms, driver.opt, notes.txt, -
                     pseudo.doc, pty.pas, pydriver.mar, tpdriver.mar
MFLAGS = /NOLIST

build : PYDRIVER.EXE TPDRIVER.EXE
        @ WRITE SYS$OUTPUT "Build finished."

install : build $(DESTDIR)pydriver.exe $(DESTDIR)tpdriver.exe
        $ write sys$output "Installation finished"

distribute : zz_pty_dist.com
        $ write sys$output "DCL archive complete: $(MMS$SOURCE)"

TPDRIVER.EXE : TPDRIVER.OBJ
        - LINK TPDRIVER.OBJ,DRIVER.OPT/OPT,SYS$SYSTEM:SYS.STB/SELECT

PYDRIVER.EXE    : PYDRIVER.OBJ
        - LINK PYDRIVER.OBJ,DRIVER.OPT/OPT,SYS$SYSTEM:SYS.STB/SELECT

$(DESTDIR)pydriver.exe : pydriver.exe
        $ copy $< $@

$(DESTDIR)tpdriver.exe : tpdriver.exe
        $ copy $< $@

zz_pty_dist.com : $(distribution_files)
        $ @pub:archive $(MMS$TARGET) "$(MMS$SOURCE_LIST)"
$*$*EOD*$*$
$ checksum DESCRIP.MMS
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ check_sum = 1702060495
$ write sys$output "Creating DRIVER.OPT"
$ create DRIVER.OPT
$ DECK/DOLLARS="$*$*EOD*$*$"
base=0
$*$*EOD*$*$
$ checksum DRIVER.OPT
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ check_sum = 238513379
$ write sys$output "Creating NOTES.TXT"
$ create NOTES.TXT
$ DECK/DOLLARS="$*$*EOD*$*$"
These notes should be considered an addition to the documentation
to be found in PSEUDO.DOC.  These notes document some enhancements
I have made to the drivers as well as some caveats I have found.

First of all, I want to grant credit to the original authors of the
driver, they are listed at the beginning of the two source files.  In
private correspondence with Dale Moore at CMU, he has chosen to release the
driver to the public domain.  There have been various incarnations of this
driver in the past.  I started with a version that was modified to run
under VMS V4 and cleaned it up and made CONNECT/DISCONNECT and HANGUP work
consistently.  I also changed the device name of the terminal device from
PTAn: to TPAn: because VMS now uses PTA0: for MSCP tapes.

The drivers have been tested on VMS V4.0 through V4.5.

Load the two drivers as PYA0: and TPA0:.  Assigning a channel to
PYA0: will create a PYAn: and a TPAn: pseudo-terminal pair (the unit
numbers just go up).  Anything you write to that channel appears on the
TP as if typed at a terminal.  Any output to the terminal TPAn: is available
to be read from PYAn:.  On output, the devices do flow control between one
another and all buffering.  You can just read, for example, with a 100
char buffer from PY and the read will complete with whatever number of
characters the QIO to the TP terminal had, anywhere from 1 to MAXBUF.
If the QIO to the TP had more chars than your read buffer, it'll just
fill the read buffer and return, and you can get the rest on the next
read.  On input, it is possible to overrun the TP device's terminal
type-ahead buffer, as it is with a real terminal.

If you assign a mailbox with the PYAn: device (use LIB$ASN_WTH_MBX
when assigning the channel to PYA0:) you will get an MSG$_TRMHANGUP
message in the mailbox whenever the terminal is hung-up.  This occurs
when a process deassigns the last channel to the device, just as a HANGUP
will on terminals with modem signals.  You may ignore the hangup message if
you choose and continue to use the device after the message.

Should you deassign all channels to the PYAn: device, a hangup will be
forced on any process using the TPAn: device.  Just like dropping carrier on
a modem line.  If the pseudo-terminal is in use by an interactive process
and has an associated virtual terminal, the process will be disconnected,
otherwise the interactive process will be logged out.

To enable virtual terminals on pseudo-terminals, you must have the
TT2$M_DISCONNECT bit set in the system default terminal characteristics
(TTY_DEFCHAR2 in SYSGEN).

Note that, just as with normal VMS terminals, you will only get a
VTAn: if the line has the DISCONNECT terminal characteristic before
you log into it, and only if you use LOGINOUT to start a process on
the terminal.  Sending a  into the pseudo-terminal device will
start up LOGINOUT as it would on a real terminal.

There is currently one minor known bug in the drivers, a side-effect of
the driver being cautious in order to prevent possible problems.  If you
have a virtual terminal associated with the pseudo-terminal, and you
deassign the last channel to the control device (the PY device) then a
DISCONNECT is forced on any process on the TP and both devices should be
deleted.  Currently, the TP will not disappear, though the PY will and
the TP will be marked as OFFLINE.  This is because I rely on VMS to do
the actual delete of the device and it appears that virtual terminals
screw up the way device reference counts work, so VMS never gets around to
deleting the device.  It is marked for delete, so anything you do (I like
SHOW TERM TPA53:, for example) will cause it to disappear.  I could fix this
by zapping the device explicitly after forcing the DISCONNECT, however I
am not convinced that someone, somewhere, won't try to reference the UCB and
thus cause bad things to occur.  Note that it DOES get deleted correctly if
you do not have a virtual terminal associated with the TP or if you have no
processes active on the TP when the PY and TP are to be deleted.  These two
cases are generally the norm.

I am currently considering a few enhancements:

 o  It should be possible to set the TP terminal characteristics through
    the PY device, so the controlling program can set them.

 o  The TP device driver should notify the controlling program (via the
    associated mailbox) whenever the TP terminal characteristics are
    changed in case applications need to know such things.

 o  There should be a mechanism, again utilizing the associated mailbox,
    for the controlling application to be notified when the TP's typeahead
    buffer fills.

I need to think about the ramifications of changing device characteristics on
TTDRIVER unexpectedly, however, before I make the first change.

If you need to set characteristics of the terminal device right now,
you must assign a channel to the TP device and use a SETMODE QIO
then deassign the channel before giving the TP to another process. You
will get a HANGUP message in the associated mailbox when you
deassign if the HANGUP attribute was set on the device.  You can
ignore this message, however, and go ahead and use the device.

You cannot set characteristics of the master device.  This used to be
allowed, though it had no purpose, and it crashes the system (at
least under V4).

        /Kevin Carosso               kvc%engvax.UUCP@oberon.usc.edu
         Hughes Aircraft Co.

         December 9, 1986
$*$*EOD*$*$
$ checksum NOTES.TXT
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ check_sum = 1110109988
$ write sys$output "Creating PSEUDO.DOC"
$ create PSEUDO.DOC
$ DECK/DOLLARS="$*$*EOD*$*$"


1. PTY Driver
  This  chapter  describes  the  use  of  the VAX/VMS Pseudo Terminal Driver as
implemented Carnegie-Mellon University Computer Science Department.  Additional
information about VMS device drivers and the VMS operating system interface can
be found in VAX/VMS I/O User's Guide, and in VAX/VMS System  Service  Reference
Manual.

1.1 PTY Driver Features and Capabilities
  Pseudo  terminals  (or  PTYs)  are  virtual  terminals in the system.  Unlike
normal terminals where the I/O is actually done with a physical  device,  PTY's
I/O  has  no  interface to any physical device.  In this way they are much like
mailboxes.



1.1.1 Concept of Control and Terminal Device
  A single PTY consists of two devices, a control and a terminal device.    The
terminal device is the device that acts like a terminal.

  The  control  device  on  VMS is named ``PYAx:''.  For example, a PTY control
device could be named ``PYA1:''.  The terminal device portion  of  the  PTY  is
name  ``TPAx:''.    The  terminal  device  ``TPA1:'' is the mate to the control
device ``PYA1:''.  Similarly, the control device ``PYA5:'' is the mate  to  the
terminal device ``TPA5:''.


1.1.1.1 Similarities Between PTYs and Terminals
  The  terminal  portion  of  a  PTY  behaves  very much like a regular VAX/VMS
terminal.  The terminal portion of a PTY has

   - Type-ahead

   - Specifiable or default line terminators

   - Special operating modes, such as NOECHO and PASSALL

   - American National Standard escape sequence detection

   - Terminal/mailbox interaction

   - Terminal control characters  such  as  Sontrol-S  and  Control-Q  for
     starting  and  stopping  output, Control-O for discarding output, and
     all other special characters that are handled by the VAX/VMS terminal
     driver.

   - Limited  full-duplex  operation(simultaneously  active read and write
     requests)


1.1.1.2 Differences Between PTYs and Terminals
  The difference between a VAX/VMS terminal and the terminal portion of  a  PTY
is  where  the  input  comes  from  and where the output goes to.  On a VAX/VMS
terminal, the input comes from an actual terminal and the  output  goes  to  an
actual  terminal.    On  a  PTY the input comes from the control device and the
output goes to the control device.  In order to simulate someone typing at  the
terminal  device  ``TPA5:''  we must write to the control device ``PYA5:''.  In
order to read what has been typed out to the terminal device ``TPA5:''  we must
read  from  the  control  device  ``PYA5:''.  PTYs are like terminals that only
software can access.

  There is no buffering of the input on the control device.  It is possible  to
fill up the terminal device's typeahead buffer by writing large amounts of data
to the control device.

  PTYs are virtual devices.  When allocating  a  PTY,  they  behave  much  like
mailboxes or network devices.  To allocate a PTY, simply allocate the first one
('PYA0:').  The allocating routine will be create a new PTY and  assign  it  to
you.  The PTY will be deallocated when no process is referencing the device.


1.1.1.3 Uses of PTYs
  There  is  no  device  from  DEC  which suffices for remote logins when using
non-DECnet protocols.  The remote terminal driver expects the remote node to do
the local line editing.  This includes delete, control-o, control-s, control-q,
and much other stuff.  PTYs handle the local line editing locally.    When  the
line  editing  is  done locally, the remote machine has to know much less about
VMS and its terminal handling conventions.

1.2 Installing PTYs
  In VAX/VMS V3.0, several pieces of software support the  terminal  interface.
The  hardware  independent portion is SYS$SYSTEM:TTDRIVER.EXE.  This version of
PTYs does not require any changes or patches to this code.    There  are  other
pieces   of   software  for  particular  pieces  of  hardware.    The  code  in
SYS$SYSTEM:DZDRIVER.EXE is the device driver required for  DZ11's  and  DZ32's.
The  code in SYS$SYSTEM:YCDRIVER.EXE is the device driver required for DMF32's.
PTY drivers consist of  two  device  drivers,  TPDRIVER.EXE  for  the  terminal
portion of the PTY's, and PYDRIVER.EXE for the control portion.



1.2.1 Compiling Sources
  There   are  several  pieces  of  source  code  that  make  up  PTY  drivers.
PYDRIVER.MAR is the source for the control portion.  TPDRIVER.MAR is the source
for the terminal portion.

  The commands for compiling and linking the devices are:
$ ! Compile the drivers
$ MACRO /LIST /OBJECT TPDRIVER.MAR+SYS$LIBRARY:LIB/LIBRARY
$ MACRO /LIST /OBJECT PYDRIVER.MAR+SYS$LIBRARY:LIB/LIBRARY
$ ! Link the drivers
$ LINK /SHARE /MAP /FULL /CROSS -
        TPDRIVER,SYS$SYSTEM:SYS.STB/SELECTIVE,SYS$INPUT:/OPTION
BASE=0
$ LINK /SHARE /MAP /FULL /CROSS -
        PYDRIVER,SYS$SYSTEM:SYS.STB/SELECTIVE,SYS$INPUT:/OPTION
BASE=0



1.2.2 Installing the Devices
  To  load  a  PTY,  login  to  a  privileged  account  and issue the following
commands.
$ RUN SYS$SYSTEM:SYSGEN
CONNECT TPA0/NOADAPTER/DRIVER=device:[directory]TPDRIVER
CONNECT PYA0/NOADAPTER/DRIVER=device:[directory]PYDRIVER

  You may want to build a command file which installs the devices at boot  time
by modifying SYS$MANAGER:SYSTARTUP.COM.

  Don't try to use TPB0 or PYB0.  The code is not built to handle these.  It is
only built to handle the PYA and TPA control and terminal devices.

1.3 Device Information
  User processes can obtain terminal and control device  information  by  using
the  $GETDVI,  $GETCHN and $GETDEV system services (see VAX/VMS System Services
Reference Manual).

  It is recommended that new programs make use  of  the  $GETDVI  and  $GETDVIW
system services.



1.3.1 PTY Terminal Device Dependent Information
  The  information  returned  about  a terminal device is in the same format as
information returned about a regular VAX/VMS terminal.  By only looking at  the
information returned from $GETCHN and $GETDEV system services, it is impossible
to tell the difference between a PTY terminal device  and  an  actual  terminal
device.



1.3.2 PTY Control Device Dependent Information
  When  applied  to  a  PTY  control  device, $GETCHN return information in the
format as show in Figure 1-1.


              31        24 23        16 15         8 7          0
             -----------------------------------------------------
             |              Device Characteristics               |
             -----------------------------------------------------
             |       Buffer Size       |    Type    |    Class   |
             -----------------------------------------------------
             |           Unused        |     TP Unit Number      |
             -----------------------------------------------------

            Figure 1-1:   PTY Control Device Dependent Information

  The first longword contains device-independent data.  The  second  and  third
longwords contain device-dependent data.


1.3.2.1 Device Characteristics
  The  characteristics  of  the  the  PTY  control device (PYA) can be found by
either using the first longword returned by  the  $GETDEV  and  $GETCHN  system
services, or by using the item code DVI$ DEVCHAR to the $GETDVI system service.

  A PTY control device has the following characteristics.

   - DEV$M AVL - On line and available

   - DEV$M IDV - Capable of input

   - DEV$M ODV - Capable of output

   - DEV$M REC - Record oriented

  A PTY control device does not have the following characteristics.

   - DEV$M CCL - Carriage Control

   - DEV$M TRM - Terminal Device


1.3.2.2 Buffer Size
  Also returned from $GETDVI by using item code DVI$ DEVBUFSIZ.


1.3.2.3 Device Type
  The  device  type  is  DT$ PY.  Since a PTY is a nonstandard device, you will
probably not find the correct macros, literals or  constants  for  this  device
type.  This field should have a value of 0.

  Also returned from $GETDVI by using item code DVI$ DEVTYPE.


1.3.2.4 Device Class
  The  device  class  is DC$ PY.  Since a PTY seems to be such an odd creature,
this field should be FF in hex, or 177 in octal.

  Also returned from $GETDVI by using item code DVI$ DEVCLASS.


1.3.2.5 PT Unit Number
  The unit number is unit number of the associated PTY terminal device.

  The unit number is also returned as the low sixteen bits in DVI$ DEVDEPEND by
$GETDVI.

1.4 PTY Function Codes
  The  function  codes for the terminal device portion of a PTY are exactly the
same as those for regular VAX/VMS terminals.  For more information  on  VAX/VMS
terminal I/O see VAX/VMS I/O User's Guide.

  The  basic  function  of the control portion of a PTY are read, write and set
mode or characteristics.

  A user does not need to have assigned the PTY terminal device in order to  do
I/O  operations  on  the  PTY  control device.  If the terminal device has type
ahead enabled, sending the right characters at the control device will  send  a
message  to  OPCOM  to  start  running  SYS$SYSTEM:LOGINOUT.EXE on the terminal
device.



1.4.1 Read
  The basic purpose of a PTY control device read is to transfer data  from  the
output buffer of the PTY terminal device to a user specified buffer.  There are
three read functions which a user can apply to a PTY control device.

   - IO$ READVBLK - Read virtual block

   - IO$ READLBLK - Read logical block

   - IO$ READPBLK - Read physical block

  A read is complete if either of the below conditions occur:

   - The user specified buffer is full

   - At least one character is available from the PTY terminal device

  The read function codes can  take  the  following  device/function  dependent
arguments:

   - P1  =  The  starting virtual address of the buffer that is to receive
     the data read

   - P2 = The size of the buffer that is to receive the data read in bytes

   - P3, P4, P5, P6 = ignored



1.4.2 Write
  The basic purpose of a PTY control device write is to transfer data from  the
user  specified  buffer  to  the  typeahead  buffer of the PTY terminal device.
There are three write functions which a user can apply to a PTY control device.

   - IO$ WRITEVBLK - write virtual block

   - IO$ WRITELBLK - write logical block

   - IO$ WRITEPBLK - write physical block

  The write function codes can take  the  following  device/function  dependent
arguments:

   - P1 = The starting virtual address of the buffer that is to be written
     to the PTY terminal device

   - P2 = The number of bytes that are to be sent

   - P3, P4, P5, P6 = Ignored



1.4.3 Set Mode and Set Characteristics
  The Set Mode function affects the mode and temporary characteristics  of  the
associated PTY control device.  Set Mode is a logical I/O function and requires
no privilege.  A single function code is provided:

   - IO$ SETMODE

  The Set Charateristics function affects the permanent characteristics of  the
associated  PTY control device.  Set Characteristics is a physical I/O function
and requires the privilege  necessary  to  perform  physical  I/O.    A  single
function code is provided:

   - IO$ SETCHAR

  These  functions take the following device/function dependent arguments if no
function modifiers are specified:

   - P1 = address of characteristics buffer

   - P2, P3, P4, P5, P6 = ignored

  The P1 argument points to a quadword block, as shown in Figure 1-2.


              31        24 23        16 15         8 7          0
             -----------------------------------------------------
             |       Buffer Size       |    Type    |    Class   |
             -----------------------------------------------------
             |                      Unused                       |
             -----------------------------------------------------

                 Figure 1-2:   Set Mode Characteristics Buffer


1.4.3.1 Function Modifiers
  Function Modifiers to the control device currently do not affect the state or
accessibility of terminal device.

  You  cannot  change  the  mode  or  characteristics of the terminal device by
adding function modifiers to the I/O routines  for  the  control  device.    An
earlier version of PTYdrivers supported this feature.  This feature has not yet
been added to this version.



1.4.4  Sense Mode and Sense Characterisitics
  The two function codes to sense the mode of the PTY control device are:

   - IO$ SENSEMODE

   - IO$ SENSECHAR

  The IO$ SENSEMODE function returns the process-associated, that is temporary,
characteristics  of the PTY control device.  The IO$ SENSECHAR function returns
the permanent characteristics of the PTY control  device.    The  IO$ SENSEMODE
function  is  a  logical function and requires no privilege.  The IO$ SENSECHAR
function is a physical function and requires the privilege necessary to perform
physical I/O.

  These function codes take the following device/function dependent.

   - P1 = address of the quadword characteristics buffer

   - P2, P3, P4, P5, P6 = ignored

  The  P1  argument  points to a quadword block which is the same format as the
Set Mode Characteristics Buffer, shown in Figure 1-2



1.4.5 I/O Status Block
  The I/O status block formats for read, write, set mode  and  sense  mode  I/O
functions are shown in Figures 1-3 and 1-4.


              31        24 23        16 15         8 7          0
             -----------------------------------------------------
             |       Byte Count        |         Status          |
             -----------------------------------------------------
             |                      Unused                       |
             -----------------------------------------------------

             Figure 1-3:   IOSB Contents - Read and Write Function


              31        24 23        16 15         8 7          0
             -----------------------------------------------------
             |         Unused          |         Status          |
             -----------------------------------------------------
             |                      Unused                       |
             -----------------------------------------------------

Figure 1-4:   IOSB Contents - Set and Sense Mode and Characteristics Functions

  The status indicates the succes or failure of the specified operation.  Below
are possible values for the status field:

   - SS$ ABORT - The operation was canceled by the Cancel I/O  on  Channel
     ($CANCEL)  system service.  Applicable only if the drive was actively
     involved in an operation.

   - SS$ NORMAL - Successful Completion

   - SS$ ACCVIO - The specified buffer is not accessible to the  specified
     process

1.5 Possible Improvements and Bugs
  Watch  the  error counts on the devices.  Occasionally, a PTY terminal device
might get a timeout.  Perhaps,  it  is  not  executing  at  a  sufficient  IPL.
Somehow, we aren't entering the startio routine soon after the UCB$V_TIM bit is
set in UCB$W_STS.

  It may be useful for the associated mailbox of the PY  device  to  receive  a
message  every  time  the  stop,  start, abort, resume, xoff, or xon routine is
called.  This would permit programs that control the  PY  device  to  determine
when  to slow down the data rate, or abort the output in the buffers which have
already been read.



                               Table of Contents

1. PTY Driver                                                                 2

   1.1 PTY Driver Features and Capabilities                                   2
       1.1.1 Concept of Control and Terminal Device                           2
           1.1.1.1 Similarities Between PTYs and Terminals                    2
           1.1.1.2 Differences Between PTYs and Terminals                     2
           1.1.1.3 Uses of PTYs                                               2
   1.2 Installing PTYs                                                        2
       1.2.1 Compiling Sources                                                2
       1.2.2 Installing the Devices                                           2
   1.3 Device Information                                                     2
       1.3.1 PTY Terminal Device Dependent Information                        2
       1.3.2 PTY Control Device Dependent Information                         2
           1.3.2.1 Device Characteristics                                     2
           1.3.2.2 Buffer Size                                                2
           1.3.2.3 Device Type                                                2
           1.3.2.4 Device Class                                               3
           1.3.2.5 TP Unit Number                                             3
   1.4 PTY Function Codes                                                     3
       1.4.1 Read                                                             3
       1.4.2 Write                                                            3
       1.4.3 Set Mode and Set Characteristics                                 3
           1.4.3.1 Function Modifiers                                         3
       1.4.4  Sense Mode and Sense Characterisitics                           3
       1.4.5 I/O Status Block                                                 3
   1.5 Possible Improvements and Bugs                                         3



                                List of Figures
   Figure 1-1:   PTY Control Device Dependent Information                     2
   Figure 1-2:   Set Mode Characteristics Buffer                              3
   Figure 1-3:   IOSB Contents - Read and Write Function                      3
   Figure 1-4:   IOSB Contents - Set and Sense  Mode  and  Characteristics    3
                 Functions
$*$*EOD*$*$
$ checksum PSEUDO.DOC
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ check_sum = 1443436246
$ write sys$output "Creating PTY.PAS"
$ create PTY.PAS
$ DECK/DOLLARS="$*$*EOD*$*$"
(* Simple program to converse with the pseudo-terminal control device. *)

[inherit ('SYS$LIBRARY:STARLET')]
program PTY;

const
  buffer_size = 256;
  CTRL_BACKSLASH = chr (28);

type
  string = varying [256] of char;
  unsigned_byte = [byte] 0..255;
  unsigned_word = [word] 0..65535;
  characteristic_buffer = packed record
                            dev_class, dev_type : unsigned_byte;
                            width : unsigned_word;
                            charbits, extended_bits : unsigned;
                          end;
  status_block = packed record
                   status, count, terminator, terminator_size : unsigned_word;
                 end;

var
  stat : integer;
  mbx_chan, tt_chan, pty_chan : [static, volatile] unsigned_word;
  input_char : [static, volatile] char;
  buffer : packed array [1..buffer_size] of char;
  mbx_buffer : [static, volatile] packed array [1..40] of char;
  term_chars, old_term_chars : [static, volatile] characteristic_buffer;
  in_iosb, out_iosb, mbx_iosb : [static, volatile] status_block;
  exit_descriptor : [static] packed record
                      flink : unsigned;
                      handler : unsigned;
                      argnum : integer;
                      reason : ^integer;
                    end := (0, 0, 1, nil);
  message : [static, volatile] string :=
                                ''(13, 10, 7)'Got a mailbox message'(13, 10);

{-----------------------------------------------------------------------------}

  [external]
  function LIB$ASN_WTH_MBX
                (DEVNAM : [class_s] packed array [l1..u1:integer] of char;
                 MAXMSG, BUFQUO : integer;
                 var DEVCHAN, MBXCHAN : [volatile] unsigned_word
                ) : integer; extern;

(*
 * CLEANUP is our exit handler.  It resets the terminal characteristics.
 *)

  [unbound]
  function Cleanup (reason : integer) : integer;

  var
    stat : integer;
    iosb : status_block;

  begin (* Cleanup *)
    Cleanup := reason;
    stat := $QIOW (CHAN := tt_chan,
                     FUNC := IO$_SETMODE,
                     P1 := old_term_chars,
                     P2 := size (old_term_chars));
    if not odd (stat) then Cleanup := stat;
  end; (* Cleanup *)

  [asynchronous, unbound]
  procedure Set_asynch_mbx_input; forward;

(*
 * MBX_INPUT_AST is the AST handler invoked whenever we get something in
 * the associated mailbox.
 *)
  [asynchronous, unbound]
  procedure Mbx_input_AST;

  var
    stat : integer;
    iosb : status_block;

  begin (* Mbx_input_AST *)
    if not odd (mbx_iosb.status) then $EXIT (mbx_iosb.status);

    stat := $QIOW (CHAN := tt_chan,
                   FUNC := IO$_WRITEVBLK,
                   IOSB := iosb,
                   P1 := message.body,
                   P2 := message.length);
    if not odd (stat) then $EXIT (stat);
    if not odd (iosb.status) then $EXIT (iosb.status);

    Set_asynch_mbx_input;
  end; (* mbx_input_AST *)

(*
 * SET_ASYNCH_MBX_INPUT issues an asynchronous read on the terminal.
 *)
  procedure Set_asynch_mbx_input;

  var
    stat : integer;

  begin (* Set_asynch_mbx_input *)
    stat := $QIO (CHAN := mbx_chan,
                  FUNC := IO$_READVBLK,
                  IOSB := mbx_iosb,
                  ASTADR := mbx_input_AST,
                  P1 := mbx_buffer,
                  P2 := size (mbx_buffer));
    if not odd (stat) then $EXIT (stat);
  end; (* Set_asynch_mbx_input *)

  [asynchronous, unbound]
  procedure Set_asynch_input; forward;

(*
 * INPUT_AST is the AST handler invoked whenever we get something in
 * from the terminal.  Just ship it to the PTY.
 *)
  [asynchronous, unbound]
  procedure Input_AST;

  var
    stat : integer;

  begin (* Input_AST *)
    if not odd (in_iosb.status) then $EXIT (in_iosb.status);
    if input_char = CTRL_BACKSLASH then $EXIT (SS$_NORMAL);

    stat := $QIOW (CHAN := pty_chan,
                   FUNC := IO$_WRITEVBLK,
                   IOSB := in_iosb,
                   P1 := input_char,
                   P2 := 1);
    if not odd (stat) then $EXIT (stat);
    if not odd (in_iosb.status) then $EXIT (in_iosb.status);

    Set_asynch_input;
  end; (* Input_AST *)

(*
 * SET_ASYNCH_INPUT issues an asynchronous read on the terminal.
 *)
  procedure Set_asynch_input;

  var
    stat : integer;

  begin (* Set_asynch_input *)
    stat := $QIO (CHAN := tt_chan,
                  FUNC := IO$_READVBLK,
                  IOSB := in_iosb,
                  ASTADR := Input_AST,
                  P1 := input_char,
                  P2 := 1);
    if not odd (stat) then $EXIT (stat);
  end; (* Set_asynch_input *)

begin (* PTY *)
  stat := LIB$ASN_WTH_MBX (DEVNAM := 'PYA0:',
                            MAXMSG := size (mbx_buffer),
                            BUFQUO := 2 * size (mbx_buffer),
                            DEVCHAN := pty_chan,
                            MBXCHAN := mbx_chan);
  if not odd (stat) then $EXIT (stat);

  Set_asynch_mbx_input;

  stat := $ASSIGN (CHAN := tt_chan, DEVNAM := 'TT');
  if not odd (stat) then $EXIT (stat);

(* Get terminal characteristics. *)

  stat := $QIOW (CHAN := tt_chan,
                 FUNC := IO$_SENSEMODE,
                 P1 := term_chars,
                 P2 := size (term_chars));
  if not odd (stat) then $EXIT (stat);

  if term_chars.dev_class <> DC$_TERM then
    $EXIT (SS$_IVDEVNAM);

  old_term_chars := term_chars;

(* declare exit handler so that terminal chars are restored *)

  exit_descriptor.handler := iaddress (Cleanup);
  new (exit_descriptor.reason);

  stat := $DCLEXH (DESBLK := exit_descriptor);
  if not odd (stat) then $EXIT (stat);

(*
 * Set terminal characteristics we need:
 *    PASSTHRU, NOECHO, NOTTSYNC, NOHOSTSYNC
 *)

  with term_chars do
  begin
    extended_bits := UOR (extended_bits, TT2$M_PASTHRU);
    charbits := UOR (charbits, TT$M_NOECHO);
    charbits := UAND (charbits, UNOT (TT$M_TTSYNC));
    charbits := UAND (charbits, UNOT (TT$M_HOSTSYNC));
  end;

  stat := $QIOW (CHAN := tt_chan,
                 FUNC := IO$_SETMODE,
                 P1 := term_chars,
                 P2 := size (term_chars));
  if not odd (stat) then $EXIT (stat);

  Set_asynch_input;

  repeat
    stat := $QIOW (CHAN := pty_chan,
                   FUNC := IO$_READVBLK,
                   IOSB := out_iosb,
                   P1 := buffer,
                   P2 := size (buffer));
    if not odd (stat) then $EXIT (stat);
    if not odd (out_iosb.status) then $EXIT (out_iosb.status);

    stat := $QIOW (CHAN := tt_chan,
                   FUNC := IO$_WRITEVBLK,
                   IOSB := out_iosb,
                   P1 := buffer,
                   P2 := out_iosb.count);
    if not odd (stat) then $EXIT (stat);
    if not odd (out_iosb.status) then $EXIT (out_iosb.status);
  until false;
end. (* PTY *)
$*$*EOD*$*$
$ checksum PTY.PAS
$ if checksum$checksum .ne. check_sum then -
$   write sys$output "Checksum failed, file probably corrupted"
$ exit