[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
[0357]
[0358]
[0359]
[0360]
[0361]
[0362]
[0363]
[0364]
[0365]
[0366]
[0367]
[0368]
[0369]
[0370]
[0371]
[0372]
[0373]
[0374]
[0375]
[0376]
[0377]
[0378]
[0379]
[0380]
[0381]
[0382]
[0383]
[0384]
[0385]
[0386]
        .TITLE        FIX_BOOK - Fix .DECW$BOOK or .BKB file after download
        .IDENT        /X01.03/
;+
; FIX_BOOK.MAR - Try to fix mangled (by http: download) .DECW$BOOK or .BKB file 
;
; Description:
; 
; OpenVMS ODLs (Online Documentation Library CD files) are being presented
; at http://odl.sysworks.biz. When downloading .DECW$BOOK or .BKB files from 
; this site, they are unsuable, because the download removes the required 
; record format (sequential file with variable length records) through the 
; download as octet-stream and/or the copy operation with FTP or sftp.
;
; This program tries to reconstruct a valid .DECW$BOOK or .BKB bookreader file
;
; Initiated by note EISNER::VMS 3726
;
;
; Build with:
;
;        $ MACRO fix_book                ! for OpenVMS VAX
;        $ MACRO/MIGRATE fix_book        ! for OpenVMS Alpha, I64 or x86_64
;        $ LINK fix_book
;
; Run with:
;
;        $ DEFINE INFILE  filename.DECW$BOOK        (or filename.BKB)
;        $ DEFINE OUTFILE new_filename.DECW$BOOK
;        $ RUN fix_book
;
; Parameters:
;
; Input:        INFILE        - logical pointing to mangled .DECW$BOOK/.BKB
; Output:        OUTFILE        - logical pointing to reconstructed .DECW$BOOK/.BKB
;
; 25-MAR-2021        Volker Halle [ halle (at) encompasserve.org ]
;
; Modification history:
;
; X01.00  VH        Initial test release
;
; X01.01  VH        Do not specify RAT in OUTRAB (force Record Attributes = none)
; 26-MAR-2021        Check for RFM=VAR and output appropriate SET FILE/ATTR message
;                Refer to 'bytes on disk' in good_book message
;
; X01.02  VH        Fix 'end of file byte' discrepancy in LAST record
; 27-MAR-2021        Output no. of parts in book together with title
;
; X01.03  VH        Fix %MACRO-E-BRDESTRANG, Branch destination out of range errors
; 29-MAR-2021        to make FIX_BOOK also work with OpenVMS VAX macro assembler
;-

        .PSECT        DATA,WRT,NOEXE
INFAB:        $FAB        FNM = <INFILE:>,-        ; Input file name
                FAC = <BIO,GET>                ; Block I/O read operations
INRAB:        $RAB        FAB = INFAB,-                ; Pointer to FAB
                BKT = 0,-                ; Start with current block
                UBF = REC_BUFF,-        ; Record buffer
                USZ = REC_SIZE                ;   and size

OUTFAB:        $FAB        FNM = <OUTFILE:>,-        ; Output file name
                FOP = CBT,-                ; Try for contiguous file
                MRS = 0,-                ; Maximum record size
                FAC = <PUT>                ; Record I/O write operations
OUTRAB:        $RAB        FAB = OUTFAB,-                ; Pointer to FAB
                BKT = 0,-                ; Start with current block
                RBF = REC_BUFF                ; Output uses same buffer
                                        ;   as input
REC_SIZE = 62*1024                        ; Maximum record size
REC_BUFF:
                .BLKB        REC_SIZE        ; Record buffer
end_of_rec_buff: .ADDRESS        .        ; pointer to end of REC_BUFF

vbn:                .LONG        1                ; starting VBN for $READ

good_str:        .ASCID        /%FIX_BOOK-I-GOOD, looks like a good .DECW$BOOK (bytes on disk)/
var_str:        .ASCID        \%FIX_BOOK-W-NOT_VAR, use SET FILE/ATTRIB=(RFM=VAR,RAT=NONE) infile\
bad_str:        .ASCID        /%FIX_BOOK-E-BAD, does not look like a mangled .DECW$BOOK/
title_str:        .ASCID        /%FIX_BOOK-I-TITLE, book title: !AC (!UL parts)/
rec_str:        .ASCID        /%FIX_BOOK-I-RECORD, record: !UL starts with !XL/
bad_rec:        .ASCID        /%FIX_BOOK-E-BAD_REC, unexpected start of record/
read_str:        .ASCID        /%FIX_BOOK-I-READ, reading !UL bytes starting at VBN !UL/

FAOOUT:         .WORD   256
                .BYTE   DSC$K_DTYPE_T
                .BYTE   DSC$K_CLASS_S
                .ADDRESS .+4
                .BLKB   256
record:                .LONG        1                ; Record no. (starting at 1)
rec_ptr:        .BLKL        1                ; pointer to next record in REC_BUFF
n_parts:        .BLKL        1                ; no. of parts in book

                .PSECT        CODE,NOWRT,EXE

        .ENTRY        FIX_BOOK,^M<>                ; No registers to save

        $OPEN        FAB=INFAB
        BLBC        R0,EXIT1
        $CONNECT        RAB=INRAB
        BLBC        R0,EXIT2
        MOVL        INFAB+FAB$L_ALQ,-
                OUTFAB+FAB$L_ALQ
        $CREATE        FAB=OUTFAB
        BLBC        R0,EXIT3
        $CONNECT        RAB=OUTRAB
        BLBC        R0,EXIT4
        BRB        READ

EXIT1:        MOVL        INFAB+FAB$L_STS,R2
        MOVL        INFAB+FAB$L_STV,R3
        BRW        EXIT

EXIT2:        MOVL        INRAB+RAB$L_STS,R2
        MOVL        INFAB+RAB$L_STV,R3
        BRW        EXIT

EXIT3:        MOVL        OUTFAB+FAB$L_STS,R2
        MOVL        OUTFAB+FAB$L_STV,R3
        BRW        EXIT

EXIT4:        MOVL        OUTFAB+RAB$L_STS,R2
        MOVL        OUTFAB+RAB$L_STV,R3
        BRW        EXIT



READ:        
        MOVW        #256, faoout                        ; reset buffer length
        $FAO_S        CTRSTR=read_str,-
                OUTBUF=faoout,-
                OUTLEN=faoout,-
                P1=INRAB+RAB$W_USZ,-                ; no. of bytes to read
                P2=vbn                                ; starting VBN

        PUSHAQ  faoout
        CALLS   #1, G^LIB$PUT_OUTPUT

        $READ        RAB=INRAB
        BLBS        R0,check_book
        CMPL        R0,#RMS$_EOF
        BEQL        80$
        BRW        EXIT2

80$:        BRW        done                ; branch aid for VAX

good_book:                        ; looks like a valid .DECW$BOOK file
        PUSHAQ  good_str
        CALLS   #1,G^LIB$PUT_OUTPUT

        CMPB        INFAB+FAB$B_RFM, #FAB$C_VAR        ; RFM = variable length records ?
        BEQL        90$

        PUSHAQ  var_str
        CALLS   #1,G^LIB$PUT_OUTPUT
        
90$:
        BISL        #FAB$M_DLT,OUTFAB+FAB$L_FOP        ; delete OUTFILE on close
        
        BRW        done
        
bad_book:                        ; not an octet-stream mangled .DECW$BOOK
        PUSHAQ  bad_str
        CALLS   #1,G^LIB$PUT_OUTPUT
        BRW        done



check_book:
        CMPW        #^x03FE,rec_buff        ; correct 1st hdr record length ?
        BEQL        good_book                ; EQL -> yes -> exit
        CMPW        #1,rec_buff                ; octet-stream .DECW$BOOK ?
        BNEQ        bad_book                ; NEQ -> no

;
; Special handling of first and possibly second record in mangled .DECW$BOOK
;
        MOVL        rec_buff+^x46, n_parts                ; no. of parts in book

        MOVW        #256, faoout                        ; reset buffer length
        $FAO_S        CTRSTR=title_str,-
                OUTBUF=faoout,-
                OUTLEN=faoout,-
                P1=#rec_buff+^x7E,-                ; ptr to title
                P2=n_parts                        ; no. of parts in book

        PUSHAQ  faoout
        CALLS   #1, G^LIB$PUT_OUTPUT

        MOVW        #256, faoout                        ; reset buffer length
        $FAO_S        CTRSTR=rec_str,-
                OUTBUF=faoout,-
                OUTLEN=faoout,-
                P1=record,-                        ; record number
                P2=rec_buff                        ; 1st longword of record
        PUSHAQ  faoout
        CALLS   #1, G^LIB$PUT_OUTPUT

        CMPW        rec_buff,#1                        ; check 1st record type field
        BEQL        1$

        PUSHAQ        bad_rec
        CALLS   #1, G^LIB$PUT_OUTPUT
        BISL        #FAB$M_DLT,OUTFAB+FAB$L_FOP        ; delete OUTFILE on close
        BRW        done

1$:
        MOVW        #^x03FE,OUTRAB+RAB$W_RSZ        ; 1st record is 0x3FE long
        MOVAB        rec_buff,OUTRAB+RAB$L_RBF        ; start of offset 0
        $PUT        RAB=OUTRAB                        ; output 1st HDR record
        BLBS        R0, 2$
        BRW        EXIT4

2$:
        MOVAL        rec_buff+^x03FE,rec_ptr                ; point to next record        
        CMPW        #2,rec_buff+^x03FE                ; 2nd header type field = 2 ?
        BNEQ        copy_records

        INCL        record                                ; increment record number

        MOVW        #256, faoout                        ; reset buffer length
        $FAO_S        CTRSTR=rec_str,-
                OUTBUF=faoout,-
                OUTLEN=faoout,-
                P1=record,-                        ; record number
                P2=rec_buff+^x03FE                ; 1st longword of record 2

        PUSHAQ  faoout
        CALLS   #1, G^LIB$PUT_OUTPUT

        ADDL2        #^x03FE,OUTRAB+RAB$L_RBF        ; point to hdr 2
        $PUT        RAB=OUTRAB                        ; output 2nd HDR record
        BLBS        R0, 5$
        BRW        EXIT4

5$:
        ADDL2        #^x03FE, rec_ptr                ; point to next record

;
; Copy remaining records of a mangled .DECW$BOOK file
;
; rec_ptr        - point to start of next record in REC_BUFF
;                  contents of @rec_ptr should be xxxxtttt
;                  with xxxx = length of next record in bytes
;                       tttt = type of record

copy_records:
        MOVL        rec_ptr, R2                     ; address of next record

        ADDL2        #2, R2                                ; point to size word
        MOVZWL        (R2), OUTRAB+RAB$W_RSZ                ; size of next record

        MOVZWL        (R2), R3                        ; record size
        ADDL2        rec_ptr, R3                        ; point past next record

        CMPL        R3, end_of_rec_buff                ; outside REC_BUFF ?
        BGTRU        11$                                ; next record inside REC_BUFF

        MOVL        rec_ptr,OUTRAB+RAB$L_RBF        ; start of record
        INCL        record                                ; increment record number

        MOVW        #256, faoout                        ; reset buffer length
        $FAO_S        CTRSTR=rec_str,-
                OUTBUF=faoout,-
                OUTLEN=faoout,-
                P1=record,-                        ; record number
                P2=@rec_ptr                        ; 1st longword of record

        PUSHAQ  faoout
        CALLS   #1, G^LIB$PUT_OUTPUT

        TSTL        @rec_ptr                        ; NULL = end-of-file
        BEQL        19$                                ; if EQL -> done

        CMPW        @rec_ptr, #0                        ; must be > 0
        BLEQ        18$
        CMPW        @rec_ptr, #32                        ; check record type field < 33 ?
        BLEQ        20$
        BRB        18$

11$:        BRW        10$                                ; branch aid for VAX

18$:
        PUSHAQ        bad_rec
        CALLS   #1, G^LIB$PUT_OUTPUT
        BISL        #FAB$M_DLT,OUTFAB+FAB$L_FOP        ; delete OUTFILE on close
19$:        BRW        done                                ; branch aid for VAX



20$:        CMPW    @rec_ptr, #5                    ; 5 = LAST record ?
        BNEQ        25$

; 
; for the LAST record to be written, correctly determine record length
;
; The last record looks like this (from hyperreader.c):
;
; "Last" Record
; -------------
; variable size
; part type:                      short   0x00 (type=0x0005)
; part length:                    long    0x02
; then starting at offset 0x06:
; (1 .. number of parts in the book) series of 10 byte units, each:
; VBN (512) of part               long    0x00
; position in VBN of part         short   0x04
; length of this part             long    0x06
;

        MULL3        n_parts, #10, R2                ; no. of parts times 10 bytes
        ADDL2        #6, R2                                ; add part type + length
        MOVL        R2, OUTRAB+RAB$W_RSZ                ; actual size of LAST record

25$:
        $PUT        RAB=OUTRAB                        ; output record
        BLBS        R0, 26$
        BRW        EXIT4

26$:
        CMPW        @rec_ptr, #5                        ; 5 = LAST record written ?
        BEQL        27$                                ; EQL: yes -> done

        MOVL        R3, rec_ptr                        ; point to next record

        BRW        copy_records                        ; copy next record

27$:        BRW        done                                ; branch aid for VAX

;
; current record exceeds end of data in REC_BUFF
; copy record data from @rec_ptr until @end_of_rec_buff to start of REC_BUFF,
; then read next blocks from INFILE, then output current record
;

10$:
        MOVL        end_of_rec_buff, R2
        SUBL2        rec_ptr, R2                        ; no of bytes to copy
        PUSHL        R2
        MOVC3        R2, @rec_ptr, rec_buff
        POPL        R2

        MOVAL        rec_buff, rec_ptr        ; point to beginning of REC_BUFF

        ADDL2        #rec_buff, R2                ; point past copied part of record
        MOVL        R2, INRAB+RAB$L_UBF        ; set input read buffer address

        SUBL3        R2, #end_of_rec_buff, R2  ; get no. of bytes to read
        DIVL2        #512, R2                    ; get rounded no. of blocks
        ADDL2        R2, vbn                        ; starting VBN for next $READ
        MULL2        #512, R2                ; set no. of bytes to read
        
        MOVW        R2, INRAB+RAB$W_USZ        ; set input buffer size
        MOVL        INRAB+RAB$L_UBF,end_of_rec_buff
        ADDL2        R2, end_of_rec_buff        ; new end of rec_buff

        MOVZWL        INRAB+RAB$W_USZ, R2
        MOVW        #256, faoout                ; reset buffer length
        $FAO_S        CTRSTR=read_str,-
                OUTBUF=faoout,-
                OUTLEN=faoout,-
                P1=R2,-                        ; no. of bytes to read
                P2=vbn                        ; starting VBN

        PUSHAQ  faoout
        CALLS   #1, G^LIB$PUT_OUTPUT

        $READ        RAB=INRAB
        BLBC        R0, 12$
        BRW        copy_records                ; process next record

12$:
        CMPL        R0,#RMS$_EOF
        BEQL        DONE

        BRW        EXIT2

DONE:        $CLOSE        FAB=INFAB
        $CLOSE        FAB=OUTFAB
        RET

EXIT:        PUSHL        R3
        PUSHL        R2
        CALLS        #2, G^LIB$SIGNAL
        RET

        .END        FIX_BOOK