.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