.title DU Disk usage by directory
;++
;
; Usage:
; $ DU
/SINCE=date
; /BEFORE=date
; /MODIFIED
; /MINIMUM=size
; /OUTPUT=output-file
;
; DU displays the total number of blocks in the named directory. Wildcards
; are supported, including [...].
;
; DU works by searching the index file ([000000]INDEXF.SYS) for files with
; directories matching the spec given. Note that file backlinks are followed,
; so files entered in multiple directories will only appear once.
;
; The index file is read using large double-buffered $QIOs, and on an HSC
; connected RA82 parses approximately 1000 files per second, making it faster
; than DIRECTORY for all but the smallest jobs, as well as more useful for
; those big disk space hunts.
;
;
; Don Stokes 7-Oct-1991
; GP Print Ltd, Wellington, New Zealand
; don@zl2tnm.gp.co.nz
;
; Modifications:
; 3/11/91/dcs Added /OUTPUT qualifier
;
;--
.sbttl "Macro stuff"
.library "SYS$LIBRARY:LIB"
$HM2DEF ; Home block definitions
$FH2DEF ; File header
$FI2DEF ; File header ID area
$FATDEF ; File attributes
$RMSDEF ; RMS stuff
$DSCDEF
;
; DESCRIPTOR macro - create string descriptor
; Usage: DESCRIPTOR [string [,length [,type [,class]]]]
;
.macro descriptor addr=0, len=0, type=T, class=S
.ascid "" ; Force DEBUG to view the descriptor
.=.-8 ; as a string rather than a word
.word len
.byte DSC$K_DTYPE_'type'
.byte DSC$K_CLASS_'class'
.long addr
.endm
;
; CALL macro - call a subroutine, pass parameters on stack
; Usage: CALL routine [,p1...,p20]
;
.macro call routine, -
p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11, -
p12,p13,p14,p15,p16,p17,p18,p19,p20
.narg call.argc
call.argc = call.argc - 1
call.argn = 20
.irp call.argv,
.if less_equal call.argn - call.argc
pushal call.argv
.endc
call.argn = call.argn - 1
.endr
calls #call.argc, G^routine
.endm
;
; Output macro. Prints a line to the output file
; Usage: OUTPUT string
;
.macro output string
movl string+4, output_rab+RAB$L_RBF
movw string, output_rab+RAB$W_RSZ
$PUT output_rab
status R0
.endm
;
; Status macro. Bails out on dud return codes
; Usage: STATUS code
;
.macro status code, ?L1
blbs code, L1
$EXIT_S code
L1: .endm
.sbttl "Data areas"
.psect du_rw, long,wrt,noexe,noshr
buf1: .blkb 32768 ; Buffers for INDEXF fast parse
buf2: .blkb 32768
iosb1: .blkw 4 ; IOSBs for INDEXF read
iosb2: .blkw 4
read_block: .blkl ; Next block to read
index1: .blkl ; Pointers into index array
index2: .blkl
block_index: .blkl ; Next pointer to index array
last_index: .blkl ; Last valid index in array
chan: .blkw ; Channel file is open on
iosb: .blkw 4 ; General dogsbody IOSB
first_block: .blkl ; VBN of first index block in INDEXF
indexaddr: .blkl 2 ; Address of index array (for $EXPREG)
dataaddr: .long 0 ; Address of data area (for $EXPREG)
.blkl
bitmap_max: .blkl ; Size of bitmap address
bitmapaddr: .blkl 2 ; $EXPREG return address for bitmap
homeblock: .blkb 512 ; Home block buffer
.align long
indexf_fab: $FAB fnm=<[000000]INDEXF.SYS>, fop=ufo, xab=indexf_xabfhc
indexf_xabfhc: $XABFHC ; FAB & XABFHC for INDEXF.SYS
.align long
dirparse_fab: $FAB nam=dirparse_nam ; Stuff for directory parse
dirparse_nam: $NAM esa=dirparse_esa, ess=255, nop=noconceal
dirparse_esa: .blkb 256
badsquares: .ascii "][" ; For dealling with [DIR1.][DIR2]
zerodir: .ascii "[000000]" ; Generally useful
star_dot_dir: .ascii "*" ; used as "*.DIR", don't split these
dot_dir: .ascii ".DIR" ; lines!
dirlisthead: .long 0 ; Header of link list of dir names
dirlistlast: .long dirlisthead ; Floating pointer to last list item
lastdirnum: .long 0 ; Tracks parent directory changes
mfdparent: .long 1 ; Fake directory name for MFD
.ascii "[" ; For faking top
getjpi_itmlst: .word 8 ; Item list for getting image privs
.word JPI$_IMAGPRIV ; mask to disable privs when not
.long imagpriv ; needed.
.long 0
.long 0
imagpriv: .blkq
null_str: .ascid "" ; FAO strings etc for output
column_heading: .ascid "Directory_______________________________" -
"________________Used_Allocated_____Files"
fao1: .ascid "!60!20%D"
fao2: .ascid "!50 !9UL !9UL !9UL"
fao3: .ascid "!AD!AD...]!/!50 !9UL !9UL !9UL"
fao4: .ascid "!50 !9UL !9UL !9UL"
faoout: descriptor 1$, 256
1$: .blkb 256
;
; CLI stuff
;
cli_p1: .ascid "P1" ; Directory name
cli_since: .ascid "SINCE" ; /SINCE=date
cli_before: .ascid "BEFORE" ; /BEFORE=date
cli_minimum: .ascid "MINIMUM" ; /MINIMUM blocks in directory
cli_modified: .ascid "MODIFIED" ; /MODIFIED flag
cli_output: .ascid "OUTPUT" ; /OUTPUT=file
since_flag: .blkb
before_flag: .blkb
minimum_flag: .blkb
modified_flag: .blkb
.align long
since: .blkq
before: .blkq
minimum: .blkl
min_tmp_1: .byte ^A"+"
.blkb 11
min_tmp_2: .blkb 8
since_d: descriptor 0,0,T,D ; Dynamic descriptor for minimum
before_d: descriptor 0,0,T,D ; Dynamic descriptor for minimum
minimum_d: descriptor 0,0,T,D ; Dynamic descriptor for minimum
dirname_d: descriptor 0,0,T,D ; Dynamic descriptor for directory name
output_d: descriptor 0,0,T,D ; Dynamic descriptor for output file
today: descriptor today_data, 11
today_full: descriptor today_data, 20
today_data: .ascii "xx-xxx-xxxx 0:0:0.00"
.align long
search_fab: $FAB nam=search_nam ; Stuff for directory search
search_nam: $NAM esa=search_esa, ess=255, rsa=search_rsa, rss=255
search_esa: .blkb 256
search_rsa: .blkb 256
.align long
output_fab: $FAB fnm=, dnm=, rat=cr
output_rab: $RAB fab=output_fab ; Output file
;
; Constants describing dynamic structures
;
ib_backlink = 0 ; long, file number of backlink
ib_alloc = 4 ; long, allocated size of file
ib_used = 8 ; long, used size of file
ib_dirinfo = 12 ; long, address of directory info block
ib_length = 16
di_nextdir = 0 ; Index to next directory entry
di_dirnam = 4 ; Directory name pointer if different
di_alloc = 8 ; long, allocated size of files in dir
di_used = 12 ; long, used size of files in directory
di_files = 16 ; long, number of files in directory
di_namelen = 20 ; long, length of directory name
di_name = 24 ; text:40, directory name
di_length = 64
di_namlength = 40
.sbttl "Initialisation"
.psect du_re, long,nowrt,exe,shr
;
; Register usage for data gathering:
; R0-R5 Scratch
; R6 Launch next QIO countdown
; R7 Index pointer
; R8 ID block address
; R9 Bitmap pointer
; R10 Address of next text block
; R11 Address of file header
;
.entry du, ^M
;
; FIRST! Disable privileges (if any), saving them for later.
;
$GETJPIW_S itmlst=getjpi_itmlst, iosb=iosb
status R0
status iosb
$SETPRV_S prvadr=imagpriv
status R0
;
; Get directory name, parse to ensure it exists
;
clrb since_flag
clrb before_flag
clrb minimum_flag
clrb modified_flag
call CLI$GET_VALUE cli_p1, dirname_d
status R0
cvtwb dirname_d, indexf_fab+FAB$B_DNS
movl dirname_d+4, indexf_fab+FAB$L_DNA
movaq dirname_d, R2
jsb get_dir_file
;
; Look for /MINIMUM flag
;
call CLI$GET_VALUE cli_minimum, minimum_d
cmpl R0, #CLI$_ABSENT
beql 2$
incb minimum_flag
movc3 minimum_d, @minimum_d+4, min_tmp_1+1
cvtsp minimum_d, min_tmp_1, #8, min_tmp_2
cvtpl #8, min_tmp_2, minimum
2$:
;
; Check /SINCE
; This defaults to today
;
call CLI$PRESENT cli_since
cmpl R0, #CLI$_ABSENT
beql 3$
incb since_flag
movaq since_d, R2
call CLI$GET_VALUE cli_since, since_d
cmpl R0, #CLI$_ABSENT
bneq 4$
call LIB$DATE_TIME today
status R0
movaq today_full, R2
4$: $BINTIM_S timbuf=(R2), timadr=since
status R0
3$:
;
; Check /BEFORE
; This defaults to today
;
call CLI$PRESENT cli_before
cmpl R0, #CLI$_ABSENT
beql 6$
incb before_flag
movaq before_d, R2
call CLI$GET_VALUE cli_before, before_d
cmpl R0, #CLI$_ABSENT
bneq 7$
call LIB$DATE_TIME today
status R0
movaq today_full, R2
7$: $BINTIM_S timbuf=(R2), timadr=before
status R0
6$:
;
; Check /MODIFIED
;
call CLI$PRESENT cli_modified
cmpl R0, #CLI$_ABSENT
beql 5$
incb modified_flag
5$:
;
; Check /OUTPUT
; This defaults to SYS$OUTPUT
; Open the output file now.
;
call CLI$GET_VALUE cli_output, output_d
cmpl R0, #CLI$_ABSENT
beql 8$
movl output_d+4, output_fab+FAB$L_FNA
cvtwb output_d, output_fab+FAB$B_FNS
8$: $CREATE output_fab
status R0
$CONNECT output_rab
status R0
;
; Fire off first $SEARCH.
;
movb R6, search_fab+FAB$B_FNS
movl R7, search_fab+FAB$L_FNA
$PARSE search_fab
status R0
$SEARCH search_fab
status R0
;
; Do UFO open on INDEXF.SYS
; First enable privs so we can get a looksee at this thing.
; Turn 'em off after the open.
;
$SETPRV_S enbflg=#1, prvadr=imagpriv
status R0
$OPEN indexf_fab
status R0
movw indexf_fab+FAB$L_STV, chan
$SETPRV_S prvadr=imagpriv
status R0
;
; Grab home block (lotsa useful goodies in there!)
;
$QIOW_S chan=chan, iosb=iosb, func=#IO$_READVBLK, -
p1=homeblock, p2=#512, p3=#2
status R0
status iosb
;
; Load the index file bitmap into memory
;
movzwl homeblock+HM2$W_IBMAPSIZE, R2
$EXPREG_S pagcnt=R2, retadr=bitmapaddr
status R0
movzwl homeblock+HM2$W_IBMAPVBN, R0
ashl #9, R2, R2 ; R2 = size of bitmap, bytes
$QIOW_S chan=chan, iosb=iosb, func=#IO$_READVBLK, -
p1=@bitmapaddr, p2=R2, p3=R0
status R0
status iosb
movl bitmapaddr, R9 ; R9 = bitmap pointer
addl3 R2, R9, bitmap_max
;
; Calculate file sizes from home block data
;
divl3 #4096, homeblock+HM2$L_MAXFILES, R0
movzwl homeblock+HM2$W_CLUSTER, R1
mull2 #4, R1
addl2 R1, R0
addl3 #2, R0, read_block
decl R0
movl R0, first_block
;
; Calculate number of headers in file and allocate index array accordingly
;
movl indexf_xabfhc+XAB$L_EBK, R2
tstw indexf_xabfhc+XAB$W_FFB
bneq 1$
decl R2
1$: subl2 R0, R2 ; R1 = number of headers
ashl #-7, R2, R2 ; hdrs * 4 / 512
incl R2
$EXPREG_S pagcnt=R2, retadr=indexaddr
status R0
;
; Allocate the first data block, and fire off the first QIO to INDEXF.SYS
;
jsb infoblock
movl indexaddr, block_index
jsb fire_1
.sbttl "First phase -- read INDEXF.SYS details into memory"
;
; Double buffered INDEXF read:
; Synch with current QIO
; Exit if EOF
; Fire off QIO to other buffer
; Process
; Then do the same with the other buffer, firing off a QIO on the first.
;
loop: jsb fire_2
$SYNCH_S efn=#1, iosb=iosb1
cmpw iosb1, #SS$_ENDOFFILE
bneq 1$
brw 10$
1$: status iosb1
movl index1, R7 ; R7 = index pointer
movzwl iosb1+2, R0
ashl #-9, R0, R6 ; R6 = Countdown to next QIO
movab buf1, R11 ; R11 = first header
jsb process_block
jsb fire_1
$SYNCH_S efn=#2, iosb=iosb2
cmpw iosb2, #SS$_ENDOFFILE
beql 10$
status iosb2
movl index2, R7 ; R7 = index pointer
movzwl iosb2+2, R0
ashl #-9, R0, R6 ; R6 = Countdown to next QIO
movab buf2, R11 ; R11 = first header
jsb process_block
brw loop
;
; Come here on EOF
; Abort any outstanding I/O (it'll fail anyway)
; Free the bitmap memory.
; Mark the end of the index array and get the index base address for the
; next phases.
;
10$: $DASSGN_S chan=chan
status R0
$DELTVA_S inadr=bitmapaddr
status R0
movl #-1, @last_index ; Mark end of array
subl3 #4, indexaddr, R11 ; R11 = base address
.sbttl "Second phase -- Search for directories to summarise"
;
; We've got all the raw data now, so lets find out what we want to look at.
; The NAM got set up before, we just use it now.
;
; R11 = Base address for index array
; R10 = Pointer to directory info
; R9 = Pointer to info block array
;
; Get each filespec...
;
search_loop:
;
; Make sure there's room for the next directory item, if not then make some.
;
addl3 #di_length, R10, R0
cmpl R0, dataaddr+4
blss 2$
jsb infoblock
2$: ; R10 = address if dir block
;
; Set the high bit of the index. The second BICL is in case the bit was
; already set (shouldn't happen, but on a roached disk....), since we need
; the address without the high bit set anyway.
; If the little beggar is blank, then ignore it.
;
movzbl search_nam+NAM$W_FID+5, R2
ashl #16, R2, R2
movw search_nam+NAM$W_FID, R2 ; R2 = file number of directory
tstl (R11)[R2]
bneq 6$
brw 1$
6$: bisl2 #^x80000000, (R11)[R2]
bicl3 #^x80000000, (R11)[R2], R9
; R9 = file info block
;
; Add our directory info block to the file info one already present.
;
movl R10, ib_dirinfo(R9)
movzbl search_nam+NAM$B_NAME, R6 ; R6 = length of name
movl R6, di_namelen(R10)
movc3 R6, @search_nam+NAM$L_NAME, di_name(R10)
clrl di_alloc(R10)
clrl di_used(R10)
clrl di_files(R10)
;
; Add the entry into the list of directories
;
clrl di_nextdir(R10)
movl R10, @dirlistlast
moval di_nextdir(R10), dirlistlast
;
; Move free space pointer up a little. Save current value in case we need it
; for the parent directory name.
;
movl R10, R8 ; R8 = address of directory blk
addl2 #di_length, R10
;
; Add directory name entry if not blank
;
clrl di_dirnam(R8)
movl ib_backlink(R9), R0 ; R0 = file number of parent
cmpl R0, lastdirnum
beql 1$
cmpl R0, #4
bneq 5$
movab mfdparent, di_dirnam(R8)
brb 1$
5$: movl R0, lastdirnum
movzbl search_nam+NAM$B_DIR, R7 ; R7 = length of directory spec
addl3 #4, R7, R6 ; R6 = length of parent blk)
addl2 R10, R6
cmpl R6, dataaddr+4
blss 4$
jsb infoblock
4$: movl R10, di_dirnam(R8)
movl R7, (R10)
movc3 R7, @search_nam+NAM$L_DIR, 4(R10)
movb #^A".", 3(R10)[R7] ; Change trailing ']' to '.'
movl R6, R10
1$: $SEARCH search_fab
cmpl R0, #RMS$_NMF
beql 3$
status R0
brw search_loop
3$:
.sbttl "Third phase -- Do summarizing by following backlinks"
;
; Now process gathered info....
;
; Register usage from here on in:
; R0-R5 Scratch
; R9 Database entry pointer for file name
; R10 Index pointer
; R11 Index base register
;
clrl R10 ; R10 = index
;
; Loop through index array....
; -1 = end of array
; 0 = No header (index array is allocated from demand-zero pages)
; Other = valid header
;
header_loop: incl R10
movl (R11)[R10], R9 ; R9 = addr of file
beql header_loop
cmpl R9, #-1
bneq 1$ ; -1 = end of array
brw show_info
1$: bicl2 #^x80000000, R9 ; Clear MSB(can you say ACCVIO?)
;
; Search through backlinks.
; Quit if backlink == 4 or 0, or backlink is invalid
; (file #4 is MFD; we'll loop horribly if we don't handle this case!)
; Get next backlink if this one is not marked as being one to look at.
; First check that file has not been marked as to be ignored
;
movl R9, R1 ; R1 = pointer to current info
cmpl ib_alloc(R1), #-1 ; Does it qualify?
beql header_loop ; Nope, go round
3$: movl ib_backlink(R1), R0 ; R0 = backlink index
beql header_loop ; No backlink? Oh never mind.
movl (R11)[R0], R1 ; R1 = new info ptr
beql header_loop ; Nothing there? Oh dear.
blss 4$ ; High bit not set, go around
cmpl R0, #4 ; Number 4 = [000000] directory
beql header_loop
brb 3$
;
; Found a file that matches our criteria, add its size to the directory info
; Remember to remove flag bit from top of database pointer
;
4$: bicl2 #^x80000000, R1
movl ib_dirinfo(R1), R6 ; R6 = directory info pointer
addl2 ib_alloc(R9), di_alloc(R6)
addl2 ib_used(R9), di_used(R6)
incl di_files(R6)
brw header_loop
.sbttl "Final phase -- Display the info.
;
; Now dump out all the interesting stuff
; First, talk about the disk structure.
;
show_info: clrl R6 ; R6 = Total space used
clrl R7 ; R7 = Total space allocated
clrl R8 ; R8 = Total files
clrl R10 ; R10 = Number of directories
movzbl search_nam+NAM$B_DEV, R0
movw #256, faoout
$FAO_S ctrstr=fao1, outlen=faoout, outbuf=faoout, -
p1=#HM2$S_VOLNAME, p2=#homeblock+HM2$T_VOLNAME, -
p3=R0, p4=search_nam+NAM$L_DEV, p5=#0
OUTPUT faoout
OUTPUT null_str
OUTPUT column_heading
movl dirlisthead, R9
show_loop: tstl R9 ; Null pointer signals end
bneq 5$
brw show_totals
5$: movl di_dirnam(R9), R0 ; R0 = addr of parent info or 0
beql 1$
movl (R0), R4 ; R4 = length of parent dir
addl3 #4, R0, R5 ; R5 = address of parent dir
1$: tstb minimum_flag ; Now check /MINIMUM
beql 4$
cmpl di_alloc(R9), minimum
bgeq 4$
brw 3$
4$:
movab di_name(R9), R0
movaq fao2, R1
cmpl R4, #50
bleq 2$
movaq fao2, R1
2$: movw #256, faoout
$FAO_S ctrstr=(R1), outlen=faoout, outbuf=faoout, -
p1=R4, p2=R5, p3=di_namelen(R9), p4=R0, -
p5=di_used(R9), p6=di_alloc(R9), p7=di_files(R9)
OUTPUT faoout
addl2 di_used(R9), R6
addl2 di_alloc(R9), R7
addl2 di_files(R9), R8
incl R10
3$: movl di_nextdir(R9), R9
brw show_loop
show_totals: OUTPUT null_str
movw #256, faoout
$FAO_S ctrstr=fao4, outlen=faoout, outbuf=faoout, -
p1=R10, p2=R6, p3=R7, p4=R8
OUTPUT faoout
;
; Come here when all done -- time to depart!
;
$CLOSE output_fab
ret ; Bye bye!
.sbttl "Process file headers"
;
; Routine to handle each (up to) 32768 byte block of INDEXF.SYS.
;
;
; For each header in block....
; If file header is invalid (ie deleted, never used) or this is not
; the primary header then skip it.
;
process_block: tstw FH2$W_SEG_NUM(R11) ; Skip if not primary header
bneq 4$
tstw FH2$W_FID_NUM(R11) ; Skip if file header invalid
bneq 5$
tstb FH2$B_FID_NMX(R11)
bneq 5$
4$: brw 2$
5$: bbs #FH2$V_MARKDEL, FH2$L_FILECHAR(R11), 4$ ; test for deleted
;
; Add header to file info database and index
; Copy backlink to database
;
movl R10, (R7)
movw FH2$W_BK_FIDNUM(R11), ib_backlink(R10)
movzbw FH2$B_BK_FIDNMX(R11), ib_backlink+2(R10)
;
; Copy things like blocks used, blocks allocated to database
;
ashl #16, FH2$W_RECATTR+FAT$W_EFBLKH(R11), R0
movw FH2$W_RECATTR+FAT$W_EFBLKL(R11), R0
bleq 1$
tstw FH2$W_RECATTR+FAT$W_FFBYTE(R11)
bneq 1$
decl R0
1$: movl R0, ib_used(R10)
ashl #16, FH2$W_RECATTR+FAT$W_HIBLKH(R11), R0
movw FH2$W_RECATTR+FAT$W_HIBLKL(R11), R0
movl R0, ib_alloc(R10)
;
; Check /SINCE qualifier. If this file doesn't qualify then stick -1 into the
; allocated size.
;
tstb since_flag
beql 6$
cvtbl FH2$B_IDOFFSET(R11), R0
movaw (R11)[R0], R0
tstb modified_flag
beql 8$
cmpl FI2$Q_REVDATE+4(R0), since+4
bneq 7$
cmpl FI2$Q_REVDATE(R0), since
brb 7$
8$: cmpl FI2$Q_CREDATE+4(R0), since+4
bneq 7$
cmpl FI2$Q_CREDATE(R0), since
7$: bgtr 6$
movl #-1, ib_alloc(R10)
6$:
;
; Check /BEFORE qualifier. If this file doesn't qualify then stick -1 into the
; allocated size.
;
tstb before_flag
beql 10$
cvtbl FH2$B_IDOFFSET(R11), R0
movaw (R11)[R0], R0
tstb modified_flag
beql 12$
cmpl FI2$Q_REVDATE+4(R0), before+4
bneq 11$
cmpl FI2$Q_REVDATE(R0), before
brb 11$
12$: cmpl FI2$Q_CREDATE+4(R0), before+4
bneq 11$
cmpl FI2$Q_CREDATE(R0), before
11$: blss 10$
movl #-1, ib_alloc(R10)
10$:
;
; Move pointer to next free byte of space, if at end of allocated block
; then allocate a new one.
;
addl2 #ib_length, R10
addl3 #ib_length, R10, R0
cmpl R0, dataaddr+4
blss 3$
jsb infoblock
3$:
;
; Advance to next header, loop unless we need to be fed from disk again.
; Save last index pointer as we go.
;
2$: addl2 #512, R11
addl2 #4, R7
movl R7, last_index
decl R6
beql 9$
brw process_block
9$: rsb
.sbttl "QIOs to INDEXF.SYS"
;
; Routines to fire QIOs off to INDEXF.SYS
; These fire off QIOs only to chunks that actually have valid headers in them.
; If the advancing over invalid blocks clears the end of file, then we pretend
; that we hit EOF.
;
fire_1: jsb find_next
blbc R0, 1$
movl block_index, index1
$QIO_S efn=#1, chan=chan, iosb=iosb1, func=#IO$_READVBLK, -
p1=buf1, p2=R1, p3=read_block
status R0
brb move_pointers
1$: movw #SS$_ENDOFFILE, iosb1
$SETEF_S efn=#1
rsb
move_pointers: addl2 #64, read_block ; Block number for next QIO
addl2 #<64*4>, block_index ; Index to save for QIO
addl2 #8, R9 ; Pointer into index bitmap
rsb
fire_2: jsb find_next
blbc R0, 1$
movl block_index, index2
$QIO_S efn=#2, chan=chan, iosb=iosb2, func=#IO$_READVBLK, -
p1=buf2, p2=R1, p3=read_block
status R0
brb move_pointers
1$: movw #SS$_ENDOFFILE, iosb2
$SETEF_S efn=#2
rsb
;
; Advance things to the next 32-header boundary that contains a file header.
;
find_next: tstb (R9)
bneq 1$
incl R9
cmpl R9, bitmap_max
bgeq 2$
addl2 #8, read_block
addl2 #<8*4>, block_index
brb find_next
2$: clrl R0
rsb
;
; Come here when we've found a block to get, trim I/O size to just get stuff
; we want.
;
1$: movl #32768, R1
movl #7, R0
3$: tstb (R9)[R0]
bneq 4$
decl R0
subl2 #4096, R1
brb 3$
4$: movl #1, R0
rsb
.sbttl "Allocate memory"
;
; Grab 256K or memory for file & directory databases
;
infoblock: movl dataaddr, R2
$EXPREG_S pagcnt=#512, retadr=dataaddr
status R0
movl dataaddr, R10
movl R2, (R10)+
movl dataaddr+4, (R10)+
rsb
.sbttl "Directory spec to file name parse"
;
; Routine to parse directory spec to name of parent file
; Input: R2 = Address of descriptor
; Output: R6 = length of resultant spec
; R7 = address of resultant spec
;
get_dir_file:
;
; Get raw directory
;
cvtwb (R2), dirparse_fab+FAB$B_FNS
movl 4(R2), dirparse_fab+FAB$L_FNA
$PARSE dirparse_fab
status R0
movzbl dirparse_nam+NAM$B_DIR, R6 ; R6 = dir length
movl dirparse_nam+NAM$L_DIR, R7 ; R7 = dir address
;
; Deal with pathological cases eg [0,0],
;
movl R6, R0
4$: decl R0
blss 5$
cmpb (R7)[R0], #^A"<"
beql 41$
cmpb (R7)[R0], #^A">"
beql 42$
cmpb (R7)[R0], #^A","
bneq 4$
movl #8, R6
movc3 R6, zerodir, (R7)
brb 5$
41$: movb #^A"[", (R7)[R0]
brb 4$
42$: movb #^A"]", (R7)[R0]
brb 4$
5$:
;
; Look for [dir1.][dir2] case, remove spare ][
;
matchc #2, badsquares, R6, (R7)
bneq 1$
subl3 #2, R3, R0
movc3 R2, (R3), (R0)
subl2 #2, R6
1$:
;
; Yet another pathological case: ...], convert to ...]*.DIR and quit
;
movab -4(R7)[R6], R0
cmpl #^A"...]", (R0)
bneq 6$
movc3 #5, star_dot_dir, (R7)[R6]
addl2 #5, R6
brw 9$
6$:
;
; Find last '.'
;
movl R6, R8 ; R8 = last '.'
2$: decl R8
beql 3$
cmpb (R7)[R8], #^A"."
bneq 2$
;
; '.' found, convert [XX.YY] to [XX]YY.DIR
; But first check that we aren't dealling with two '.'s, ie part of [...X].
; If so, then move things along one to convert the right '.' to a ']'.
;
cmpb -1(R7)[R8], #^A"."
bneq 7$
subl3 R8, R6, R0
movc3 R0, (R7)[R8], 1(R7)[R8]
incl R8
incl R6
7$: movb #^A"]", (R7)[R8]
decl R6
movc3 #4, dot_dir, (R7)[R6]
addl2 #4, R6
brb 9$
;
; No '.', convert [XX] to [000000]XX.DIR
;
;
3$: subl2 #2, R6
movc3 R6, 1(R7), 8(R7)
movc3 #8, zerodir, (R7)
addl2 #8, R6
movc3 #4, dot_dir, (R7)[R6]
addl2 #4, R6
;
; File parse done. Put into search_fab, re-$parse and do the first search.
; This is so we don't have to wait for the index file search if this screws up
;
9$:
addb2 dirparse_nam+NAM$B_DEV, R6
movl dirparse_nam+NAM$L_DEV, R7
rsb
.end du