!verify %option "-low-nons-nocheck" %include "inc:util.imp" %include "inc:fs.imp" %constinteger cr=13 %begin %routine error line ! newline set terminal mode(0) %end ! Robustified filestore comms start here %integerfn transact(%bytename b,%integer size) ! Send buffer at B of length SIZE to filestore, ! and receive reply into the same buffer, ! returning its length as result. %integer timer,bit=1<timer %if ack&bit=0 %start ack = ack!bit error line printstring("ACK timeout"); newline %continue %finish %exitif nak&bit=0 nak = nak!!bit error line printstring("NAK"); newline %repeat timer = cputime+20000 %cycle %result = etherread(lsap,b,532) %if dtx&bit#0 %repeatuntil cputime>timer errorline printstring("DTX timeout"); newline %repeat %end %bytemap hdhex(%bytename b,%integername n) ! Read HDhex number at B into N. ! Result is B advanced past the terminator. n = 0 %while b>='0' %cycle n = n<<4-'0'+b; b == b[1] %repeat %result == b[1] %end %string(255)%fn ninfo(%string(255)filename) %bytearray bb(1:532) %string(*)%name s %bytename b %integer len x1: s == string(addr(bb(1))); b == length(s) s = "N0".filename.tostring(nl) b[2] = userno+'0' b = transact(b[1],b)-1 %signal 3,3,,s %if b[1]='-' b == hdhex(b[1],len) b[-1] = len s == string(addr(b[-1])) %result = s %end %routine openr(%string(255)file,%integername xno,size) %bytearray bb(1:532) %string(*)%name s %integer blocks,pad %bytename b s == string(addr(bb(1))) s = "S0".file.tostring(nl) b == bb(2) b[1] = userno+'0' b[-1] = transact(b,b[-1])-1 %signal 3,3,,s %if b='-' b == hdhex(b,xno) b == hdhex(b,blocks) b == hdhex(b,pad) size = blocks<<9-pad %end %routine close(%integer xno) %bytearray bb(1:532) %string(*)%name s %bytename b s == string(addr(bb(1))) s = "K0".tostring(nl) b == charno(s,1) b[1] = xno+'0' b[-1] = transact(b,b[-1])-1 %signal 3,3,,s %if b='-' %end %predicate checkblock(%integer xno,bytes,%integername pos,%bytename buf) %label no %bytearray bb(1:532) %integer amount,p %bytename b %routine addhex(%integer k) addhex(k>>4) %and k = k&15 %if k>47 b[p] = k+'0' p = p+1 %end %signal 3,4,,"misaligned" %unless pos&511=0 %cycle b == bb(1) b = 'R' b[1] = xno+'0' p = 2; addhex(pos>>9) b[p] = nl amount = transact(b,p+1) %if b='-' %start b = amount-2; %signal 3,4,b[1]-'0',string(addr(b)) %finish b == hdhex(b,amount) %trueif amount=0 *move.l b,a0 *move.l buf,a1 *moveq #-1,d0 *add.l amount,d0 loop: *cmpm.b (a0)+,(a1)+ *dbne d0,loop *bne no bytes = bytes-amount; pos = pos+amount; buf == buf[amount] %trueif bytes<=0 %or amount<512 %repeat no: %false %end %routine openw(%string(255)file,%integername xno) %bytearray bb(1:532) %string(*)%name s %integer blocks,pad %bytename b s == string(addr(bb(1))) s = "T0".file.tostring(nl) b == charno(s,1) b[1] = userno+'0' b[-1] = transact(b,b[-1])-1 %signal 3,3,,s %if b='-' b == hdhex(b,xno) %end %routine writeblock(%integer xno,bytes,%integername pos,%bytename buf) %bytearray bb(1:532) %integer amount,p %bytename b %routine addhex(%integer k) addhex(k>>4) %and k = k&15 %if k>47 b[p] = k+'0' p = p+1 %end %returnif bytes=0 %signal 3,4,,"not at block boundary" %unless pos&511=0 %signal 3,4,,"multiblock write" %if bytes>512 b == bb(1) b = 'W'; b[1] = xno+'0'; p = 2; addhex(pos>>9) b[p] = ','; p = p+1; addhex(bytes); b[p] = nl *move.l p,d0 *move.l b,a1 *move.l buf,a0 *lea 1(a1,d0),a1 *moveq #-1,d0 *add.l bytes,d0 loop: *move.b (a0)+,(a1)+ *dbra d0,loop amount = transact(b,p+1+bytes) pos = pos+bytes %end %recordformat output file fm(%integer buf,pos,bytes,xno) %record(output file fm)%name curout %routine selectout(%record(output file fm)%name r) curout == r %end %routine openout(%string(255)file) curout = 0 openw(file,curout_xno) curout_buf = heapget(512) %end %routine flushout %returnif curout_bytes=0 writeblock(curout_xno,curout_bytes,curout_pos,byte(curout_buf)) curout_bytes = 0 %end %routine closeout flushout close(curout_xno); curout_xno = 0 heapput(curout_buf) %end %routine putsym(%integer k) byte(curout_buf+curout_bytes) = k curout_bytes = curout_bytes+1 flushout %if curout_bytes=512 %end %routine putstring(%string(255)s) %integer i putsym(charno(s,i)) %for i = 1,1,length(s) %end %record(output file fm)err=0,log=0 ! Robustified filestore comms end here %string(31)%fn combine(%string(*)%name d,t) ! Combine date and time strings into a single "DD/MM/YY:hh.mm.ss". %string(31)dt dt = d.":".t dt = dt.".00" %if length(t)=5 %result = dt %end ! Tape handling starts here ! The tape format looks like this: ! ! First, a "standard" 80 byte tape label record, the first 4 bytes ! of which contain the characters 'VOL1', the next 6 bytes contain ! the label (up to 6 alphanumerics padded on the right with spaces), ! the remainder of the record contains spaces. ! ! Then, for each file on the tape, we have a header block containing ! information about the file. This header block is followed by an ! appropriate number of data blocks. ! ! A single tape mark is written in fornt of the header block of the ! first file in every directory, the purpose of which is merely to ! speed up searches for a particular file during RESTORE operations. %include "scsitape.inc" %constinteger - virtual block size = 2048, {user data in each VB} overhead = 12, {block numbers and checksum fields} physical block size = virtual block size + overhead, tape blocks = 150 %recordformat vtbf {virtual tape block} - (%byte b {reference byte for tape operations} %or- %integer tbn, {tape block number} %half fbn, {file block number (0 for headers)} length, {block data content (0 for headers)} ((%integer file size, %string(31)time taped, file time stamp, %string(255)file name, file attributes) %or- %bytearray data(1:virtual block size)), %integer checkword) %record(vtbf)%arrayname tape buffer(1:tapeblocks) %record(vtbf)%name virtual block %integer vbn=tapeblocks,tbn=-1, fbn %string(7)tapelabel %routine tape mount(%string(63)which) ! Request a new tape to be mounted, and ask the operator to ! type in the tape label. The label actually on the tape ! is checked and must be the same as typed in. But typing ! "*" as the label matches anything. %string(81)s,a {S for SHOULD BE, A for ACTUALLY IS} %integer k,i errorline printstring("Please mount ";which;" tape."); newline %cycle; %repeatuntil tape unit ready tape rewind and wait tape mode select(buffered + default density + slow + variable block size) k = tape read block(charno(a,1),81) length(a) = 4 printstring("Tape has curious label") %and newline %unless k=80 %and a="VOL1" length(a) = 80 tapelabel = "" %for i = 5,1,10 %cycle k = charno(a,i); tapelabel = tapelabel.tostring(k) %unless k=' ' %repeat printstring("Tape label is "; tapelabel); newline tape mode select(buffered + default density + slow + sizeof(tapebuffer(1))) ! set terminalmode(0) selectout(log); putsym(' '); putstring(tapelabel); putsym(nl) %end %routine read virtual block %owninteger ts=0,tm=0 %cycle %if vbnlength(info) %or charno(info,pos)=sep s = s.tostring(charno(info,pos)); pos = pos+1 %repeat %end %on 4 %start errorline printstring(event_message;" in info line"); newline printstring(info); newline selectout(err) putstring(event_message;" in info line"); putsym(nl) putstring(info); putsym(nl) nblocks = 0 %return %finish pos = 1 to(' ',name) to(' ',attributes) to(' ',date) to(' ',time) to('(',blocks) stamp = combine(date,time) nblocks = stoi(blocks) %end %string(255)%fn file name part(%string(255)s) ! Remove any directory prefix and version number suffix. %integer p %if s -> s.(":-") %start; %finish {strip off version number} p = length(s) p = p-1 %while p>0 %and charno(s,p)#':' %result = substring(s,p+1,length(s)) %end %routine check file(%string(255)name,date,%integer size) %string(255)aname,aatt,astamp %integer ablocks,xno=0,pos=0,amt,ok %on 3 %start errorline printstring("File ";name;" no longer accessible"); newline read virtual block %and size = size-virtualblocksize %while size>0 %return %finish extractfinfo(ninfo(name),aname,aatt,astamp,ablocks) ! %if date#astamp %start ! errorline ! printstring("File ";name;"'s stamp has changed to ";astamp); newline ! %finish openr(name,xno,ok) %unless ok=size %start errorline printstring("File ";name;"'s size has changed"); newline %finish ok = 1 %while size>0 %cycle read virtual block %if virtualblock_length=0 %start errorline printstring("Unexpected header block in file ";name); newline vbn = vbn-1; close(xno) %return %finish size = size-virtualblock_length %continueif ok=0 ok = 0 %unless checkblock(xno,virtualblock_length,pos,virtualblock_data(1)) %repeat %if ok=0 %start errorline printstring("File ";name;"'s contents have changed"); newline %finish close(xno) %end ! Main program starts here cliparam = "*" %if cliparam="" printstring("Looking for ";cliparam); newline scsireset tape mount("first") tape buffer == new(tape buffer) %cycle read virtual block %until virtualblock_fbn=0 %exitif matches(virtualblock_filename,cliparam) printstring("Ignoring ";virtual block_filename); newline %repeat %cycle %unless virtualblock_fbn=0 %and virtualblock_length=0 %start errorline; printstring("Unexpected data block") write(virtualblock_fbn,1; virtualblock_length,1); newline %continue %finish printstring(virtualblock_timetaped;" ";- virtualblock_filetimestamp;" "); phex(virtualblock_filesize) printstring(" ";virtualblock_filename) newline check file(virtualblock_filename,virtualblock_filetimestamp, virtualblock_filesize) read virtual block %repeat %end