! COPY with automatic filestore switching
! RWT January 1991

option  "-low-nodiag-nocheck"
include  "inc:fs.imp"
include  "inc:util.imp"

begin 
string (255)ins,in,out

predicate  done remotely
! Attempt to get the filestore to perform the copying.
! If the input contains a comma (indicating more than one file), or
! if either of the file names begin with ':' (indicating a device name),
! this will fail (result FALSE).  Otherwise (result TRUE) the filestore
! will (start to) do the copying (completing it asynchronously).
integer  i
  on  3 start ; false ; finish 
  copy(ins,out); true 
end 

! Parse the parameter, which consists of one or more comma-separated
! input file names, plus an output file name, separated from the input(s)
! by '/' or space.

  ins = cliparam
  cycle 
    stopif  ins=""
    if  charno(ins,1)=' ' start 
      ins = substring(ins,2,length(ins))
    elseif  charno(ins,length(ins))=' '
      ins = substring(ins,1,length(ins)-1)
    elseif  ins -> in.(", ").ins or  ins -> in.(" ,").ins
      ins = in.",".ins
    elseif  ins -> in.("  ").ins
      ins = in." ".in
    finishelseexit 
  repeat 
  ins = in."/".ins if  ins -> in.(" ").ins
  out = "" unless  ins -> ins.("/").out

! Default input(s) or output is the terminal (":").

  out = ":" if  out=""
  ins = ":" if  ins=""
  stopif  done remotely

! End of easy bit.
! If we get here, it's because either there's a problem with the files,
! or device names or concatenation are involved, or explicit or implicit
! inter-filestore transfer is required.

! Ether comms

routine  ackwait(integer  port)
integer  bit=1<<port,marktime=cputime+1000,deadline = marktime+14000
  cycle 
    returnif  ack&bit#0
    printsymbol('.') and  marktime=marktime+1000 if  cputime>marktime
  repeatuntil  cputime>deadline
  printline("No ACK/NAK"); signal  9
end 

routine  dtxwait(integer  port)
integer  bit=1<<port,marktime=cputime+1000,deadline=marktime+6500
  cycle 
    returnif  dtx&bit#0
    printsymbol('.') and  marktime=marktime+1000 if  cputime>marktime
  repeatuntil  cputime>deadline
  printline("No response"); signal  9
end 

predicate  nacked(integer  port)
integer  bit=1<<port
  falseif  nak&bit=0; nak = nak&¬bit; true 
end 

predicate  gotack(integer  port)
  on  9 start ; false ; finish 
  ackwait(port); falseif  nacked(port); true 
end 

predicate  gotdtx(integer  port)
  on  9 start ; false ; finish 
  dtxwait(port); true 
end 

! Filestore comms

routine  disconnect
byte  disc = {4}12
  returnif  rdte=0; rdte = 0; rsap = 0
  etherwrite(lsap,disc,1); ackwait(lsap); returnif  nacked(lsap)
end 

routine  connect
string (255)s
integer  port
byte  con = 2
  on  9 start 
    printline(event_message); return 
  finish 
  rsap = 0; fsport = lsap; etheropen(lsap,rdte<<8)
  etherwrite(lsap,con,1); ackwait(lsap)
  if  nacked(lsap) start 
    printstring("No Ack from station "); phex2(rdte); newline; return 
  finish 
  dtxwait(lsap)
  length(s) = etherread(lsap,charno(s,1),80)
  port = charno(s,1)-'0'
  if  port<0 start 
    printline(substring(s,3,length(s))); return 
  finish 
  etheropen(lsap,rdte<<8+port)
! check connection
  while  dtx&1#0 cycle 
    length(s) = etherread(0,charno(s,1),255)
    printline("Spurious port 0 packet ignored")
  repeat 
  s = "M0".snl; etherwrite(lsap,charno(s,1),3)
  unless  gotack(lsap) start 
    printline("No M0 ack"); return 
  finish 
  unless  gotdtx(lsap) start 
    printline("No M0 response"); return 
  finish 
  length(s) = etherread(lsap,charno(s,1),255)
  s = "G0".snl; etherwrite(lsap,charno(s,1),3)
  unless  gotack(lsap) start 
    printline("No G0 ack"); return 
  finish 
  unless  gotdtx(lsap) start 
    printline("No G0 response"); return 
  finish 
  length(s) = etherread(lsap,charno(s,1),255)
  while  charno(s,1)='-' cycle 
    printline("Ignoring duplicate response")
    unless  gotdtx(lsap) start 
      printline("No duplicate response"); return 
    finish 
    length(s) = etherread(lsap,charno(s,1),255)
  repeat 
  rsap = port
end 

begin 
recordformat  f(byte  ldte,lsap,rdte,rsap)
@#ldte record (f)cur
record (f)array  portinfo(1:31)
integer  i,inport=1,outport=1

routine  select(integer  port)
owninteger  uno=0
  uno = userno if  userno#0
  userno = 0
  userno = uno if  port=1
  cur = portinfo(port); fsport = port
end 

integerfn  allocate port
integer  i
  for  i = 1,1,31 cycle 
    if  portinfo(i)_rdte=rdte start 
      select(i)
      result  = i
    finish 
  repeat 
  for  i = 2,1,31 cycle 
    if  portinfo(i)_rdte=0 start 
      lsap = i; fsport = i
      connect; select(1) andresult  = 0 if  rsap=0
      portinfo(i) = cur
      result  = i
    finish 
  repeat 
  printline("No free local ports"); select(1); result  = 0
end 

integerfn  access(routine  open(integer  stream,string (255)filename),
                  string (255)filename)
! Result 0 after error report if failed, otherwise result is port number.
! Parse the filename to determine which filestore is meant, selecting the
! appropriate port, connecting if necessary.  Then open the file.  If this
! fails with "owner not found" (i.e. event_extra=12), and the filestore in
! question was B or C, then try instead on C or B.
ownbytearray  dtetable('A':'Z')=16_14,16_15,16_1B,0(*)
string (255)server,rawfilename
integer  dte,port,retried=0,k
  on  3 start 
    selectoutput(0)
    retried = 1 unless  event_extra=12
    if  retried=0 start 
      if  dte=dtetable('B') or  dte=dtetable('C') start 
        dte = dtetable('B')+dtetable('C')-dte
      finishelse  retried=1
    finish 
    if  retried#0 start 
      printline(event_message); result  = 0
    finish 
    retried = 1
  finish 
  select(1)
  if  retried=0 start   {first time parse filename}
    dte = rdte; rawfilename = filename
    if  filename -> server.("::").rawfilename start 
      retried = 1       {no retrying if server specified explicitly}
      k = charno(server,1)&95 unless  server=""
      if  length(server)=1 and  'A'<=k<='Z' start 
        dte = dtetable(k)
      else 
        dte = 0
        while  server#"" cycle 
          k = charno(server,1); server = substring(server,2,length(server))
          if  '0'<=k<='9' start 
            dte = dte<<4-'0'+k
          else 
            k = k&95; exitunless  'A'<=k<='F'
            dte = dte<<4-'A'+k+10
          finish 
        repeat 
      finish 
      if  dte=0 start 
no:     select(1); printstring("Unknown server ")
        printline(filename); result  = 0
      finish 
    finish 
  finish 
  rdte = dte; port = allocate port; ->no if  port=0
  select(port); open(1,rawfilename); selectoutput(0)
  result  = port
end 

routine  transfer
bytearray  buf(0:4095)
integer  n=0,i
  on  9 start 
    closeinput
    select(outport); selectoutput(1)
    printsymbol(buf(i)) for  i = 0,1,n-1
    selectoutput(0)
    return 
  finish 
  cycle 
    select(inport); selectinput(1)
    n = 0
    cycle 
      readsymbol(buf(n)); n = n+1
    repeatuntil  n=4096
    select(outport); selectoutput(1)
    printsymbol(buf(i)) for  i = 0,1,n-1
  repeat 
end 

! Main program for local copying begins here

  portinfo(1) = cur
  portinfo(i) = 0 for  i = 2,1,31

  outport = access(openoutput,out); stopif  outport=0
  ins = ins.","
  while  ins -> in.(",").ins cycle 
    inport = access(openinput,in); continueif  inport=0
    transfer
  repeat 
  selectoutput(1); closeoutput; selectoutput(0)
  for  i = 2,1,31 cycle 
    select(i); disconnect
  repeat 
  select(1)
  space{?just to see if it got here ok?}

end 
end