!TITLE Storing terminal characteristics
!
!     This   program   creates   and   maintains   a  file  of  terminal
! characteristics, which is used by the Screen Control Package  to  find
! out how to drive a particular terminal.
!
!     The program is entered by the command
!
!           TERMCHS(file,commands)
!
! where  'file'  specifies  the  terminal  characteristics  file  to  be
! operated on, and 'commands' specifies a file of commands for  TERMCHS.
! If  'file' is null, the name TERMCHSFILE is used, and if 'commands' is
! null, .IN is used.
!
!     The user is prompted  for  the  action  required. That  action  is
! carried  out, and the prompt is repeated.  The command STOP exits from
! TERMCHS.  All commands may be  abbreviated  as  long  as  they  remain
! unambiguous.
!
!     The  available commands are given below. A summary of these may be
! obtained by typing '?' in response to the 'Action:' prompt.
!
!<Initialise
!
!    The form of this command is:
!
!          INITIALISE
!
! The terminal characteristics file is emptied, after being  created  if
! it did not exist.
!>
!<Edit
!
!     The form of this command is:
!
!           EDIT n
!
! The  action  is  to  edit  record  'n' in the terminal characteristics
! file. If 'n' is not specified, the prompt
!
!           Type:
!
! is issued,  and  the  response  should  be  a  valid  terminal  record
! number. The prompt
!
!           Ed:
!
! is  then issued, and one of several editing commands may now be given.
! On completion of the edit, the appropriate  record  is  updated.   The
! characteristics  file  is  manipulated  using NEWGEN to avoid problems
! with users who have it connected during the update.
!PAGE
!
!     Users should beware of the fact  that  some  programs  (e.g. VIEW)
! disable  disconnection of the terminal characteristics file.  Thus, if
! such a program is used prior to, and in the same session as,  an  edit
! to the terminal characteristics file, the effect of the edit cannot be
! seen without logging off and on again.
!PAGE
!
!     Most  editing  commands consist of a keyword, an equals sign and a
! value.  For example, to set a Clear Screen sequence  of  three  nulls,
! the command would be:
!
!           CLEARSCREEN=0,0,0
!
! which could be abbreviated to:
!
!           CLEARSCREEN=0(3)
!PAGE
!
!     Values in sequences may be expressed as:
!
!  * Hexadecimal numbers (e.g. 1B). Any leading X is ignored
!  * ASCII mnemonics (for non-printing characters and spaces) (e.g ESC)
!  * Single characters within single quotes (e.g. 'A')
!
!     Repeat counts are in decimal.
!PAGE
!
!     Some  items  are in fact truth values.  They may be specified with
! the keywords TRUE, YES, FALSE and NO as required.  TRUE is  equivalent
! to YES, and FALSE is equivalent to NO.
!PAGE
!
!     Keywords may be abbreviated as long as they remain unambiguous.  A
! null sequence (i.e. no characters at all) may be entered by giving the
! keyword  and  the equals sign only.  This should be done if a terminal
! does not support a particular operation.
!
!     A  summary  of available keywords may be obtained by typing '?' in
! response to the 'Ed:' prompt.  These keywords are:
!
!<Quit
!
!     Abandon the edit without updating the characteristics file.
!>
!<.End
!
!     Complete the edit, updating the characteristics file.
!>
!<Name
!
!     Set  the  name  of  the terminal associated with this record.  For
! example:
!
!           Name={7}
!>
!<Columns
!
!     Set  the  number  of  columns available on the terminal associated
! with this record.  This will often  be  used  to  update  the  ITWIDTH
! option setting.
!>
!<Lines
!
!     Set  the  number of lines available on the screen of this terminal
! type.  A value of zero should be used for hardcopy devices.
!>
!<Leadin
!
!     Set the lead-in sequence for this terminal type.
!>
!<Clear
!
!     Set the  clear  screen  sequence  for  this  terminal  type.   The
! sequence should also return the cursor to the 'home' position.
!>
!<Chome
!
!     Set the 'home' sequence for this terminal type.
!>
!<Endline
!
!     Set  the  sequence which will cause the terminal to clear the rest
! of the current line from the current cursor position.
!>
!<Endscreen
!
!     Set the sequence which will cause the terminal to clear  the  rest
! of the screen from the current cursor position.
!>
!<Initialise
!
!     Set  the sequence which will initialise the terminal in a sensible
! way.  Preferred settings are:
!
!            * Roll mode
!            * Protection off
!>
!<Interrupt
!
!     Set  the  single  character  that  is  the  preferred one for user
! programs to select as the INT: key.  This may not  always  be  ESCape,
! because this key often has a special meaning to the terminal hardware.
!>
!<Cursor up
!
!     Set  the  sequence  which  will cause the cursor to move up by one
! line.
!>
!<Cursor down
!
!     Set the sequence which will cause the cursor to move down  by  one
! line.
!>
!<Cursor left
!
!     Set  the  sequence which will cause the cursor to move left by one
! character position.
!>
!<Cursor right
!
!     Set the sequence which will cause the cursor to move right by  one
! character position.
!>
!<Insert line
!
!     Set  the sequence which will insert a blank line on the display at
! the current cursor position.
!>
!<Delete line
!
!     Set the sequence which will remove a line from the display at  the
! current cursor position.
!>
!<Home at top
!
!     Indicate whether the Home operation leaves the cursor at  the  top
! line of the screen.  The setting is TRUE if so, otherwise FALSE.
!>
!<Can do page mode
!
!     Indicate  whether  the  terminal  is  capable of operating in page
! mode.  The setting is TRUE if so, otherwise FALSE.
!>
!<Xbase
!
!     The  value  to be added to an X coordinate value before use.  This
! is a single byte.
!>
!<Ybase
!
!     The value to be added to a Y coordinate value before use.  This is
! a single byte.
!>
!<X intro
!
!     The string that introduces the X coordinate setting string, or the
! whole coordinate setting string for those  terminals  where  both  are
! lumped together.
!>
!<Y intro
!
!     The  string that introduces the Y coordinate setting string.  This
! will be null if both settings are lumped together.
!>
!<Y first
!
!    Indicate whether the Y coordinate setting comes before or after the
! X coordinate setting.  The setting is TRUE if the Y  coordinate  value
! is first, otherwise FALSE.
!
!     Note  that  the  column  number  is the X coordinate, and the line
! number is the Y coordinate.
!>
!<Auto wrap
!     Indicate  whether  the  terminal   automatically   'wraps   round'
! (generates  a  carriage  return, linefeed sequence) when an attempt is
! made to write a character beyond the last column of the screen.
!
!     The setting is TRUE if the terminal behaves like  this,  otherwise
! FALSE.
!>
!<Unix name
!     The  two  character  abbreviation commonly used by UNIX systems to
! describe this type of terminal.
!>
!<Start standout mode
!     Set  the  sequence  which  will  cause  the  terminal  to  display
! subsequent output in 'standout' or 'highlighted' mode.
!>
!<End standout
!     Set the sequence which will end 'standout' or 'highlighted' mode.
!>
!<Cursor position
!
!     Set  the  sequence  which  indicates precisely how to position the
! cursor at a given point on the screen.
!
! The string used is precisely that which needs to be sent to the screen
! in order to position the cursor appropriately. Variable information is
! indicated  by  an  escape  character, '%', which indicates that row or
! column values are to be inserted in a  specified  format. The  row  is
! normally  sent  first, followed by the column (this may be reversed by
! the %i escape). Escape sequences are  also  used  to  side-effect  the
! string.
!PAGE
!
! Escape sequences are as follows:
!
!    %d  -  Minimum width ASCII representation of number
!           (like 'write(n,1) in IMP)
!    %2  -  Same as %d, but padded to a width of 2
!    %3  -  Same as %d, but padded to a width of 3
!    %.  -  Output of value as stored (like a %byteinteger)
!    %+x -  Same as %., but with the character 'x' added to the value
!    %r  -  Reverses the order of the 'x' and 'y' values, but generates
!           no output
!    %i  -  Increments both the 'x' and the 'y' value, but generates no
!           output
!    %%  -  Outputs a '%'
!>
!<Print
!
!     Print  the  current  state  of  the terminal record, then continue
! editing.
!>
!>
!<Print
!
!     The form of this command is:
!
!           PRINT n
!
! The   action   is   to  print  the  contents  of  record  'n'  in  the
! characteristics file. If 'n' is not specified, the prompt
!
!           Which:
!
! is issued, and the response should be a valid terminal record  number,
! or .ALL to print all the records currently in use.
!>
!<New
!
!     The form of this command is:
!
!           NEW
!
! The  action  is to locate an empty terminal record, and then to invoke
! the Edit action (see above) to fill it in.  The type number  allocated
! to the record is output for future reference.
!>
!<Copy
!
!     The form of this command is:
!
!           COPY
!
! The  action is to copy a complete terminal record (apart from the name
! field) to another (possibly empty) record. It is useful when more than
! one record has a very similar structure.  Prompts are issued  for  the
! type numbers of the source and destination records.
!>
!<Delete
!
!     The form of this command is:
!
!           DELETE
!
! The  action is to clear out a terminal record, and free it for re-use.
! This command is only needed when a particular type of terminal  is  no
! longer  in use by any user.  A prompt is issued for the type number of
! the terminal whose record is to be deleted.
!>
!<Dump
!
!     The form of this command is:
!
!           DUMP
!
! The  action  is to dump the current state of the characteristics file,
! to a specified character file.  This is useful as a backup, and if the
! format of the characteristics file is to be changed.
!
!     A prompt is issued for the name of the output file.
!>
!<Cmode
!
!     The form of this command is:
!
! The action is to set the output mode for subsequent Print commands  so
! that bytes are represented as characters and ASCII mnemonics.
!>
!<Nmode
!
!     The form of this command is:
!
! The  action is to set the output mode for subsequent Print commands so
! that bytes are represented as hexadecimal numbers.
!>
!<Stop
!
!     The form of this command is:
!
!           STOP
!
! The action is to cause an immediate exit from the program.
!>
!
!***********************************************************************
!*
!*       Program to create and edit terminal characteristics file
!*
!*             R.D. Eager   University of Kent   MCMLXXXVI
!*
!***********************************************************************
!
!
!***********************************************************************
!*
!*          Constants
!*
!***********************************************************************
!
constantinteger  no = 0, yes = 1
constantinteger  inchan = 1, outchan = 2
constantinteger  character mode = 0, number mode = 1
constantinteger  max space = 224;       ! Space in terminal record
constantinteger  ssdatafiletype = 4;    ! Subsystem file type
constantinteger  maxterm = 31;          ! Max number of terminal types
constantstring (11) default chsfile = "TERMCHSFILE"
                                        ! Default name for characteristics file
constantstring (10) temp chsfile = "T#TEMPCHS"
constantinteger  hdsize = 32;           ! Size of a file header
constantinteger  maxcom = 12;           ! Number of commands
constantstring (10)array  com(1:maxcom) = c 
"INITIALISE","EDIT","PRINT","NEW","COPY","DELETE","DUMP","SUMMARY",
"CMODE","NMODE","QUIT","STOP"
constantinteger  maxedcom = 33;         ! Number of editing commands
constantstring (10)array  edcom(1:maxedcom) = c 
"QUIT",".END","NAME","COLUMNS","LINES","LEADIN","CLEAR","CHOME",
"ENDLINE","ENDSCREEN","INITIALISE","INTERRUPT","CUP","CDOWN","CLEFT",
"CRIGHT","INSERTLINE","DELETELINE","HOMETOP","PAGEMODE","ROLLMODE",
"CANPAGE","XBASE","YBASE","XINTRO","YINTRO","YFIRST","AUTOWRAP",
"UNIXNAME","SSTAND","ESTAND","CPOSITION","PRINT"
constantstring (1) snl = "
"
constantstring (3)array  ascode(0:32) = c 
"NUL","SOH","STX","ETX","EOT","ENQ","ACK","BEL", "BS", "HT", "LF", "VT",
 "FF", "CR", "SO", "SI","DLE","DC1","DC2","DC3","DC4","NAK","SYN","ETB",
"CAN", "EM","SUB","ESC", "FS", "GS", "RS", "US", "SP"
!
!
!***********************************************************************
!*
!*          Record and array formats
!*
!***********************************************************************
!
recordformat  fdf(integer  link,dsnum,byteinteger  status,accessroute,
                  valid action,cur state,mode of use,mode,file org,
                  dev code,rec type,flags,lm,rm,integer  asvar,arec,
                  recsize,minrec,maxrec,maxsize,lastrec,conad,currec,
                  cur,end,transfers,darecnum,cursize,datastart,
                  string (31) iden,integer  keydesc0,keydesc1,
                  recsizedesc0,recsizedesc1,byteinteger  f77flag,
                  f77form,f77access,f77status,integer  f77recl,f77nrec,
                  idaddr,byteinteger  f77blank,f77ufd,spare1,spare2)
recordformat  hf(integer  dataend,datastart,filesize,filetype,
                 sum,datetime,format,records)
recordformat  rf(integer  conad,filetype,datastart,dataend)
recordformat  termf(byteinteger  type,name,columns,lines,leadin,
                    clearscreen,home,endofline,endofscreen,init,int,
                    cursor up,cursor down,cursor left,cursor right,
                    insert line,delete line,home at top,page mode,
                    roll mode,can do page mode,xbase,ybase,xintro,
                    yintro,yfirst,auto wrap,uname,start standout,
                    end standout,cursor pos,
                    (byteinteger  string ptr or  c 
                    string (max space) string space))
!
ownrecord (termf)arrayformat  termaf(1:maxterm)
!
!
!***********************************************************************
!*
!*          Subsystem references
!*
!***********************************************************************
!
systemroutinespec  connect(string (31) file,integer  mode,hole,
                           prot,record (rf)name  r,integername  flag)
systemroutinespec  define(integer  chan,string (31) iden,
                          integername  afd,flag)
systemroutinespec  disconnect(string (31) file,integername  flag)
systemstringfunctionspec  failuremessage(integer  mess)
systemstringfunctionspec  htos(integer  value,places)
externalintegerfunctionspec  instream
systemintegerfunctionspec  iocp(integer  ep,parm)
systemstringfunctionspec  itos(integer  n)
systemintegermapspec  mapssfd(integer  dsnum)
systemroutinespec  move(integer  length,from,to)
systemroutinespec  newgen(string (31) file,newfile,integername  flag)
systemroutinespec  outfile(string (31) file,integer  size,hole,
                           prot,integername  conad,flag)
externalintegerfunctionspec  outpos
externalintegerfunctionspec  outstream
systemintegerfunctionspec  parmap
externalroutinespec  prompt(string (255) s)
systemintegerfunctionspec  pstoi(string (63) s)
systemroutinespec  setfname(string (63) s)
systemroutinespec  setpar(string (255) s)
externalroutinespec  set return code(integer  i)
systemstringfunctionspec  spar(integer  n)
systemroutinespec  uctranslate(integer  ad,len)
!
!
!***********************************************************************
!*
!*          Service routines
!*
!***********************************************************************
!
routine  closestream(integer  chan)
! Private version of 'closestream'.  Does  not  give  an  error  if  the
! operation fails.
integer  flag
!
return  unless  instream # chan # outstream
flag = iocp(16,chan)
end ;   ! of closestream
!
!-----------------------------------------------------------------------
!
routine  clearstream(integer  chan)
! Clears  out  a  channel  definition, but does not give an error if the
! channel was not defined.
record (fdf)name  f
!
if  mapssfd(chan) # 0 then  start 
   f == record(mapssfd(chan))
   if  f_status = 0 then  start 
      mapssfd(chan) = 0
      f_dsnum = 0;                      ! Mark descriptor as free
   finish 
finish 
end ;   ! of clearstream
!
!-----------------------------------------------------------------------
!
integerfunction  stoh(string (2) s)
! Converts the string 's' to a number. 's' is in hexadecimal.
integer  c,i,j,n
!
uctranslate(addr(s)+1,length(s))
n = 0
for  i = 1,1,length(s) cycle 
   c = charno(s,i)
   if  '0' <= c <= '9' then  start 
      j = c - '0'
   finish  else  c 
   if  'A' <= c <= 'F' then  start 
      j = c - 'A' + 10
   finish  else  result  = -1;          ! Invalid character
   n = (n << 4)!j
repeat 
result  = n
end ;   ! of stoh
!
!-----------------------------------------------------------------------
!
integerfunction  matchstrings(stringname  a,string (255) b)
! Performs minimum-length string comparison.
integer  l
!
l = length(a)
result  = 0 if  length(b) < l
length(b) = l
if  a = b then  result  = yes else  result  = no
end ;   ! of matchstrings
!
!-----------------------------------------------------------------------
!
integerfunction  getcode(string (4) s)
! Yields the ASCII code of the character described by 's'.  Yields -1 if
! 's' is not recognised.
integer  i
!
if  length(s) = 3 and  charno(s,1) = '''' = charno(s,3) then  start 
   result  = charno(s,2)
finish 
!
if  length(s) = 4 and  charno(s,1) = '''' = charno(s,4) and  charno(s,2) = '^' then  start 
   i = charno(s,3)
   if  'a' <= i <= 'z' then  i = i - 'a' + 'A'
   if  '@' <= i <= '_' then  result  = i - 64 else  result  = -1
finish 
!
if  length(s) = 4 and  charno(s,1) = '''' = charno(s,2) and  c 
                       charno(s,3) = '''' = charno(s,4) then  result  = ''''
!
if  2 <= length(s) <= 3 then  start 
   uctranslate(addr(s)+1,length(s))
   result  = x'7f' if  s = "DEL";       ! Special case
   !
   for  i = 0,1,32 cycle 
      result  = i if  s = ascode(i)
   repeat 
finish 
result  = -1
end ;   ! of getcode
!
!-----------------------------------------------------------------------
!
stringfunction  readchs(string (255) s,integername  flag)
! Converts the string 's', which is a textual representation of a set of
! control bytes in hexadecimal, into those bytes themselves.
integer  i,l,ch,count
string (255) chs,s0,s1,s2,s3
!
length(chs) = 0
flag = 0
result  = "" if  s = ""
!
l = 0
cycle 
   if  not  s -> s0.(",").s then  start 
      s0 = s
      s = ""
   finish 
   if  s0 = "" then  start 
      printstring("Null field".snl)
      flag = 1
      exit 
   finish 
   if  s0 -> s1.("(").s2.(")").s3 and  s3 = "" then  start 
      count = pstoi(s2)
      if  count <= 0 then  start 
         printstring("Invalid repeat count '".s2."'".snl)
         flag = 1
         exit 
      finish 
      s0 = s1
   finish  else  count = 1
   ch = getcode(s0);                    ! See if it is a mnemonic
   if  ch < 0 then  start ;             ! It is not - try for number
      if  length(s0) > 1 and  (charno(s0,1) = 'X' or  charno(s0,1) = 'x') then   start 
         s0 = substring(s0,2,length(s0))
      finish 
      if  length(s0) > 2 then  start 
         printstring("Field '".s0."' too large".snl)
         flag = 1
         exit 
      finish 
      ch = stoh(s0)
      if  ch < 0 then  start 
         printstring("Invalid number '".s0."'".snl)
         flag = 1
         exit 
      finish 
   finish 
   for  i = 1,1,count cycle 
      l = l + 1
      charno(chs,l) = ch
   repeat 
repeat  until  s = ""
length(chs) = l
result  = chs
end ;   ! of readchs
!
!-----------------------------------------------------------------------
!
integerfunction  readbool(string (255) s)
! Yields the boolean value described  by  's',  or  -1  if  's'  is  not
! recognised.  TRUE and YES are represented by 1, while FALSE and NO are
! represented by 0.
uctranslate(addr(s)+1,length(s))
if  matchstrings(s,"TRUE") = yes then  result  = yes else  c 
if  matchstrings(s,"YES") = yes then  result  = yes else  c 
if  matchstrings(s,"FALSE") = yes then  result  = no else  c 
if  matchstrings(s,"NO") = yes then  result  = no else  c 
printstring("Unrecognised boolean value".snl)
result  = -1;                           ! Error
end ;   ! of readbool
!
!-----------------------------------------------------------------------
!
routine  to term string(record (termf)name  term,byteintegername  field,
                        string (63) value)
! Stores  'value'  in the terminal record 'term', returning its key byte
! in 'field'.
if  value = "" then  start 
   field = 0
finish  else  c 
if  length(value) + term_string ptr > max space then  start 
   printstring("No more space for strings for this terminal type".snl)
   field = 0
else 
   field = term_string ptr
   string(addr(term_string space)+field) = value
   term_string ptr = field + length(value) + 1
finish 
end ;   ! of to term string
!
!-----------------------------------------------------------------------
!
stringfunction  term string(record (termf)name  term,byteinteger  ptr)
! Returns  the string associated with the item described by 'ptr' in the
! record 'term'.
result  = "" if  ptr = 0
result  = string(addr(term_string space)+ptr)
end ;   ! of term string
!
!-----------------------------------------------------------------------
!
stringfunction  character(integer  ch,mode)
! Yields a string describing 'ch', in a form which depends on 'mode'.
string (4) res
!
if  mode = character mode then  start 
      if  0 <= ch <= 32 then  result  = ascode(ch) else  c 
      if  ch = x'7f' then  result  = "DEL" else  start 
         res = "'".tostring(ch)."'"
         res = res."'" if  ch = ''''
         result  = res
      finish 
else 
   result  = htos(ch,2)
finish 
end ;   ! of character
!
!-----------------------------------------------------------------------
!
stringfunction  chs string(record (termf)name  term,byteinteger  ptr,
                           integer  mode)
! Yields a printable version of the  item  described  by  'ptr'  in  the
! record 'term'.
integer  last,i,count,flag,ch
string (63) value
string (255) res
!
res = ""
value = term string(term,ptr)
result  = "" if  length(value) = 0
last = -1
count = 0
flag = no
for  i = 1,1,length(value) cycle 
   ch = charno(value,i)
   if  ch = last then  count = count + 1 else  start 
      if  last # -1 then  start 
         res = res."," if  flag = yes
         flag = yes
         res = res.character(last,mode)
         if  count # 1 then  res = res."(".itos(count).")"
      finish 
      last = ch
      count = 1
   finish 
repeat 
res = res."," if  flag = yes
res = res.character(last,mode)
if  count # 1 then  res = res."(".itos(count).")"
result  = res
end ;   ! of chs string
!
!-----------------------------------------------------------------------
!
stringfunction  boolstring(byteinteger  value)
! Yields a string describing the boolean item 'value'.
if  value = yes then  result  = "TRUE" else  result  = "FALSE"
end ;   ! of boolstring
!
!-----------------------------------------------------------------------
!
routine  itemi(string (15) title,record (termf) term,byteinteger  ptr,
               integer  mode)
! Prints a non-graphic string item.
printstring(title)
spaces(31-outpos)
printstring(": ")
if  ptr = 0 then  printstring("<null>") else  start 
   printstring(chs string(term,ptr,mode))
finish 
newline
end ;   ! of itemi
!
!-----------------------------------------------------------------------
!
routine  itemn(string (31) title,byteinteger  info)
! Prints a decimal numeric item.
printstring(title)
spaces(31-outpos)
printstring(": ".itos(info).snl)
end ;   ! of itemn
!
!-----------------------------------------------------------------------
!
routine  itemc(string (31) title,byteinteger  info,integer  mode)
! Prints a single character item.
printstring(title)
spaces(31-outpos)
printstring(": ".character(info,mode).snl)
end ;   ! of itemc
!
!-----------------------------------------------------------------------
!
routine  itemb(string (31) title,byteinteger  info)
! Prints a boolean item.
printstring(title)
spaces(31-outpos)
printstring(": ")
if  info = no then  printstring("False") else  printstring("True")
newline
end ;   ! of itemb
!
!-----------------------------------------------------------------------
!
routine  items(string (31) title,record (termf)name  term,
               byteinteger  ptr)
! Prints a graphic string item.
printstring(title)
spaces(31-outpos)
printstring(": ".term string(term,ptr).snl)
end ;   ! of items
!
!-----------------------------------------------------------------------
!
routine  printterm(record (termf)name  term,integer  mode)
! Prints complete details about one terminal type.
itemn("Type",term_type)
items("Name",term,term_name)
itemn("Columns",term_columns)
itemn("Lines",term_lines)
itemi("Lead in",term,term_leadin,mode)
itemi("Clear Screen",term,term_clearscreen,mode)
itemi("Home",term,term_home,mode)
itemi("End of line",term,term_endofline,mode)
itemi("End of screen",term,term_endofscreen,mode)
itemi("Initialise",term,term_init,mode)
itemc("Interrupt",term_int,mode)
itemi("Cursor up",term,term_cursor up,mode)
itemi("Cursor down",term,term_cursor down,mode)
itemi("Cursor left",term,term_cursor left,mode)
itemi("Cursor right",term,term_cursor right,mode)
itemi("Insert line",term,term_insert line,mode)
itemi("Delete line",term,term_delete line,mode)
itemb("Home at top",term_home at top)
itemi("Page mode",term,term_page mode,mode)
itemi("Roll mode",term,term_roll mode,mode)
itemb("Can do page mode",term_can do page mode)
itemc("X base",term_xbase,mode)
itemc("Y base",term_ybase,mode)
itemi("X intro",term,term_xintro,mode)
itemi("Y intro",term,term_yintro,mode)
itemb("Y first",term_yfirst)
itemb("Auto wrap",term_auto wrap)
items("Unix name",term,term_uname)
itemi("Start standout",term,term_start standout,mode)
itemi("End standout",term,term_end standout,mode)
itemi("Cursor position",term,term_cursor pos,mode)
end ;   ! of printterm
!
!-----------------------------------------------------------------------
!
routine  readline(stringname  s)
! Reads  one  line from the currently selected input into 's'.  Trailing
! spaces and completely blank lines are suppressed.
integer  c
!
s = ""
cycle 
   cycle 
      readsymbol(c)
      exit  if  c = nl
      s <- s.tostring(c)
   repeat 
   while  length(s) > 0 and  charno(s,length(s)) = ' ' cycle 
      length(s) = length(s) - 1
   repeat 
   exit  unless  s = ""
repeat 
end ;   ! of readline
!
!-----------------------------------------------------------------------
!
routine  despace(stringname  s)
! Removes embedded spaces from 's'.
string (255) work1,work2
!
s = work1.work2 while  s -> work1.(" ").work2
end ;   ! of despace
!
!-----------------------------------------------------------------------
!
integerfunction  decode command(stringname  op,stringarrayname  com,
                                integer  max)
! Checks  the  command  'op'  against  its  possible values in the table
! 'com'.  The number of items in 'com' is given by 'max'.  There must be
! exactly one match, or a negative result is returned after  printing  a
! suitable message.  Otherwise, the command index in 'com' is yielded.
integer  i,j
!
uctranslate(addr(op)+1,length(op))
j = -1
for  i = 1,1,max cycle 
   if  matchstrings(op,com(i)) = yes then  start 
      if  j # -1 then  start ;          ! Already seen a match
         printstring("Ambiguous command".snl)
         j = -2
         exit 
      finish 
      j = i
   finish 
repeat 
if  j = -1 then  printstring("Unrecognised command".snl)
result  = j
end ;   ! of decode command
!
!-----------------------------------------------------------------------
!
routine  printhelp(string (31) title,stringarrayname  com,integer  max)
! Prints  a  helpful  summary  of the commands in the 'com', which has a
! length of 'max'.  'title' is output as an explanatory initial line.
integer  i
!
printstring(title.":-".snl)
for  i = 1,1,max cycle 
   printstring(com(i))
   printsymbol(',') unless  i = max
   newline if  outpos > 60 and  i # max
repeat 
newline unless  outpos = 0
end ;   ! of printhelp
!
!-----------------------------------------------------------------------
!
integerfunction  setc(stringname  param,byteintegername  value)
integer  flag
string (255) s
!
s = readchs(param,flag)
result  = no if  flag # 0
if  length(s) # 1 then  start 
   printstring("Only one character allowed".snl)
   result  = no
finish 
value = charno(s,1)
result  = yes
end ;   ! of setc
!
!-----------------------------------------------------------------------
!
routine  setn(stringname  param,byteintegername  value,oldvalue)
integer  i
!
i = pstoi(param)
if  i < 0 then  printstring("Invalid number".snl) else  start 
   value <- i
   oldvalue = 0
finish 
end ;   ! of setn
!
!-----------------------------------------------------------------------
!
integerfunction  edit function(record (termf)name  term,
                               integer  interactive,silent)
! Performs an edit on a particular terminal record.
integer  j,flag,home at top set,can do page mode set,int set,xbase set
integer  ybase set,y first set,auto wrap set,printflag
record (termf) tw
string (255) s,op,param
switch  edsw(1:maxedcom)
!
tw = 0
tw_type = term_type
tw_string ptr = 1
int set = no
home at top set = no
can do page mode set = no
xbase set = no
ybase set = no
yfirst set = no
auto wrap set = no
!
if  term_name # 0 and  silent = no then  start 
   printstring("Editing record for """.term string(term,term_name)."""".snl)
finish 
!
cycle 
   prompt("Ed: ")
   readline(s)
   if  s = "?" then  start 
      printhelp("Editing commands are",edcom,maxedcom)
      continue 
   finish 
   !
   if  not  s -> op.("=").param then  start 
      op = s
      param = ""
   finish 
   despace(op)
   printflag = 0
   !
   j = decode command(op,edcom,maxedcom)
   continue  if  j < 0
   !
   -> edsw(j)
   !
edsw(1):                                ! Quit
   if  interactive = yes then  printstring("Edit abandoned".snl)
   result  = 1
   !
edsw(2):                                ! .End
   to term string(tw,tw_name,term string(term,term_name)) if  term_name # 0
   tw_columns = term_columns if  term_columns # 0
   tw_lines = term_lines if  term_lines # 0
   to term string(tw,tw_leadin,term string(term,term_leadin)) if  term_leadin # 0
   to term string(tw,tw_clearscreen,term string(term,term_clearscreen)) if  term_clearscreen # 0
   to term string(tw,tw_home,term string(term,term_home)) if  term_home # 0
   to term string(tw,tw_endofline,term string(term,term_endofline)) if  term_endofline # 0
   to term string(tw,tw_endofscreen,term string(term,term_endofscreen)) if  term_endofscreen # 0
   to term string(tw,tw_init,term string(term,term_init)) if  term_init # 0
   tw_int = term_int unless  int set = yes
   to term string(tw,tw_cursor up,term string(term,term_cursor up)) if  term_cursor up # 0
   to term string(tw,tw_cursor down,term string(term,term_cursor down)) if  term_cursor down # 0
   to term string(tw,tw_cursor left,term string(term,term_cursor left)) if  term_cursor left # 0
   to term string(tw,tw_cursor right,term string(term,term_cursor right)) if  term_cursor right # 0
   to term string(tw,tw_insert line,term string(term,term_insert line)) if  term_insert line # 0
   to term string(tw,tw_delete line,term string(term,term_delete line)) if  term_delete line # 0
   tw_home at top = term_home at top unless  home at top set = yes
   to term string(tw,tw_page mode,term string(term,term_page mode)) if  term_page mode # 0
   to term string(tw,tw_roll mode,term string(term,term_roll mode)) if  term_roll mode # 0
   tw_can do page mode = term_can do page mode unless  can do page mode set = yes
   tw_xbase = term_xbase unless  xbase set = yes
   tw_ybase = term_ybase unless  ybase set = yes
   to term string(tw,tw_xintro,term string(term,term_xintro)) if  term_xintro # 0
   to term string(tw,tw_yintro,term string(term,term_yintro)) if  term_yintro # 0
   tw_yfirst = term_yfirst unless  yfirst set = yes
   tw_auto wrap = term_auto wrap unless  auto wrap set = yes
   to term string(tw,tw_uname,term string(term,term_uname)) if  term_uname # 0
   to term string(tw,tw_start standout,term string(term,term_start standout)) if  term_start standout # 0
   to term string(tw,tw_end standout,term string(term,term_end standout)) if  term_end standout # 0
   to term string(tw,tw_cursor pos,term string(term,term_cursor pos)) if  term_cursor pos # 0
   term = tw
   if  interactive = yes and  printflag = 0 then  printstring("Edit completed".snl)
   result  = printflag
   !
edsw(3):                                ! Name
   to term string(tw,tw_name,param)
   term_name = 0
   continue 
   !
edsw(4):                                ! Columns
   setn(param,tw_columns,term_columns)
   continue 
   !
edsw(5):                                ! Lines
   setn(param,tw_lines,term_lines)
   continue 
   !
edsw(6):                                ! Leadin
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_leadin,s)
      term_leadin = 0
   finish 
   continue 
   !
edsw(7):                                ! Clearscreen
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_clearscreen,s)
      term_clearscreen = 0
   finish 
   continue 
   !
edsw(8):                                ! Home
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_home,s)
      term_home = 0
   finish 
   continue 
   !
edsw(9):                                ! Endline
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_endofline,s)
      term_endofline = 0
   finish 
   continue 
   !
edsw(10):                               ! Endscreen
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_endofscreen,s)
      term_endofscreen = 0
   finish 
   continue 
   !
edsw(11):                               ! Initialise
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_init,s)
      term_init = 0
   finish 
   continue 
   !
edsw(12):                               ! Interrupt
   int set = setc(param,tw_int)
   continue 
   !
edsw(13):                               ! Cursor up
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_cursor up,s)
      term_cursor up = 0
   finish 
   continue 
   !
edsw(14):                               ! Cursor down
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_cursor down,s)
      term_cursor down = 0
   finish 
   continue 
   !
edsw(15):                               ! Cursor left
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_cursor left,s)
      term_cursor left = 0
   finish 
   continue 
   !
edsw(16):                               ! Cursor right
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_cursor right,s)
      term_cursor right = 0
   finish 
   continue 
   !
edsw(17):                               ! Insert line
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_insert line,s)
      term_insert line = 0
   finish 
   continue 
   !
edsw(18):                               ! Delete line
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_delete line,s)
      term_delete line = 0
   finish 
   continue 
   !
edsw(19):                               ! Home at top
   flag = readbool(param)
   if  flag >= 0 then  start 
      tw_home at top = flag
      home at top set = yes
   finish 
   continue 
   !
edsw(20):                               ! Set page mode
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_page mode,s)
      term_page mode = 0
   finish 
   continue 
   !
edsw(21):                               ! Set roll mode
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_roll mode,s)
      term_roll mode = 0
   finish 
   continue 
   !
edsw(22):                               ! Can do page mode
   flag = readbool(param)
   if  flag >= 0 then  start 
      tw_can do page mode = flag
      can do page mode set = yes
   finish 
   continue 
   !
edsw(23):                               ! X base
   xbase set = setc(param,tw_xbase)
   continue 
   !
edsw(24):                               ! Y base
   ybase set = setc(param,tw_ybase)
   continue 
   !
edsw(25):                               ! X intro
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_xintro,s)
      term_xintro = 0
   finish 
   continue 
   !
edsw(26):                               ! Y intro
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_yintro,s)
      term_yintro = 0
   finish 
   continue 
   !
edsw(27):                               ! Y coordinate first
   flag = readbool(param)
   if  flag >= 0 then  start 
      tw_yfirst = flag
      yfirst set = yes
   finish 
   continue 
   !
edsw(28):                               ! Auto wrap at line end
   flag = readbool(param)
   if  flag >= 0 then  start 
      tw_auto wrap = flag
      auto wrap set = yes
   finish 
   continue 
   !
edsw(29):                               ! Unix name for terminal
   to term string(tw,tw_uname,param)
   term_uname = 0
   continue 
   !
edsw(30):                               ! Start standout mode
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_start standout,s)
      term_start standout = 0
   finish 
   continue 
   !
edsw(31):                               ! End standout mode
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_end standout,s)
      term_end standout = 0
   finish 
   continue 
   !
edsw(32):                               ! Cursor position
   s = readchs(param,flag)
   if  flag = 0 then  start 
      to term string(tw,tw_cursor pos,s)
      term_cursor pos = 0
   finish 
   continue 
   !
edsw(33):                               ! Print record
   printflag = -1
   -> edsw(2);                          ! Use code for .END
repeat 
end ;   ! of edit function
!
!-----------------------------------------------------------------------
!
routine  dump function(record (termf)arrayname  terms)
! Dumps  the contents of all non-empty terminal records to the currently
! selected output, in a suitable form for re-input at a later time.
integer  i
record (termf)name  t
!
printstring("INITIALISE".snl)
for  i = 1,1,maxterm cycle 
   t == terms(i)
   continue  if  t_name = 0
   printstring("EDIT".snl.itos(i).snl)
   printstring("NAME=".term string(t,t_name).snl)
   printstring("COLUMNS=".itos(t_columns).snl)
   printstring("LINES=".itos(t_lines).snl)
   printstring("LEADIN=".chs string(t,t_leadin,number mode).snl)
   printstring("CLEAR=".chs string(t,t_clearscreen,number mode).snl)
   printstring("CHOME=".chs string(t,t_home,number mode).snl)
   printstring("ENDLINE=".chs string(t,t_endofline,number mode).snl)
   printstring("ENDSCREEN=".chs string(t,t_endofscreen,number mode).snl)
   printstring("INITIALISE=".chs string(t,t_init,number mode).snl)
   printstring("INTERRUPT=".htos(t_int,2).snl)
   printstring("CUP=".chs string(t,t_cursor up,number mode).snl)
   printstring("CDOWN=".chs string(t,t_cursor down,number mode).snl)
   printstring("CLEFT=".chs string(t,t_cursor left,number mode).snl)
   printstring("CRIGHT=".chs string(t,t_cursor right,number mode).snl)
   printstring("INSERTLINE=".chs string(t,t_insert line,number mode).snl)
   printstring("DELETELINE=".chs string(t,t_delete line,number mode).snl)
   printstring("HOMETOP=".bool string(t_home at top).snl)
   printstring("PAGEMODE=".chs string(t,t_page mode,number mode).snl)
   printstring("ROLLMODE=".chs string(t,t_roll mode,number mode).snl)
   printstring("CANPAGE=".bool string(t_can do page mode).snl)
   printstring("XBASE=".htos(t_xbase,2).snl)
   printstring("YBASE=".htos(t_ybase,2).snl)
   printstring("XINTRO=".chs string(t,t_xintro,number mode).snl)
   printstring("YINTRO=".chs string(t,t_yintro,number mode).snl)
   printstring("YFIRST=".bool string(t_yfirst).snl)
   printstring("AUTOWRAP=".bool string(t_auto wrap).snl)
   printstring("UNIXNAME=".term string(t,t_uname).snl)
   printstring("SSTAND=".chs string(t,t_start standout,number mode).snl)
   printstring("ESTAND=".chs string(t,t_end standout,number mode).snl)
   printstring("CPOSITION=".chs string(t,t_cursor pos,number mode).snl)
   printstring(".END".snl)
repeat 
printstring("STOP".snl)
end ;   ! of dump function
!
!-----------------------------------------------------------------------
!
integerfunction  connect chsfile(string (31) file,record (rf)name  rr)
! Connects  and  validates  the  terminal  characteristics  file 'file'.
! Yields zero if OK, otherwise a standard error code.
integer  flag
record (hf)name  r
!
connect(file,1,0,0,rr,flag)
if  flag = 0 then  start 
   r == record(rr_conad)
   unless  r_filetype = ssdatafiletype and  r_format = 3 then  start 
      setfname(file)
      flag = 267;                       ! Invalid filetype
   finish 
finish 
result  = flag
end ;   ! of connect chsfile
!
!-----------------------------------------------------------------------
!
integerfunction  make chsfile(string (31) file,integer  size,
                              integername  conad)
! Creates  an  empty  characteristics  file  'file',  of  'size'   bytes
! including header.  The connect address is returned in 'conad', and the
! result yielded is zero for success, otherwise a standard error code.
integer  flag
record (hf)name  r
!
outfile(file,size,0,0,conad,flag)
if  flag = 0 then  start 
   r == record(conad)
   r_dataend = r_filesize
   r_filetype = ssdatafiletype
   r_format = 3
finish 
result  = flag
end ;   ! of make chsfile
!
!
!***********************************************************************
!*
!*          T E R M C H S
!*
!***********************************************************************
!
externalroutine  termchs(string (255) parms)
integer  i,j,conad,flag,type,newtype,afd,print mode,interactive,silent
string (31) file,commands
string (255) action,s
record (rf) rr
record (hf)name  r
record (termf)name  t,newt
record (termf)arrayname  terms
switch  sw(1:maxcom)
!
set return code(1000)
if  sizeof(t) # 256 then  start 
   setfname("'term' record length is wrong")
   flag = 233;                          ! General error
   -> err2
finish 
!
if  parms = "?" then  start 
   printstring("Parameters are:-".snl)
   printstring("TERMCHS(characteristics file,commands file)".snl)
   set return code(0)
   return 
finish 
!
setpar(parms)
if  parmap > 3 then  start 
   flag = 263;                          ! Wrong number of parameters
   -> err2
finish 
file <- spar(1)
file = default chsfile if  file = ""
commands <- spar(2)
if  commands # "" then  start 
   define(inchan,commands,afd,flag)
   -> err2 if  flag # 0
   selectinput(inchan)
finish 
!
print mode = number mode
cycle 
   selectoutput(0)
   closestream(outchan)
   clearstream(outchan)
   prompt("Action: ")
   readline(action)
   if  action -> action.(" ").s then  despace(s) else  s = ""
   if  action = "?" then  start 
      printhelp("Actions are",com,maxcom)
      continue 
   finish 
   !
   j = decode command(action,com,maxcom)
   continue  if  j < 0
   !
   -> sw(j)
   !
sw(1):                                  ! Initialise
   flag = make chsfile(file,sizeof(t)*maxterm+hdsize,conad)
   -> err if  flag # 0
   r == record(conad)
   terms == array(conad+r_datastart,termaf)
   for  i = 1,1,maxterm cycle 
      t == terms(i)
      t = 0
      t_type = i
      t_string ptr = 1
   repeat 
   disconnect(file,flag)
   continue 
   !
sw(2):                                  ! Edit
   silent = no
   flag = connect chsfile(file,rr)
   -> err if  flag # 0
   r == record(rr_conad)
   flag = make chsfile(temp chsfile,r_filesize,conad)
   -> err if  flag # 0
   r == record(conad)
   move(rr_dataend-rr_datastart,rr_conad+rr_datastart,conad+r_datastart)
   terms == array(conad+r_datastart,termaf)
   !
   prompt("Which: ")
   cycle 
      readline(s) if  s = ""
      despace(s)
      type = pstoi(s)
      unless  1 <= type <= maxterm then  start 
         printstring("Invalid type number".snl)
         s = ""
      finish  else  exit 
   repeat 
   !
   if  commands = "" then  interactive = yes else  interactive = no
   t == terms(type)
   cycle 
      flag = edit function(t,interactive,silent)
      if  flag < 0 then  start 
         printterm(t,print mode)
         silent = yes
      finish  else  exit 
   repeat 
   if  flag = 0 then  newgen(temp chsfile,file,flag) else  flag = 0
   -> err if  flag # 0
   continue 
   !
sw(3):                                  ! Print
   if  s = "" then  start 
      prompt("Which: ")
      readline(s)
   finish 
   uctranslate(addr(s)+1,length(s))
   flag = connect chsfile(file,rr)
   -> err if  flag # 0
   terms == array(rr_conad+rr_datastart,termaf)
   unless  s -> s.(",").parms then  parms = ".OUT"
   define(outchan,parms,afd,flag)
   -> err if  flag # 0
   selectoutput(outchan)
   s = "" if  matchstrings(s,".ALL") = yes
   if  s # "" then  start 
      type = pstoi(s)
      unless  1 <= type <= maxterm then  start 
         selectoutput(0)
         printstring("Invalid type number".snl)
         continue 
      finish 
      newline
      t == terms(type)
      if  t_name = 0 then  start 
         printstring("Slot for this terminal type is empty".snl)
      else 
         printterm(t,print mode)
      finish 
   else 
      newline
      for  i = 1,1,maxterm cycle 
         t == terms(i)
         continue  if  t_name = 0
         printterm(t,print mode)
         newline
      repeat 
   finish 
   continue 
   !
sw(4):                                  ! New
   flag = connect chsfile(file,rr)
   -> err if  flag # 0
   r == record(rr_conad)
   flag = make chsfile(temp chsfile,r_filesize,conad)
   -> err if  flag # 0
   r == record(conad)
   move(rr_dataend-rr_datastart,rr_conad+rr_datastart,conad+r_datastart)
   terms == array(conad+r_datastart,termaf)
   !
   type = -1
   for  i = 1,1,maxterm cycle 
      t == terms(i)
      if  t_name = 0 then  start 
         type = i
         exit 
      finish 
   repeat 
   if  type = -1 then  start ;          ! No slot found
      setfname("No free terminal records")
      flag = 233;                       ! General error
      -> err
   finish 
   printstring("New terminal type: ".itos(type).snl)
   flag = edit function(t,yes,yes)
   if  flag = 0 then  newgen(temp chsfile,file,flag) else  flag = 0
   -> err if  flag # 0
   continue 
   !
sw(5):                                  ! Copy
   flag = connect chsfile(file,rr)
   -> err if  flag # 0
   r == record(rr_conad)
   flag = make chsfile(temp chsfile,r_filesize,conad)
   -> err if  flag # 0
   r == record(conad)
   move(rr_dataend-rr_datastart,rr_conad+rr_datastart,conad+r_datastart)
   terms == array(conad+r_datastart,termaf)
   !
   prompt("From: ")
   readline(s)
   type = pstoi(s)
   unless  1 <= type <= maxterm then  start 
      printstring("Invalid type number".snl)
      continue 
   finish 
   t == terms(type)
   prompt("To: ")
   readline(s)
   newtype = pstoi(s)
   unless  1 <= type <= maxterm then  start 
      printstring("Invalid type number".snl)
      continue 
   finish 
   newt == terms(newtype)
   i = newt_type;                       ! Save type
   s = term string(newt,newt_name);     ! Save name field
   newt = t
   newt_type = i;                       ! Restore type
   to term string(newt,newt_name,s);    ! Restore name field
   newgen(temp chsfile,file,flag)
   -> err if  flag # 0
   continue 
   !
sw(6):                                  ! Delete
   flag = connect chsfile(file,rr)
   -> err if  flag # 0
   r == record(rr_conad)
   flag = make chsfile(temp chsfile,r_filesize,conad)
   -> err if  flag # 0
   r == record(conad)
   move(rr_dataend-rr_datastart,rr_conad+rr_datastart,conad+r_datastart)
   terms == array(conad+r_datastart,termaf)
   !
   prompt("Which: ")
   readline(s)
   type = pstoi(s)
   unless  1 <= type <= maxterm then  start 
      printstring("Invalid type number".snl)
      continue 
   finish 
   t == terms(type)
   t = 0;                               ! Null implies empty slot
   newgen(temp chsfile,file,flag)
   -> err if  flag # 0
   continue 
   !
sw(7):                                  ! Dump
   flag = connect chsfile(file,rr)
   -> err if  flag # 0
   terms == array(rr_conad+rr_datastart,termaf)
   !
   prompt("Dump to: ")
   readline(s)
   uctranslate(addr(s)+1,length(s))
   define(outchan,s,afd,flag)
   -> err if  flag # 0
   selectoutput(outchan)
   dump function(terms)
   continue 
   !
sw(8):                                  ! Summary
   flag = connect chsfile(file,rr)
   -> err if  flag # 0
   terms == array(rr_conad+rr_datastart,termaf)
   newline
   for  i = 1,1,maxterm cycle 
      t == terms(i)
      continue  if  t_name = 0
      write(i,2)
      printstring(": ".term string(t,t_name).snl)
   repeat 
   newline
   continue 
   !
sw(9):                                  ! Cmode
   print mode = character mode
   continue 
   !
sw(10):                                 ! Nmode
   print mode = number mode
   continue 
   !
sw(11):                                 ! Quit
sw(12):                                 ! Stop
   exit 
   !
err:
   s = failuremessage(flag)
   printstring(substring(s,2,length(s)))
repeat 
!
selectinput(0)
closestream(inchan)
clearstream(inchan)
set return code(0)
return 
!
err2:
printstring(snl."TERMCHS fails -".failuremessage(flag))
selectinput(0)
closestream(inchan)
clearstream(inchan)
set return code(flag)
stop 
end ;   ! of termchs
endoffile