!***********************************************************************
!*
!*             Program to read files from a DEC RSX-11 tape
!*
!*             R.D. Eager   University of Kent    MCMLXXXIII
!*
!***********************************************************************
!
constantinteger  version = 4;           ! Major version number
constantinteger  edit    = 0;           ! Edit number within major version
!
!***********************************************************************
!*
!*          Constants
!*
!***********************************************************************
!
constantinteger  no = 0, yes = 1
constantinteger  channel = 1
constantinteger  ascii = 0, binary = 1, default = 2
constantinteger  fixed = 1, variable = 2
constantinteger  sscharfiletype = 3
constantinteger  ssdatafiletype = 4
constantinteger  bufsize = 4096;        ! Size of tape buffer
constantinteger  maxaction = 8
constantbyteintegerarray  actions(1:maxaction) = c 
'E','P','H','L','V','A','B','T'
constantbyteintegerarray  monc(0:11) = c 
31,28,31,30,31,30,31,31,30,31,30,31
constantstring (1) snl = "
"
constantstring (3)array  mons(0:11) = c 
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
!
!
!***********************************************************************
!*
!*          Record and array formats
!*
!***********************************************************************
!
recordformat  hf(integer  dataend,datastart,filesize,filetype,
                 sum,datetime,format,records)
recordformat  rf(integer  conad,filetype,datastart,dataend)
!
!
!***********************************************************************
!*
!*         Subsystem references
!*
!***********************************************************************
!
systemroutinespec  changefilesize(string (31) file,
                                  integer  newsize,integername  flag)
systemroutinespec  connect(string (31) file,integer  mode,hole,
                           prot,record (rf)name  r,integername  flag)
externalstringfunctionspec  date
systemroutinespec  destroy(string (31) file,integername  flag)
systemroutinespec  disconnect(string (31) file,integername  flag)
systemstringfunctionspec  failuremessage(integer  mess)
systemstringfunctionspec  itos(integer  n)
systemroutinespec  modpdfile(integer  ep,string (31) pdfile,
                             string (11) member,string (31) infile,
                             integername  flag)
systemroutinespec  move(integer  length,from,to)
systemstringfunctionspec  nexttemp
systemroutinespec  outfile(string (31) file,integer  size,hole,
                           prot,integername  conad,flag)
externalintegerfunctionspec  outpos
systemintegerfunctionspec  parmap
externalroutinespec  prompt(string (255) s)
systemintegerfunctionspec  pstoi(string (63) s)
systemroutinespec  setpar(string (255) s)
externalroutinespec  set return code(integer  i)
systemstringfunctionspec  spar(integer  n)
externalstringfunctionspec  time
systemroutinespec  trim(string (31) file,integername  flag)
systemroutinespec  uctranslate(integer  ad,len)
!
externalroutinespec  define(string (255) s)
!
!
!***********************************************************************
!*
!*          Magnetic tape interface routines
!*
!***********************************************************************
!
externalroutinespec  askmag(integer  channel,string (7) vol,
                            integername  flag)
externalroutinespec  readmag(integer  channel,ad,integername  len,flag)
externalroutinespec  rewindmag(integer  channel)
externalroutinespec  skipmag(integer  channel,n)
externalroutinespec  skiptmmag(integer  channel,n)
externalroutinespec  unloadmag(integer  channel)
!
!
!***********************************************************************
!*
!*          Service routines
!*
!***********************************************************************
!
stringfunction  specmessage(integer  n)
switch  sw(1000:1000)
!
-> sw(n)
!
sw(1000):  result  = "Failed to claim tape"
end ;   ! of specmessage
!
!
routine  fail(integer  n)
string (255) s
!
selectoutput(0)
if  n < 1000 then  s = failuremessage(n) else  s = specmessage(n)
printstring(snl."RSXPIP fails -".s)
unloadmag(channel)
set return code(n)
stop 
end ;   ! of fail
!
!
routine  reset
rewindmag(channel)
skipmag(channel,1)
end ;   ! of reset
!
!
routine  skiptm(integer  n)
integer  i
!
skiptmmag(channel,1) for  i = 1,1,n
end ;   ! of skiptm
!
!
routine  fixname(string (31) file,stringname  dest)
string (31) name,ext,dname,dext
!
if  file -> file.(";") then  start ; finish 
unless  file -> name.(".").ext then  start 
   name = file
   ext = ""
finish 
unless  dest -> dname.(".").dext then  start 
   dname = dest
   dext = ""
finish 
if  dext = "*" then  dext = ext
if  dname = "*" then  dname = name
dest = dname
if  dext # "" then  dest <- dest.".".dext
end ;   ! of fixname
!
!
routine  readline(stringname  s)
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 
uctranslate(addr(s)+1,length(s))
end ;   ! of readline
!
!
integerfunction  match(stringname  s,integer  gen,stringname  mask)
integer  mgen
string (31) mname,mext,mgens,name,ext
!
unless  mask -> mname.(".").mext then  start 
   mname = mask
   mext = ""
finish 
if  mext -> mext.(";").mgens then  start 
   if  mgens = "*" then  mgen = -1 else  mgen = pstoi(mgens)
finish  else  mgen = -1
unless  s -> name.(".").ext then  start 
   name = s
   ext = ""
finish 
!
unless  mname = name or  mname = "*" then  result  = no
unless  mext = ext or  mext = "*" then  result  = no
unless  mgen = gen or  mgen = -1 then  result  = no
result  = yes
end ;   ! of match
!
!
integerfunction  checkswitch(integer  c)
integer  i
!
for  i = 1,1,maxaction cycle 
   if  c = actions(i) then  result  = i
repeat 
result  = -1
end ;   ! of checkswitch
!
!
routine  getcommand(integername  type,stringname  srce,dest)
integer  l
string (63) line
string (255) work1,work2
!
type = 0
readline(line)
line = work1.work2 while  line -> work1.(" ").work2 
l = length(line)
if  l >= 2 then  start 
   if  charno(line,l-1) = '/' then  start 
      type = checkswitch(charno(line,l))
      length(line) = l - 2
   finish 
finish 
if  line -> work1.("/").work2 then  start 
   type = -1
   return 
finish 
!
if  line -> work1.("=").work2 then  start 
   dest <- work1
   srce <- work2
finish  else  start 
   dest = ""
   srce <- line
finish 
end ;   ! of getcommand
!
!
integerfunction  wild(string (31) s)
if  s -> s.("*") then  result  = yes
result  = no
end ;   ! of wild
!
!
stringfunction  vdate(integer  n)
integer  year,month,m
string (2) y
string (9) s
!
year = n//1000
n = n - year*1000
month = 0
cycle 
   m = monc(month)
   if  month = 1 and  year//4*4 = year then  m = m + 1
   exit  if  n <= m
   month = month + 1
   n = n - m
repeat 
s = itos(n)
y = itos(year)
if  length(y) = 1 then  y = "0".y
if  length(s) = 1 then  s = "0".s
result  = s."-".mons(month)."-".y
end ;   ! of vdate
!
!
routine  printhelp
printstring("
Program call is:    RSXPIP(tape[,commands])

Command format is:  destination=source/switch
                    where one or two items may be null.
")
printstring("
File specs are:     name.ext;version
                    where ext and/or version may be omitted. Any
                    component may be replaced by an asterisk to form a
                    'wildcard' file specification.
")
printstring("
Switches are:       /E  -  exit from RSXPIP.
                    /P  -  set current pdfile.")
printstring("
                    /H  -  print this help text.
                    /L  -  give a directory of all or part of the tape.
                    /V  -  display version number of RSXPIP.")
printstring("
                    /A  -  without filespec, set ASCII mode for
                           subsequent transfers. With filespec, set
                           ASCII mode for current transfer only.")
printstring("
                    /B  -  without filespec, set binary mode for
                           subsequent transfers. With filespec, set
                           binary mode for current transfer only.")
printstring("
                    /T  -  without filespec, set mode as given in
                           tape file for subsequent transfers. With
                           filespec, use mode in file for current
                           transfer only.

")
end ;   ! of printhelp
!
!
routine  directory(stringname  mask)
integer  ad,len,flag,i,gen,adw,blocks,files,wildcard
string (255) work
byteintegerarray  buf(0:bufsize-1)
!
ad = addr(buf(0))
adw = addr(work) + 1
!
rewindmag(channel)
len = bufsize
readmag(channel,ad,len,flag)
move(6,ad+4,adw)
length(work) = 6
while  charno(work,length(work)) = ' ' cycle 
   length(work) = length(work) - 1
repeat 
printstring(snl.snl."Directory MT:[".work."]".snl)
printstring(date." ".time.snl.snl)
!
wildcard = wild(mask)
files = 0
blocks = 0
cycle 
   len = bufsize
   readmag(channel,ad,len,flag)
   if  flag = 1 then  exit 
   if  flag > 1 then  start 
      printstring("?Tape read error".snl)
      reset
      return 
   finish 
   move(4,ad,addr(i))
   if  i # m'HDR1' then  start 
      printstring("?HDR1 not found when expected".snl)
      reset
      return 
   finish 
   move(2,ad+39,adw)
   length(work) = 2
   gen = pstoi(work) + 1
   length(work) = 17
   move(17,ad+4,adw)
   for  i = 17,-1,1 cycle 
      if  charno(work,i) # ' ' then  exit 
      length(work) = length(work) - 1
   repeat 
   if  match(work,gen,mask) = no then  start 
      skiptm(3)
      continue 
   finish 
   printstring(work)
   printstring(";".itos(gen))
   spaces(20-outpos)
   skiptm(2)
   len = bufsize
   readmag(channel,ad,len,flag)
   move(4,ad,addr(i))
   if  i # m'EOF1' then  start 
      printstring("?EOF1 not found where expected".snl)
      reset
      return 
   finish 
   move(6,ad+54,adw)
   length(work) = 6
   i = pstoi(work);                     ! Number of blocks
   blocks = blocks + i
   printstring(itos(i).".")
   spaces(31-outpos)
   move(5,ad+42,adw)
   length(work) = 5
   printstring(vdate(pstoi(work)).snl)
   files = files + 1
   exit  if  wildcard = no
   skiptmmag(channel,1)
repeat 
!
printstring(snl."Total of ".itos(blocks)."./".itos(blocks).". blocks")
printstring(" in ".itos(files).". files".snl.snl)
end ;   ! of directory
!
!
routine  transfer(stringname  srce,dest,pdfile,integer  mode)
integer  i,c,pd,rewound,ad,len,flag,gen,records,ptr,conad,adt,iptr
integer  reclen,fmode,wildcard,recmax,rectype
string (31) out,temp,temp1,name,ext,destc
byteintegerarray  buf(0:bufsize-1)
record (rf) rr
record (hf)name  r
!
if  dest = "" then  start 
   if  pdfile = "" then  dest = "*.*" else  dest = "*"
finish 
for  i = 1,1,length(dest) cycle 
   c = charno(dest,i)
   if  'a' <= c <= 'z' then  c = c - 'a' + 'A'
   charno(dest,i) = c
repeat 
for  i = 1,1,length(srce) cycle 
   c = charno(srce,i)
   if  c = '#' then  c = '.'
   if  'a' <= c <= 'z' then  c = c - 'a' + 'A'
   charno(srce,i) = c
repeat 
!
ad = addr(buf(0))
adt = addr(temp) + 1
if  wild(srce) = yes then  start 
   wildcard = yes
   reset
   rewound = yes
finish  else  start 
   wildcard = no
   rewound = no
finish 
!
cycle 
   len = bufsize
   readmag(channel,ad,len,flag)
   if  flag > 1 then  start 
      printstring("?Tape read error".snl)
      reset
      return 
   finish 
   if  flag = 1 then  start 
      reset
      if  rewound = no then  start 
         rewound = yes
         continue 
      finish 
      if  wildcard = no then  printstring("?File not found".snl)
      return 
   finish 
   move(4,ad,addr(i))
   if  i # m'HDR1' then  start 
      printstring("?HDR1 not found when expected".snl)
      reset
      return 
   finish 
   move(2,ad+39,adt)
   length(temp) = 2
   gen = pstoi(temp) + 1
   length(temp) = 17
   move(17,ad+4,adt)
   for  i = 17,-1,1 cycle 
      if  charno(temp,i) # ' ' then  exit 
      length(temp) = length(temp) - 1
   repeat 
   if  match(temp,gen,srce) = no then  start 
      skiptm(3)
      continue 
   finish 
   !
   fmode = mode
   recmax = 16388
   rectype = variable
   len = bufsize
   readmag(channel,ad,len,flag)
   if  flag > 1 then  start 
      printstring("?Tape read error".snl)
      reset
      return 
   finish 
   if  flag # 1 then  start 
      move(4,ad,addr(i))
      if  i # m'HDR2' then  start 
         printstring("?HDR2 not found when expected".snl)
         reset
         return 
      finish 
      if  fmode = default then  start 
         if  buf(4) = 'D' and  buf(36) = ' ' then  start 
            fmode = ascii
         finish  else  fmode = binary
      finish 
      if  fmode = binary then  start 
         move(5,ad+10,addr(temp1)+1)
         length(temp1) = 5
         recmax = pstoi(temp1)
         if  buf(4) = 'F' then  rectype = fixed
         if  rectype = variable then  recmax = recmax - 4
      finish 
      skiptmmag(channel,1)
   finish  else  start 
      if  fmode = default then  fmode = binary
   finish 
   destc <- dest
   fixname(temp,destc)
   if  wildcard = yes then  start 
      printstring("[".destc."]".snl)
   finish 
   if  length(destc) > 11 then  length(destc) = 11
   unless  destc -> name.(".").ext then  start 
      name = destc
      ext = ""
   finish 
   if  ext # "" then  name <- name."#".ext
   out = name
   if  pdfile # "" then  start 
      out = "T#".nexttemp
      pd = yes
   finish  else  pd = no
   outfile(out,4096,0,0,conad,flag)
   if  flag # 0 then  start 
      printstring("%".failuremessage(flag))
      skiptm(2)
      continue 
   finish 
   r == record(conad)
   if  fmode = ascii then  start 
      r_filetype = sscharfiletype
   finish  else  start 
      r_filetype = ssdatafiletype
      r_format = (recmax << 16) ! rectype
   finish 
   ptr = r_dataend
   records = 0
   !
   cycle 
      len = bufsize
      readmag(channel,ad,len,flag)
      if  flag > 1 then  start 
         printstring("?Tape read error".snl)
         destroy(out,flag)
         reset
         return 
      finish 
      if  flag = 1 then  exit ;         ! End of file
      iptr = 0
      cycle 
         exit  if  iptr = len;          ! Last record was exact fit
         i = buf(iptr)
         if  i = '^' then  exit ;       ! No more records in block
         if  rectype = variable then  start 
            move(4,ad+iptr,adt)
            length(temp) = 4
            reclen = pstoi(temp)
            if  iptr + reclen > len then  start 
               printstring("?Spanned records not supported".snl)
               skiptmmag(channel,1)
               exit 
            finish 
            iptr = iptr + 4
            reclen = reclen - 4
            if  reclen < 0 then  exit 
            if  fmode = binary then  start 
               reclen = reclen + 2
            finish  else  reclen = reclen + 1
         finish  else  reclen = recmax
         if  ptr + reclen >= r_filesize then  start 
            i = (r_filesize+131072) & (¬131071)
            changefilesize(out,i,flag)
            if  flag = 261 then  start ;! VM hole too small
               disconnect(out,flag)
               changefilesize(out,i,flag)
               if  flag = 0 then  connect(out,3,0,0,rr,flag)
               if  flag = 0 then  start 
                  conad = rr_conad
                  r == record(conad);   ! Re-map - it may have moved
               finish 
            finish 
            if  flag # 0 then  start 
               printstring("?".failuremessage(flag))
               skiptm(2)
               return 
            finish 
            r_filesize = i
         finish 
         if  fmode = ascii then  start 
            move(reclen-1,ad+iptr,conad+ptr)
            byteinteger(conad+ptr+reclen-1) = nl
            iptr = iptr + reclen - 1
         finish  else  start 
            if  rectype = variable then  start 
               halfinteger(conad+ptr) = reclen
               move(reclen-2,ad+iptr,conad+ptr+2)
               iptr = iptr + reclen - 2
            finish  else  start 
               move(reclen,ad+iptr,conad+ptr)
               iptr = iptr + reclen
            finish 
            records = records + 1
         finish 
         ptr = ptr + reclen
      repeat 
   repeat 
   !
   r_records = records
   r_dataend = ptr
   trim(out,flag)
   skiptmmag(channel,1)
   !
   if  pd = yes then  start 
      connect(pdfile,0,0,0,rr,flag)
      if  flag = 218 then  start ;      ! Create pdfile
         modpdfile(4,pdfile,"","",flag)
         if  flag # 0 then  start 
            printstring("?".failuremessage(flag))
            return 
         finish 
      finish 
      modpdfile(2,pdfile,name,"",flag); ! Delete any existing member of same name
      modpdfile(1,pdfile,name,out,flag);! Insert new member
      if  flag # 0 then  start 
         printstring("?".failuremessage(flag))
         return 
      finish 
      destroy(out,flag)
   finish  else  start 
      disconnect(out,flag)
   finish 
   unless  wildcard = yes then  exit 
repeat 
end ;   ! of transfer
!
!
!***********************************************************************
!*
!*          R S X P I P
!*
!***********************************************************************
!
externalroutine  rsxpip(string (255) parms)
integer  flag,type,mode
string (6) vol
string (11) pdfile
string (31) input,srce,dest
switch  sw(0:maxaction)
!
set return code(9999);                  ! In case of catastrophic failure
setpar(parms)
if  parmap > 3 then  fail(263);         ! Wrong number of parameters
vol <- spar(1)
input <- spar(2)
if  vol = "" then  fail(263);           ! Wrong number of parameters
if  input = "" then  input = ".IN"
define("1,".input)
selectinput(1)
!
askmag(channel,vol,flag)
if  flag # 0 then  fail(1000);           ! Failed to claim tape
reset
!
pdfile = ""
mode = default
prompt("*")
cycle 
   getcommand(type,srce,dest)
   if  type < 0 or  (type = 0 and  srce = "") then  start 
      printstring("?Illegal command".snl)
      continue 
   finish 
   -> sw(type)
   !
sw(0):
   transfer(srce,dest,pdfile,mode)
   continue 
   !
sw(1):   ! /E
   exit 
   !
sw(2):   ! /P
   pdfile <- srce
   if  dest # "" then  start 
      printstring("%File ".dest." ignored".snl)
   finish 
   continue 
   !
sw(3):   ! /H
   unless  srce = "" = dest then  start 
      printstring("%Files ignored".snl)
   finish 
   printhelp
   continue 
   !
sw(4):   ! /L
   if  dest # "" then  start 
      printstring("%File ".dest." ignored".snl)
   finish 
   if  srce = "" then  srce = "*.*;*"
   directory(srce)
   reset
   continue 
   !
sw(5):   ! /V
   unless  srce = "" = dest then  start 
      printstring("%Files ignored".snl)
   finish 
   printstring("Version: E".itos(version).".".itos(edit).snl)
   continue 
   !
sw(6):   ! /A
   if  srce # "" then  start 
      transfer(srce,dest,pdfile,ascii)
      continue 
   finish 
   unless  dest = "" then  start 
      printstring("%File ".dest." ignored".snl)
   finish 
   mode = ascii
   continue 
   !
sw(7):   ! /B
   if  srce # "" then  start 
      transfer(srce,dest,pdfile,binary)
      continue 
   finish 
   unless  dest = "" then  start 
      printstring("%File ".dest." ignored".snl)
   finish 
   mode = binary
   continue 
   !
sw(8):   ! /T
   if  srce # "" then  start 
      transfer(srce,dest,pdfile,default)
      continue 
   finish 
   unless  dest = "" then  start 
      printstring("%File ".dest." ignored".snl)
   finish 
   mode = default
   continue 
repeat 
!
unloadmag(channel)
set return code(0)
end ;   ! of rsxpip
endoffile