{+} { Program BRUREAD : read BRU files } { } { Written by Adrian Weiler 1986/87 } { Non-commercial use is OK. } { Feel free to give this program to } { anybody that can use it, as long as } { you don't do that for profit. Please } { don't remove this heading. } { } { Sorry for the bad docs & for the lots } { of hacks in this code. I didn't write } { this program for profit either... } { } { BRUREAD consists of: } { BRUREAD.PAS ( this file ) } { BRU.CLD ( Set Command BRU ) } { BRUMSG.MSG ( Message/OBJ ) } { BRU.TXT ( short doc ) } { } { Link: Link BRUREAD,BRUMSG } { } { If anybody makes improvements, please } { let me know. My address (snail mail): } { Adrian Weiler } { Hennentalweg 12 } { 7400 Tuebingen } { W-Germany } { Phone (49)(7071) 45054 } { Note: Decimal 45054 = Hex AFFE = Monkey } { BITNET: MIWE001@DTUZDV5A (until Mar'89) } { After Apr'89, I probably won't have } { that account anymore, so you could } { contact a friend: ZRKH001 (ZR?HK?) } { or CFKS001 @ the same node. } {-----------------------------------------------} [inherit ('SYS$LIBRARY:STARLET')] program bruread (output,brudat,tfile,listfile); Type V5000 = Varying [5000] of char; ufile = [unsafe] file of char; Fname = packed array [1..256] of char; String = varying [80] of char; FabPointer = ^Fab$type; RabPointer = ^Rab$type; XabPointer = ^Xab$type; NamPointer = ^Nam$type; TPointer = ^FName; byte = [byte] 0..255; word = [word] 0..65535; item = packed record siz, typ : word; adr : integer; end; { Map RSX-11M File Header } { ----------------------- } RsxHeader = packed record idof, mpof : byte; fnum, fseq : word; flev, fstr : byte; uicmember, uicgroup : byte; prot : word; ucha, scha : byte; { Settable by ATR$C_RECATTR (7 Words) } rtyp, ratt : byte; rsiz : word; hibh, hibk : word; efbh, efbk : word; ffby : word; ufat : packed array [1..9] of word; rest : packed array [1..466] of byte; end; hda = packed array [1..512] of char; { Type cast for RsxHeader } { Internal representation of File Header } { -------------------------------------- } FileHeaderPointer = ^FileHeader; FileHeader = [unsafe] record bt : integer; { Total Blocks } name : packed array [1..5] of word; { File name in Rad50 } directory : packed array [1..6] of char; attributes : record { User settable attributes } artyp, aratt : byte; arsiz : word; ahibh, ahibk : word; aefbh, aefbk : word; affby : word; end; dates : record { User settable dates } arvno : word; { revision number } arday : packed array [1..2] of char; { Revision date } armon : packed array [1..3] of char; aryea : packed array [1..2] of char; arhou : packed array [1..2] of char; armin : packed array [1..2] of char; arsec : packed array [1..2] of char; acday : packed array [1..2] of char; { creation date } acmon : packed array [1..3] of char; acyea : packed array [1..2] of char; achou : packed array [1..2] of char; acmin : packed array [1..2] of char; acsec : packed array [1..2] of char; end; aesqn : byte; { extension sequence number } aefnu : word; { next extension file number } back : FileHeaderPointer; { Backpointer to file header whose extension the current one is } ause : byte; { number of retrieval pointers in use } artrv : packed array [1..102] of packed record asize : byte; albn : integer; end; end; c3 = varying [3] of char; SigArr = Array [0..9] of Integer; { Signal Array } MchArr = Array [0..4] of Integer; { Mechanism Array } Lptr = ^ListItem; ListItem = Record Link : Lptr; Name : Varying [30] Of Char; End; var BRUREAD$_CREATED : [external,value] Integer; BRUREAD$_FILEPURGED : [external,value] Integer; BRUREAD$_UPDATED : [external,value] Integer; BRUREAD$_WORKING : [external,value] Integer; BRUREAD$_TOTAL : [external,value] Integer; BRUREAD$_CREDIR : [external,value] Integer; BRUREAD$_FNF : [external,value] Integer; BRUREAD$_IVDEV : [external,value] Integer; BRUREAD$_NOTMOUNTED : [external,value] Integer; BRUREAD$_NOTFOREIGN : [external,value] Integer; HeaderPointer : Array [0..65535] Of FileHeaderPointer; listfile : text; tfile : [unsafe] text; fullname : string; TapeChannel, Channel : word; St1, St2, Context : integer := 0; Listspec, Resultspec, Filespec : Varying [80] of char; Fab : FabPointer; DevInfo : Dev$type; FileOpen, Tape, FlagCopy, FlagDebug, FlagExclude, FlagLog, FlagList, FlagSelect, FlagTotal, FlagRewind : Boolean := False; Select, Exclude : Lptr := Nil; openstat, stat: Integer; iosb: Packed Array [1..4] of word; atrlist : record att : packed array [1..2] of item; fin : integer end := zero; brudat : file of V5000; CurrentFileHeader : FileHeaderPointer := nil; buf : [unsafe,aligned(1)] v5000; backup_set : varying [12] of char; TotalFiles, TotalBlocks, curr_file, b, block_size, bufpos, pos, l : integer := 0; mode : (undefined,directory,header,data,end_of_file) := undefined; dirbuf : [unsafe] packed record fnum, fseq, fvol : word; fnam : packed array [1..3] of word; ftyp, fver : word; end; dirspec : varying [6] of char; fnambuf : [unsafe] packed record fnam : packed array [1..3] of word; ftyp, fver : word; rvno : word; rday : packed array [1..2] of char; rmon : packed array [1..3] of char; ryea : packed array [1..2] of char; rhou : packed array [1..2] of char; rmin : packed array [1..2] of char; rsec : packed array [1..2] of char; cday : packed array [1..2] of char; cmon : packed array [1..3] of char; cyea : packed array [1..2] of char; chou : packed array [1..2] of char; cmin : packed array [1..2] of char; csec : packed array [1..2] of char; eday : packed array [1..2] of char; emon : packed array [1..3] of char; eyea : packed array [1..2] of char; end; mapbuf : [unsafe] packed record esqn, ervn : byte; efnu, efsq : word; ctsz, lbsz, use, map : byte; rtrv : packed array [1..102] of packed record lbnh, Size : byte; lbnl : word; end; end; datbuf : [unsafe] packed array [1..8] of record fnum : Word; lbnh,Size : Byte; lbnl : word; end; Hdrbuf : RsxHeader; hdrflag : boolean := false; eofflag : boolean := false; rad50 : [readonly] packed array [1..40] of char := ' ABCDEFGHIJKLMNOPQRSTUVWXYZ$./0123456789'; line : varying[132] of char; [External(Lib$Signal)] Function $Signal ( %Immed Cond : Integer; %Immed Arguments : [List,Unsafe] Integer ) : Integer; Extern; [Asynchronous] Function Handler ( Var SigArgs : SigArr; Var MchArgs : MchArr ) : Integer; Begin If SigArgs[1] Div 65536 <> 0 { Not a System Signal } Then SigArgs[0] := SigArgs[0]-2; { Remove PC, PSL } if sigargs[1] <> ss$_unwind then $Putmsg ( SigArgs ); Case SigArgs[1] Mod 8 Of 0, { Warning } 1, { Success } 3 : { Information } Handler := SS$_Continue; 2 : { Error } Handler := SS$_Continue; Otherwise Begin $Unwind ( MchArgs[2]+1 ); { Fatal: Exit program } End; End; End; Function VDesc ( Var What : Varying[l] of Char ) : Integer; Var VD : [static] Item; { ** Note: cannot be used twice in a single $signal call } Begin With VD Do Begin Siz := What.Length; Typ := 0; Adr := IAddress (What.body); End; VDesc := IAddress (VD); End; procedure collapse ( a : varying [l1] of char; var b : varying [l2] of char ); var i : integer; begin b := ''; for i := 1 to l1 do if a[i] <> ' ' then b := b+a[i]; b := pad (b,' ',length(a)); end; function c5ta ( p : word ) : c3; var a : c3; i : integer; begin a := ''; for i := 1 to 3 do begin a := rad50 [p mod 40 + 1] + a; p := p div 40; end; c5ta := a; end; Function FindHeader (num:word;FindBase:Boolean := False) : FileHeaderPointer; Var Hd : FileHeaderPointer; begin Hd := HeaderPointer[Num]; If Hd = Nil Then Begin { Not found } If FindBase Then Begin { Called by open_file } Writeln ('*** Fatal, File ID ',oct(num,6,6),' not found'); End Else Begin Hd^.directory := 'EXTEND'; { Just in case of error } New (Hd); Hd^ := Zero; HeaderPointer[Num] := Hd; End; End; { If File ID refers to an extension file header, search base header } { ----------------------------------------------------------------- } If FindBase Then While Hd^.back <> nil do Hd := Hd^.Back; FindHeader := Hd; end; procedure total; var i : integer; d,c,s1, s2 : string; begin c := ''; if FlagCopy then c := 'created '; writev (s1,TotalFiles); collapse (s1,s1); for i := s1.length downto 1 do if s1[i] = ' ' then s1.length := i - 1; writev (s2,TotalBlocks); collapse (s2,s2); for i := s2.length downto 1 do if s2[i] = ' ' then s2.length := i - 1; d := s1+' files '+c+'('+s2+' blocks)'; if FlagList then begin writeln (ListFile); writeln (ListFile,'Total of ',d); end else begin writeln; $Signal (BRUREAD$_TOTAL,3,%Descr d); end; end; procedure cleanup; { Forget all we have done... } begin end; [external(Lib$Create_Dir)] Function $Create_Dir ( %DESCR dirspec : string ) : integer; extern; [external(CLI$GET_VALUE)] function $GetValue ( entity_desc : [CLASS_S] packed array [l..u:integer] of char; VAR retdesc : [CLASS_S] packed array [l1..u1:integer] of char; Var Retlength : word ) : Integer; extern; [external(CLI$PRESENT)] function $Present ( entity_desc : [CLASS_S] packed array [l..u:integer] of char ) : Boolean; extern; [external(LIB$FIND_FILE)] function $FindFile ( Filespec : [CLASS_S] packed array [l1..u1:integer] of char; %descr Resultspec : varying [l2] of char; Var Context : integer; DefaultSpec : [CLASS_S] packed array [l3..u3:integer] of char := %immed 0; RelatedSpec : [CLASS_S] packed array [l4..u4:integer] of char := %immed 0; Var StatusValue : integer := %immed 0; UserFlags : integer := %immed 0 ) : Integer; Extern; [external(STR$MATCH_WILD)] function $MatchWild ( %Descr CandidateString : varying [l1] of char; %Descr PatternString : varying [l2] of char ) : Integer; Extern; procedure close_file; begin If FileOpen Then Begin With CurrentFileHeader^ Do If FlagLog Then Begin if (openstat = rms$_created) then begin $Signal (BRUREAD$_CREATED,4,Vdesc(fullname),bt); end else if (openstat = rms$_filepurged) then begin $Signal (BRUREAD$_FILEPURGED,4,VDesc(fullname),bt); end else begin $Signal (BRUREAD$_UPDATED,4,Vdesc(fullname),bt); end; End; close (tfile); { Dummy, damit pascal OTS zufrieden ist } FileOpen := False; Stat := $qiow ( chan := Channel, func := IO$_DEACCESS, iosb := iosb, p5 := IADDRESS (Atrlist) ); if not (odd(stat) and odd(iosb[1])) then writeln ('Deaccess:',hex(stat),hex(iosb[1])); $dassgn (Channel); end{If File was open}; curr_file := 0; end; procedure add_to_file ( fnum, b, lbn : integer ); var filename : string; procedure open_file; var allocation : integer; function user_open ( var fab : fab$type; var rab : rab$type; var f : text ) : integer; var status : integer; nam : NamPointer; chan : [unsafe] packed array [1..2] of word; dir : string; retried : integer; begin {user_open} retried := 0; repeat with fab do begin fab$v_bio := true; fab$v_ufo := true; fab$v_upi := true; fab$l_alq := Allocation; Nam := fab$L_NAM :: NamPointer; end; status := $create (fab); if not odd(status) then begin if status = rms$_dnf then begin {Directory not found} retried := retried + 1; { Allow one retry after dir created } Writev (Dir,Nam^.Nam$L_DEV :: TPOINTER^ : Nam^.Nam$B_DEV, Nam^.Nam$L_DIR :: TPOINTER^ : Nam^.Nam$B_DIR); if $create_dir (Dir) = ss$_created then if FlagLog then $Signal (BRUREAD$_CREDIR, 3, Vdesc(dir)); end else retried := 2; { Other error - no retry } end; until odd(status) or (retried = 2); if odd (status) then $connect (rab); user_open := status; openstat := status; if odd (status) then Writev (fullname,Nam^.Nam$L_RSA :: TPOINTER^:Nam^.Nam$B_RSL) else fullname := ''; Chan := Fab.Fab$L_STV; Channel := chan[1]; end; Function InList ( List : Lptr; Empty : Boolean ) : Boolean; Var Found : Boolean; Candidate, Pattern : Varying [35] Of Char; Begin If List = Nil Then InList := Empty Else Begin Found := False; Candidate := '['+CurrentFileHeader^.directory+']'+FileName; Repeat Pattern := List^.Name; If Index (Pattern,'[') = 0 Then Pattern := '[*]'+Pattern; If Index (Pattern,';') = 0 Then Pattern := Pattern+';*'; Found := odd ( $MatchWild (Candidate,Pattern) ); If Found And FlagDebug Then Writeln ( 'Matched ',Candidate,' with ',List^.Name ); List := List^.Link; Until Found Or (List=Nil); InList := Found; End; End; begin {open_file} if curr_file <> 0 then close_file; curr_file := fnum; CurrentFileHeader := FindHeader (curr_file,true); with CurrentFileHeader^ do begin writev (filename,c5ta(name[1]),c5ta(name[2]),c5ta(name[3]),'.', c5ta(name[4]),';',name[5]:5); collapse (filename,filename); with attributes do allocation := ahibh * 65536 + ahibk; with attributes do if arsiz = 0 then begin Writeln ('*** Illegal recordsize 0 encountered. Set to 512.',chr(7)); arsiz := 512; end; While Filename[Filename.Length] = ' ' Do Filename.Length := Filename.Length - 1; if FlagCopy then Begin If InList(Select,True) And Not InList(Exclude,False) Then begin FileOpen := True; TotalFiles := TotalFiles + 1; TotalBlocks := TotalBlocks + Allocation; open ( tfile, filename,unknown, default := '[.'+backup_set+'.'+directory+']', user_action := user_open ); with atrlist.att[1] do begin siz := 28; typ := atr$c_ascdates; adr := iaddress (DATES); end; with atrlist.att[2] do begin siz := 14; typ := atr$c_recattr; adr := iaddress (attributes); end; End; end{if copy} Else Begin{Listing} TotalFiles := TotalFiles + 1; TotalBlocks := TotalBlocks + Allocation; End; end; end; { * The BRU data blocks describe logical blocks. * Since we deal with files, we must remap the logical blocks * to virtual blocks of the current file. (Fortunately BRU has * the kindness to tell us to which file the block belongs } Function Vbn : Integer; { Lbn To Vbn conversion } Var I, Vb : Integer; Hd : FileHeaderPointer; Begin Hd := CurrentFileHeader; I := 1; { start mapping at 1st mapping pointer } Vb := 1; { it maps vbn 1 } While ( lbn < Hd^.Artrv[i].albn ) Or ( lbn > Hd^.Artrv[i].albn+Hd^.Artrv[i].asize ) Do Begin Vb := Vb + Hd^.Artrv[i].asize + 1;{ calculate the vbn mapped by next ptr } i := i + 1; { advance index to mapping pointer } If i > Hd^.Ause Then Begin { if all mapping pointers done...} Hd := HeaderPointer[Hd^.aefnu]; { step to extension file header } i := 1; { and restart mapping } End; End; Vbn := Vb + ( lbn - Hd^.Artrv[i].albn ); End; begin if curr_file <> fnum then open_file; if FileOpen then begin Stat := $qiow ( chan := Channel, func := IO$_WRITEVBLK, iosb := iosb, p1 := %immed iaddress (buf.body) + bufpos, p2 := b*512, p3 := vbn ); if not (odd(stat) and odd(iosb[1])) then writeln ('File write error: ',hex(stat),hex(iosb[1]),hex(iosb[2]), ' vbn=',vbn); end; with CurrentFileHeader^ do bt := bt+b; end; Procedure Process; Var ExtensionHeader : FileHeaderPointer; function check_tape ( var fab : fab$type; var rab : rab$type; var f : text ) : integer; var status : integer; chan : [unsafe] packed array [1..2] of word; begin {user_open} with fab do begin fab$v_nfs := tape; fab$v_ufo := tape; fab$v_nil := fab$v_ufo; end; status := $open (fab); if odd (status) then $connect (rab); check_tape := status; Chan := Fab.Fab$L_STV; TapeChannel := chan[1]; If tape and FlagRewind then $qiow ( chan := TapeChannel, func := IO$_REWIND ); end; procedure ReadTape; begin if Tape then begin Stat := $qiow ( chan := TapeChannel, func := IO$_READVBLK, iosb := iosb, p1 := %immed iaddress (buf.body), p2 := 5000, p3 := 0 ); if not (odd(stat) and odd(iosb[1])) then if iosb[1] <> SS$_ENDOFFILE then writeln ('Tape read error: ',hex(stat),hex(iosb[1]),hex(iosb[2])); buf.length := iosb[2]; end else begin read (brudat,buf); eofflag := eofflag or eof (brudat); end; end; begin { process } eofflag := false; open (brudat, ResultSpec, old, user_action := check_tape ); if not tape then reset (brudat); repeat ReadTape until buf.length = 0; ReadTape; readv (substr (buf,1,12),backup_set); if FlagList then Writeln (listfile,'Directory of Backup Set ',Backup_set) else writeln ('Backup Set Name: ',backup_set); ReadTape; { Boot block } ReadTape; { Home block } while not eofflag do begin ReadTape; l := buf.length; if l = 80 then begin if curr_file <> 0 then close_file; mode::byte := (index ('UFDHEADATEOF',substr(buf,1,3)) + 2) div 3; case mode of header : begin hdrflag := true; if FlagDebug then writeln ('Starting File Headers Section.'); end; directory : begin dirbuf := substr (buf,5,16); with dirbuf do dirspec := c5ta(fnam[1])+c5ta(fnam[2]); if hdrflag then begin mode := header; { Subsequent records are headers } with dirbuf do if FlagList then begin writeln (listfile); writeln (listfile,'[',c5ta(fnam[1]),',',c5ta(fnam[2]),']'); writeln (listfile); end; end; end; data : if FlagDebug then writeln ('Starting File Data Section.'); undefined : writeln ('*** Undefined mode ***'); end_of_file : begin if FlagTotal then total; eofflag := true; end; end{case}; end else if l <> 0 then begin case mode of undefined : writeln ('*** Undefined mode ***'); directory : begin {Directory entry} pos := 1; repeat dirbuf := substr (buf,pos,16); with dirbuf do if fnum = 0 then pos := l + 1 else begin pos := pos+16; New (CurrentFileHeader); { Create new entry } CurrentFileHeader^ := Zero; HeaderPointer[fnum] := CurrentFileHeader; with CurrentFileHeader^ do begin name[1] := fnam[1]; name[2] := fnam[2]; name[3] := fnam[3]; name[4] := ftyp; name[5] := fver; directory := dirspec.body; end; end; until pos >= l; end; header : begin { File header } pos := 1; repeat hdrbuf :: hda := substr (buf,pos,512); b := hdrbuf.idof*2+1; { Identification area } fnambuf := substr (hdrbuf::hda,b,45); b := hdrbuf.mpof*2+1; { Map area } mapbuf := substr (hdrbuf::hda,b,512+1-b); with hdrbuf, fnambuf, mapbuf do begin CurrentFileHeader := FindHeader (fnum); with CurrentFileHeader^ do begin with attributes do begin artyp := rtyp; aratt := ratt; arsiz := rsiz; ahibh := hibh; ahibk := hibk; aefbh := efbh; aefbk := efbk; affby := ffby; end; with dates do begin arvno := rvno; arday := rday; armon := rmon; aryea := ryea; arhou := rhou; armin := rmin; arsec := rsec; acday := cday; acmon := cmon; acyea := cyea; achou := chou; acmin := cmin; acsec := csec; end; aesqn := esqn; { Ext. sequence number } aefnu := efnu; { Ext. file number } if efnu <> 0 Then Begin ExtensionHeader := FindHeader (efnu); ExtensionHeader^.Back := CurrentFileHeader; End; ause := use div 2; { Number of pointers in use } For b := 1 To Ause Do With Artrv[b],rtrv[b] Do Begin asize := Size; albn := lbnh*65536+lbnl; End; End; writev (line,c5ta(fnam[1]),c5ta(fnam[2]),c5ta(fnam[3]),'.', c5ta(ftyp),';',fver:5); collapse (line,line); If FlagList then write (listfile,line,' '); block_size := hibh*65536+hibk; writev (line,block_size:7,'. '); collapse (line,line); if FlagList then writeln (listfile,line, cday,'-',cmon,'-',cyea,' ',chou,':',cmin,':',csec); end; pos := pos+512; until pos >= l; end; data : begin datbuf := substr (buf,1,48); pos := 0; bufpos := 48; repeat pos := pos + 1; with datbuf[pos] do if fnum = 0 then pos := 8 else begin add_to_file (fnum, size+1, lbnh*65536+lbnl); bufpos := bufpos + (size+1)* 512; end; until pos = 8; end; end{case}; end; end; repeat ReadTape; if FlagDebug then writeln (buf:10,buf.length) until buf.length = 0; close (Brudat); If tape then $dassgn (TapeChannel); Cleanup; { Remove all 'CurrentFileHeader' entries } End{Process}; Function GetList ( What : Packed Array [l..u:integer] Of Char; Var List : Lptr ) : Boolean; Var FileSpec : Varying [30] Of Char; Next : LPtr; Begin GetList := False; If $Present ( What ) Then Begin GetList := True; List := Nil; While Odd ( $GetValue (What,FileSpec.body, FileSpec.length) ) Do Begin If List = Nil Then Begin New (List); Next := List; End Else Begin New (Next^.Link); Next := Next^.Link; End; With Next^ Do Begin Link := Nil; Name := FileSpec; End; End; End; End; Begin {Main} Establish ( Handler ); FlagExclude := GetList ('EXCLUDE',Exclude); FlagCopy := $Present ('COPY'); FlagDebug := $Present ('DEBUG'); FlagLog := $Present ('LOG'); FlagList := $Present ('LIST'); FlagRewind := $Present ('REWIND'); FlagSelect := GetList ('SELECT',Select); FlagTotal := $Present ('TOTAL'); If FlagList then FlagCopy := False; { Remove default } If FlagList then begin $GetValue ('LIST',Listspec.body, Listspec.length); open (listfile,listspec,new,default := '.LIS'); rewrite (listfile); end; St1 := 1; While odd (st1) do begin St1 := $GetValue ('TAPE',Filespec.body, Filespec.length); if odd (St1) then begin St2 := 1; while odd (st2) do begin st2 := $FindFile (Filespec, Resultspec, Context, '.DOS', UserFlags := 2); if odd (st2) then begin fab := Context :: FabPointer; DevInfo := fab^.fab$L_DEV :: Dev$Type; with devinfo do begin if not DEV$V_FOD then $Signal (BRUREAD$_IVDEV) else if not DEV$V_MNT then $Signal (BRUREAD$_NOTMOUNTED) else if dev$v_sqd and not dev$v_for then $Signal (BRUREAD$_NOTFOREIGN) else begin Tape := dev$v_sqd; $Signal (BRUREAD$_WORKING,1,VDesc(Resultspec)); Process; end; end; end; end{While more files}; if st2 <> RMS$_NMF then $Signal (BRUREAD$_FNF, 1,VDesc(ResultSpec),St2 ); end; end; end.