! 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
