home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume1 / 8709 / 19 < prev    next >
Encoding:
Text File  |  1990-07-13  |  71.3 KB  |  1,363 lines

  1. Path: uunet!seismo!sundc!pitstop!sun!amdcad!ames!ll-xn!adelie!necntc!ncoast!allbery
  2. From: dave@safari.UUCP
  3. Newsgroups: comp.sources.misc
  4. Subject: Disk Usage for VAX/VMS
  5. Keywords: vms, not-shar, not-c
  6. Message-ID: <4771@ncoast.UUCP>
  7. Date: 29 Sep 87 20:37:26 GMT
  8. Sender: allbery@ncoast.UUCP
  9. Lines: 1350
  10. Approved: allbery@ncoast.UUCP
  11. X-Archive: comp.sources.misc/8709/19
  12.  
  13. This is a program for VAX/VMS users interested in finding out not only
  14. where their disk space is going, but also where the most significant
  15. changes are occuring.
  16.  
  17. --------------------------  Cut Here  --------------------------------
  18.  
  19.                 .title  DISK_USAGE  Provides information on disk usage
  20. ;------------------------------------------------------------------------
  21. ;       
  22. ;       DISK_USAGE                              8/87  D.A. Munroe
  23. ;
  24. ;
  25. ;       Overview:
  26. ;       ---------
  27. ;       This program shows disk usage in terms of the directory structure.
  28. ;       By the use of comparison files, shows where the most significant
  29. ;       changes are occurring.
  30. ;
  31. ;       As explained below, this program makes use of a data file produced
  32. ;       by the ANALYZE/DISK_STRUCTURE command and as a result produces two
  33. ;       files: a listing file and a comparison file.
  34. ;
  35. ;
  36. ;       The listing file:
  37. ;       -----------------
  38. ;       This file shows how many files and blocks are in use by each directory
  39. ;       and subdirectory.  The entries are organized in both an alphabetical
  40. ;       and a heirarchical order which reflects the directory structure.  The
  41. ;       file count and block usage information for a parent directory includes
  42. ;       all the files and blocks of its subdirectories.  In addition, the
  43. ;       listing file also provides such information as the volume name, owner
  44. ;       name, and the time when the usage data file (from ANALYZE/DISK_STRUCTURE)
  45. ;       was created.
  46. ;
  47. ;       If a comparison file (described below) was provided as input to this
  48. ;       program, then the listing will also show how much increase or decrease
  49. ;       there was per directory, in terms of blocks allocated.  The comparison
  50. ;       file provides a way of showing how much change there has been between
  51. ;       the last time this program has been run and the current run.
  52. ;
  53. ;       Notes:
  54. ;               1. If a directory is empty (has no files) it will not appear
  55. ;                  in the listing because ANALYZE/DISK_USAGE will not generate
  56. ;                  an entry for it in the data file.
  57. ;
  58. ;               2. The value for blocks allocated includes those for the file
  59. ;                  header, thus this value is larger than what you see from the
  60. ;                  DIRECTORY command.
  61. ;
  62. ;               3. Files marked for deletion, but which have not actually been
  63. ;                  deleted yet, will appear at the top of the listing with no
  64. ;                  parent directory.
  65.  
  66.  
  67. ;       The comparison file:
  68. ;       --------------------
  69. ;       Each run of this program generates a comparison file as output which
  70. ;       is intended to be used as input for the next run of this program.  If
  71. ;       no input comparison file is available, the program will run normally,
  72. ;       but will not be able to show changes on the listing.  This will be the
  73. ;       case on the first run of this program.
  74. ;
  75. ;       This program will check that the comparison file corresponding to one
  76. ;       disk cannot be used as comparison for an entirely different disk; the
  77. ;       program is able to handle and does take into account addition or deletion
  78. ;       of directories when doing comparisons.
  79. ;
  80. ;       In order to better keep track of listing and comparison files on systems
  81. ;       with more than one disk, it is recommended that the user of this program
  82. ;       provide descriptive names which override the default file specifications
  83. ;       used by this program.
  84. ;
  85. ;
  86. ;       Invoking the program:
  87. ;       ---------------------
  88. ;       In order to run this program you first need to issue the command:
  89. ;
  90. ;               $ ANALYZE/DISK_STRUCTURE device:/USAGE=filespec
  91. ;
  92. ;       which will produce a data file file for input to this program (the
  93. ;       default filespec is USAGE.DAT).
  94. ;
  95. ;       This program is intended to be invoked as a foreign command.  If the
  96. ;       program resides in sys$system, you would first enter the definition:
  97. ;
  98. ;               $ DISK_USAGE := "$SYS$SYSTEM:DISK_USAGE"
  99. ;
  100. ;       then to run the program, issue the command:
  101. ;
  102. ;               $ DISK_USAGE  [filespec]
  103. ;
  104. ;       where "USAGE.DAT" is the default filename of a data file created
  105. ;       by ANALYZE/DISK_STRUCTURE.  A typical approach in using this
  106. ;       program would be to first do:
  107. ;
  108. ;               $ ANALYZE/DISK_STRUCTURE _DUA0:/USAGE=DUA0_USAGE
  109. ;               $ DISK_USAGE DUA0_USAGE
  110. ;
  111. ;       then use a similar sequence with DUA1 and so on.  The default
  112. ;       filespec is SYS$MANAGER:USAGE.DAT but may be overridden by a
  113. ;       user provided filespec on the command line.
  114.  
  115.  
  116. ;       Files used (defaults):
  117. ;       ----------------------
  118. ;       inputs:
  119. ;
  120. ;       sys$manager:usage.dat   usage data from ANALYZE/DISK_STRUCTURE
  121. ;       sys$manager:usage.cmp   (optional) comparison file, from a prior
  122. ;                               run of this program
  123. ;
  124. ;       outputs:
  125. ;
  126. ;       sys$manager:usage.lis   listing of disk usage information
  127. ;       sys$manager:usage.cmp   for use in comparing one run of this
  128. ;                               program with the next
  129. ;
  130. ;
  131. ;       Additional information:
  132. ;       -----------------------
  133. ;       A complete description of the ANALYZE/DISK_STRUCTURE command and
  134. ;       the format of the USAGE.DAT file is given in the VMS documentation
  135. ;       of the VERIFY utility and in the $USGDEF module of STARLET.MLB .
  136. ;
  137. ;       Comments, corrections, and suggestions should be sent to:
  138. ;
  139. ;                       David A. Munroe
  140. ;                       Capital Cities Communications/ABC
  141. ;                       7818 SE Stark Ave
  142. ;                       Portland, Oregon  97215
  143. ;                       (503) 251-7533
  144. ;
  145. ;               uucp:   ...tektronix!reed!omen!safari!dave
  146. ;                       ...ptsfa!safari!dave
  147. ;                       ...ihnp4!safari!dave
  148. ;
  149. ;------------------------------------------------------------------------
  150.  
  151.  
  152.                 $RMSDEF
  153.                 $USGDEF
  154.  
  155. ;       primary definitions
  156.  
  157. version         =               212.                    ; see .ident and version_msg
  158.  
  159. cmp_field_size  =               8                       ; for showing usage difference
  160. cmp_size        =               512.                    ; record size for USAGE.CMP files
  161. indent_value    =               4                       ; spacing before each subdirectory
  162. lis_size        =               80.                     ; max record size for USAGE.LIS
  163. max_name_size   =               39.                     ; for file and directory names
  164. max_nodes       =               2000.                   ; depends on number of .DIR entries
  165. q_stack_size    =               64.                     ; for comparison tree traversal
  166.  
  167.  
  168. ;--------------------------------------------------------------------------------
  169. ;               Macros
  170. ;--------------------------------------------------------------------------------
  171.  
  172. ; generate general purpose string descriptor
  173.  
  174.                 .macro  string_dsc      buffer,size
  175.                 .word           size                    ; output buffer size
  176.                 .byte           DSC$K_DTYPE_T           ; text data type
  177.                 .byte           DSC$K_CLASS_S           ; string descriptor class
  178.                 .address        buffer                  ; address of output buffer
  179.                 .endm
  180.  
  181. ; format a value using FA0 and write it to the output file
  182.  
  183.                 .macro  write_fmt       item,fao_p1,fao_p2=#0,?ok
  184.                 $fao_s  -
  185.                         ctrstr  =       'item'_fmt,-    ; descriptor for format control string
  186.                         outlen  =       fao_outlen,-    ; where to store length of formatted output
  187.                         outbuf  =       fao_outbuf,-    ; descriptor for formatted output buffer
  188.                         p1      =       fao_p1,-
  189.                         p2      =       fao_p2
  190.                 blbs    r0,ok                           ; branch if converted ok
  191.                 movab   'item'_cvt_msg,r0               ; else set up to write error message
  192.                 bsbw    write_msg                       ;
  193.                 brw     done                            ;
  194.  
  195. ok:             cvtwb   fao_outlen,lis_outlen           ; set length of formatted output
  196.                 movab   lis_outlen,r0                   ; .ASCIC format
  197.                 bsbw    write_msg                       ; write it
  198.                 .endm
  199.  
  200.  
  201.                 .ident  \v2.12\                         ; 10-Sep-1987  D.A. Munroe
  202.                 .psect  CODE,nowrt,exe
  203.                 .entry  DISK_USAGE,^m<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>
  204.  
  205. ;----------------------------------------------------------------
  206. ;       Look at the command line to see if the user provided
  207. ;       anything to override the default file name. If this
  208. ;       program is invoked with a line such as:
  209. ;
  210. ;                       $ DISK_USAGE DUA0
  211. ;
  212. ;       then the effect of calling lib$get_foreign is to put
  213. ;       "DUA0" in fname_buff (via fname_dsc).  This will then
  214. ;       override the "USAGE" default so that we will expect
  215. ;       to work with:
  216. ;
  217. ;       sys$manager:dua0.dat    (the usage data file)
  218. ;       sys$manager:dua0.cmp    (optional input comparison file)
  219. ;
  220. ;       sys$manager:dua0.lis    (usage listing file)
  221. ;       sys$manager:dua0.cmp    (new output comparison file)
  222. ;
  223. ;       Similarly, the user could have specified something like:
  224. ;
  225. ;               $ DISK_USAGE SYS$USER:[DAVE]
  226. ;
  227. ;       to redirect where the files are placed.
  228. ;
  229. ;----------------------------------------------------------------
  230.  
  231. gcml:           pushaw  fname_length            ; where to put resulting length
  232.                 clrl    -(sp)                   ; no prompt string
  233.                 pushaq  fname_dsc               ; descriptor for default name
  234.                 calls   #3,g^lib$get_foreign    ; look at command line
  235.                 blbs    r0,10$                  ; continue if ok
  236.                 ret                             ; else exit w/status in r0
  237. 10$:
  238. ;--------------------------------------------------------------------------------
  239. ;  At this point if the user has provided a name, it will be in fname_buff
  240. ;  and r2 will be its length.  Otherwise, r2 will be zero and the default
  241. ;  of sys$manager:usage.dat will be used.
  242. ;--------------------------------------------------------------------------------
  243.  
  244.  
  245. ;--------------------------------------------------------------------------------
  246. ;               Open files and connect streams.
  247. ;
  248. ;               The fna field of the data fab is a pointer to the filename
  249. ;               buffer (which may or may not contain "usage" at this time).
  250. ;--------------------------------------------------------------------------------
  251.  
  252. open:           cvtwb   fname_length,data_fab+fab$b_fns ; set filename length
  253.                 $open   fab = data_fab                  ; open USAGE.DAT
  254.                 blbs    r0,5$                           ; branch if ok
  255.                 brw     file_error                      ;
  256.  
  257. 5$:             $create  fab = lis_fab                  ; create USAGE.LIS
  258.                 blbs    r0,10$                          ; branch if ok
  259.                 brw     file_error                      ;
  260.  
  261. 10$:            movb    #1,r3                           ; assume USAGE.CMP (input) exists
  262.                 $open   fab = icmp_fab                  ; try to open USAGE.CMP
  263.                 blbs    r0,15$                          ; branch if it exists
  264.                 clrb    r3                              ; nope
  265.                 cmpl    r0,#RMS$_FNF                    ; skip if file not found
  266.                 beql    15$                             ;
  267.                 brw     file_error                      ;
  268.  
  269. 15$:            movb    r3,icmp_state                   ; USAGE.CMP (input) status
  270.                 $create  fab = ocmp_fab                 ; create USAGE.CMP (output)
  271.                 blbs    r0,20$                          ; branch if ok
  272.                 brw     file_error                      ;
  273.  
  274. 20$:            $connect  rab = data_rab                ; connect rab for USAGE.DAT
  275.                 blbs    r0,25$                          ; branch if ok
  276.                 brw     record_error                    ;
  277.  
  278. 25$:            $connect  rab = lis_rab                 ; connect rab for USAGE.LIS
  279.                 blbs    r0,30$                          ; branch if ok
  280.                 brw     record_error                    ;
  281.  
  282. 30$:            tstb    r3                              ; USAGE.CMP (input) exists?
  283.                 beql    35$                             ; branch if not
  284.                 $connect  rab = icmp_rab                ; connect rab for USAGE.CMP (input)
  285.                 blbs    r0,35$                          ; branch if ok
  286.                 brw     record_error                    ;
  287.  
  288. 35$:            $connect  rab = ocmp_rab                ; connect rab for USAGE.CMP (output)
  289.                 blbs    r0,40$                          ; branch if ok
  290.                 brw     record_error                    ;
  291.  
  292. 40$:
  293.  
  294.  
  295. ;--------------------------------------------------------------------------------
  296. ;       The IDENT record should be the first record in the file;  we read
  297. ;       it in, verify it, and print the contents of its fields
  298. ;--------------------------------------------------------------------------------
  299. ident:
  300.                 $get    rab = data_rab                  ; read ident record
  301.                 blbs    r0,5$                           ; branch if ok
  302.                 moval   data_rab,r2                     ;
  303.                 brw     record_error                    ;
  304.  
  305. 5$:             movab   version_msg,r0                  ; show program version
  306.                 bsbw    write_msg                       ;
  307.  
  308.                 cmpw    #USG$C_IDENT_LEN,data_rab+rab$w_rsz     ; length ok?
  309.                 beql    10$
  310.                 movab   ident_len_msg,r0                ; print msg if length not ok
  311.                 bsbw    write_msg                       ;
  312.                 brw     done                            ;
  313.  
  314. 10$:            movab   data_buff,r6                    ; address of ident record
  315.                 cmpb    #USG$K_IDENT,USG$B_TYPE(r6)     ; record type ok?
  316.                 beql    15$                             ; branch if ok
  317.                 movab   not_ident_msg,r0                ;
  318.                 bsbw    write_msg                       ;
  319.                 brw     done                            ;
  320.  
  321. ;--------------------------------------------------------------------------------
  322. ;       The write_fmt macro is used to format and and write the contents
  323. ;       of the IDENT record
  324. ;--------------------------------------------------------------------------------
  325.  
  326. 15$:            write_fmt -                             ; creation time
  327.                         item    = time,-
  328.                         fao_p1  = #data_buff+USG$Q_TIME
  329.  
  330.                 write_fmt -                             ; volume serial number
  331.                         item    = serial,-
  332.                         fao_p1  = USG$L_SERIALNUM(r6)
  333.  
  334.                 write_fmt -                             ; structure (volume set) name
  335.                         item    = strucname,-
  336.                         fao_p1  = #USG$S_STRUCNAME,-
  337.                         fao_p2  = #data_buff+USG$T_STRUCNAME
  338.  
  339.                 write_fmt -                             ; volume name
  340.                         item    = volname,-
  341.                         fao_p1  = #USG$S_VOLNAME,-
  342.                         fao_p2  = #data_buff+USG$T_VOLNAME
  343.  
  344.                 write_fmt -                             ; ownername (of volume)
  345.                         item    = ownername,-
  346.                         fao_p1  = #USG$S_OWNERNAME,-
  347.                         fao_p2  = #data_buff+USG$T_OWNERNAME
  348.  
  349.                 write_fmt -                             ; file format
  350.                         item    = format,-
  351.                         fao_p1  = #USG$S_FORMAT,-
  352.                         fao_p2  = #data_buff+USG$T_FORMAT
  353.  
  354.                 bsbw    blank_line                      ; write blank line
  355.  
  356.  
  357. ;--------------------------------------------------------------------------------
  358. ;       Comparison file initialization.
  359. ;
  360. ;       The code below does two things:
  361. ;
  362. ;               - it initializes the data structures for the first part
  363. ;                 of the output comparison file
  364. ;
  365. ;               - if an input comparison file exists, it reads in the
  366. ;                 file's IDENT record and checks whether the file can
  367. ;                 be used for comparison
  368. ;
  369. ;       The IDENT record from the USAGE.DAT file is used as the first part of
  370. ;       the output USAGE.CMP file.
  371. ;
  372. ;       To uniquely identify a comparison file, its USG$B_TYPE field has the
  373. ;       value -USG$K_IDENT (note the negation).  The program's version number
  374. ;       is also stored in the comparison file as a check on the integrity of the
  375. ;       file (a comparison file created by a different version of this program
  376. ;       may not have compatible pointer values in the tree structure).
  377. ;
  378. ;       When a comparison file is read in, we need to do these checks before
  379. ;       we can accept it as a comparison file:
  380. ;
  381. ;               - we need to make sure it is in fact a comparison file by
  382. ;                 checking USG$B_TYPE for the value -USG$K_IDENT
  383. ;
  384. ;               - we need to make sure it's for the same disk volume
  385. ;
  386. ;               - we need to check that it was created by the same version
  387. ;                 of the program
  388. ;
  389. ;--------------------------------------------------------------------------------
  390. cmp_init:
  391.  
  392. ;  intialize the data structures for the first part of the output .CMP file
  393.  
  394.                 mnegb   #USG$K_IDENT,USG$B_TYPE(r6)     ; identifies .CMP file
  395.                 movc3   #USG$C_IDENT_LEN,(r6),tree_ident
  396.                 movw    #version,tree_version
  397.  
  398. ;  if an input .CMP file exists, do some tests before accepting it
  399.  
  400.                 movb    icmp_state,r7                   ; does it exist?
  401.                 bneq    10$                             ; branch if yes
  402.                 movab   no_icmp_msg,r0                  ; say no input .CMP
  403.                 bsbw    write_msg                       ;
  404.                 brw     70$                             ;
  405.  
  406. 10$:            movab   cmp_tree_ident,r8               ; input address
  407.                 movl    r8,icmp_rab+rab$l_ubf           ;
  408.                 movl    #cmp_size,icmp_rab+rab$w_usz    ; set size
  409.                 $get    rab = icmp_rab                  ; read first part
  410.                 blbs    r0,20$                          ; branch if ok
  411.                 moval   icmp_rab,r2                     ;
  412.                 brw     record_error                    ;
  413.  
  414. 20$:            cmpb    USG$B_TYPE(r8),#-USG$K_IDENT    ; really .CMP file?
  415.                 beql    25$                             ; branch if yes
  416.                 movab   wrong_type_msg,r0               ;
  417.                 brb     40$                             ;
  418. 25$:            cmpc3   #USG$S_VOLNAME,-                ; volume names match?
  419.                         USG$T_VOLNAME(r6),-             ;
  420.                         USG$T_VOLNAME(r8)               ;
  421.                 beql    30$                             ; branch if yes
  422.                 movab   wrong_volume_msg,r0             ;
  423.                 brb     40$                             ;
  424.  
  425. 30$:            cmpw    #version,cmp_tree_version       ; versions match?
  426.                 bneq    35$                             ; branch if not
  427.                                                         ; else all is ok
  428.                 write_fmt -                             ; show .CMP creation time
  429.                         item    = icmp_time,-
  430.                         fao_p1  = #cmp_tree_ident+USG$Q_TIME
  431.                 brb     70$
  432.  
  433. 35$:            movab   wrong_version_msg,r0            ;
  434. 40$:            bsbw    write_msg                       ; report discrepancy
  435.                 movb    #-1,icmp_state                  ; set .CMP status
  436.  
  437. ;--------  we finish with icmp_state set properly  --------;
  438.  
  439. 70$:            bsbw    blank_line
  440.  
  441.  
  442. ;--------------------------------------------------------------------------------
  443. ;       If we have a proper input comparison file, we read it in.
  444. ;--------------------------------------------------------------------------------
  445. read_cmp:       tstb    icmp_state                      ; check status
  446.                 bgtr    10$                             ; branch if ok
  447.                 brw     tree_init                       ; else ignore comparison file
  448.  
  449. 10$:            addl2   #cmp_size,icmp_rab+rab$l_ubf    ; set address
  450.                 $get    rab = icmp_rab                  ; read first part
  451.                 blbs    r0,10$                          ; loop back if ok
  452.                 cmpl    r0,#RMS$_EOF                    ; exit loop if eof
  453.                 beql    relink                          ;
  454.                 moval   icmp_rab,r2                     ; else some other error
  455.                 brw     record_error                    ;
  456.  
  457.  
  458. ;--------------------------------------------------------------------------------
  459. ;       Relink comparison tree
  460. ;
  461. ;       The next_sibling and first_child links for each node in the comparison
  462. ;       tree contain the values that they had when the tree was built.  These
  463. ;       values are addresses within "tree" itself and are relative to the DATA
  464. ;       psect.  On being read back in, they need to be adjusted to point into
  465. ;       "cmp_tree".  Although it is possible to make the links relative to the
  466. ;       base of the tree itself, the extra code that would be needed in every
  467. ;       reference to a link would have made the code for tree building and tree
  468. ;       comparison very awkward.  Thus, by not choosing that method and doing
  469. ;       this relinking step instead, the process of tree building and comparison
  470. ;       is straightforward.
  471. ;
  472. ;       This relinking step does not guarantee comparison files to be compatible
  473. ;       across different versions of this program, however (even if the relative
  474. ;       distance between the two trees remains the same).
  475. ;
  476. ;       We traverse cmp_tree and adjust all the links by the value of
  477. ;       cmp_tree - tree.  Note also that a tree is guaranteed to have at least
  478. ;       one node.
  479. ;--------------------------------------------------------------------------------
  480. relink:         pushl   #0                              ; indicates when to stop
  481.                 movab   cmp_tree,r1                     ; used to traverse tree
  482.                 subl3   #tree,r1,r2                     ; adjustment value
  483. 10$:            movl    first_child(r1),r0              ; child exists?
  484.                 beql    20$                             ; branch if not
  485.                 pushl   r1                              ; else save our place
  486.                 addl2   r2,r0                           ; adjust child pointer
  487.                 movl    r0,first_child(r1)              ;
  488.                 movl    r0,r1                           ; descend to child
  489.                 brb     10$                             ; continue
  490.  
  491. 20$:            movl    next_sibling(r1),r0             ; sibling exists?
  492.                 beql    30$                             ; branch if not
  493.                 addl    r2,r0                           ; adjust sibling pointer
  494.                 movl    r0,next_sibling(r1)             ;
  495.                 movl    r0,r1                           ; go to sibling
  496.                 brb     10$                             ; test for child
  497.  
  498. 30$:            movl    (sp)+,r1                        ; restore our place
  499.                 bneq    20$                             ; continue if not done
  500.  
  501. ;       At this point, the comprison file has been read in and relinked
  502.  
  503.  
  504. ;--------------------------------------------------------------------------------
  505. ;
  506. ;       Read USAGE.DAT and accumulate usage information
  507. ;
  508. ;       Here we read the FILE records and build a tree representing the logical
  509. ;       structure of directories on the disk.  For every file on the disk there
  510. ;       is a FILE record in the USAGE.DAT file.  The FILE record contains, among
  511. ;       other things, the complete file specification and the number of blocks
  512. ;       used and allocated.  As part of the tree building process, the usage
  513. ;       information for a file is summed into that file's parent directory and
  514. ;       all subsequent parent directories, including totals for the entire disk.
  515. ;
  516. ;       Here is a simple overview of the algorithm; specific details and data
  517. ;       structures are given in the appropriate part of the code.
  518. ;
  519. ;       T1 [initialization]  Set totals to 0.  Create first node of tree.
  520. ;
  521. ;       T2 [new file]  Read a record from USAGE.DAT.  If end-of-file, then the
  522. ;               algorithm terminates.  Otherwise add usage data into totals,
  523. ;               set a pointer (p) to the first node, and set current branch name
  524. ;               (bn) to the first branch name in the directory specification.
  525. ;
  526. ;       T3 [scan for branch] Starting from p and following the next_sibling chain,
  527. ;               scan all nodes for a match with bn.  If a match is found, set p and
  528. ;               go to step T5.  If a match is not found (either because end of chain
  529. ;               or bn is lexically less than the name in the next node), continue
  530. ;               on with step T4.
  531. ;
  532. ;       T4 [new sibling]  Create a new node, add it to the next_sibling chain, and
  533. ;               set p to it.
  534. ;
  535. ;       T5 [accumulate]  The usage information in the record is added into node p.
  536. ;
  537. ;       T6 [follow branch name]  If there is another branch name in the directory
  538. ;               specification, then set bn to that and go on to the next step.
  539. ;               Otherwise we are done with this file and go back to step T2.
  540. ;
  541. ;       T7 [descend to child node]  If node p has a child node, then set p to
  542. ;               that and go to step T3.  Otherwise, create a child node and
  543. ;               set p to that.  Go on to step T5.
  544. ;       
  545. ;       Register usage:
  546. ;
  547. ;               r6  = pointer to data_buff (the current FILE record)
  548. ;               r7  = pointer to found node in the tree
  549. ;               r8  = pointer to first character in a branch name
  550. ;               r9  = length of branch name
  551. ;               r10 = count of available nodes in the tree
  552. ;               r11 = pointer to next available node
  553. ;--------------------------------------------------------------------------------
  554.  
  555.  
  556. ;--------------------------------------------------------------------------------
  557. ;       Initialization: set totals to zero, create first node of tree
  558. ;--------------------------------------------------------------------------------
  559. tree_init:      moval   tree_total_files,r0             ; clear totals:
  560.                 clrl    (r0)+                           ; total files
  561.                 clrl    (r0)+                           ; total blocks used
  562.                 clrl    (r0)                            ; total blocks allocated
  563.  
  564.                 movl    #max_nodes,r10                  ; ready to create first node
  565.                 bgtr    10$                             ; should have room for it
  566.                 brw     no_space                        ; else error
  567.  
  568. 10$:            movab   tree,r11                        ; r11 points to node in tree
  569.                 clrb    branch_name(r11)                ; branch name is null: []
  570.                 clrb    name_length(r11)                ; length is 0
  571.                 clrl    file_count(r11)                 ; no files
  572.                 clrl    blocks_used(r11)                ; no blocks used
  573.                 clrl    blocks_allocated(r11)           ; no blocks allocated
  574.                 clrl    next_sibling(r11)               ; no sibling nodes
  575.                 clrl    first_child(r11)                ; no child nodes
  576.                 addl2   #node_size,r11                  ; update ptr to next avail node
  577.                 decl    r10                             ; decrement node count
  578.  
  579. ;--------------------------------------------------------------------------------
  580. ;       New file: read a record from USAGE.DAT, add usage data into totals,
  581. ;       look at first branch name in the directory specification
  582. ;--------------------------------------------------------------------------------
  583. new_file:       $get    rab = data_rab                  ; read FILE record
  584.                 blbs    r0,10$                          ; branch if ok
  585.                 cmpl    r0,#RMS$_EOF                    ; test and branch if eof
  586.                 beql    5$                              ; 
  587.                 moval   data_rab,r2                     ;
  588.                 brw     record_error                    ; else error
  589.  
  590. 5$:             brw     print_tree                      ; on eof, print the tree
  591.  
  592. 10$:            cmpb    #USG$K_FILE,USG$B_TYPE(r6)      ; record type ok?
  593.                 beql    20$                             ; branch if ok
  594.                 movab   not_file_msg,r0                 ;
  595.                 bsbw    write_msg                       ;
  596.                 brw     done                            ;
  597.  
  598. 20$:            moval   tree_total_files,r0             ; ready to update totals
  599.                 incl    (r0)+                           ; total files
  600.                 addl2   USG$L_USED(r6),(r0)+            ; total used
  601.                 addl2   USG$L_ALLOCATED(r6),(r0)        ; total allocated
  602.  
  603.                 movab   tree,r7                         ; ptr to first node in tree
  604.  
  605. ;--------------------------------------------------------------------------------
  606. ;       Setup for first branch name: for a branch name such as "[abc.de.fghij]",
  607. ;       we set r8 to point to "a" and set r9 = 3.  We also decrement the count
  608. ;       in USG$W_DIR_LEN in order to simplify things later on.
  609. ;--------------------------------------------------------------------------------
  610.                 movab   USG$T_FILESPEC+1(r6),r8         ; ptr to branch name
  611.                 cvtwl   USG$W_DIR_LEN(r6),r9            ; full directory spec length
  612.                 subl2   #2,r9                           ; discount "[" and "]"
  613.                 subw2   #2,USG$W_DIR_LEN(r6)            ;
  614.                 beql    40$                             ; branch if null name
  615.                 locc    #^a\.\,r9,(r8)                  ; scan for "."
  616.                 beql    40$                             ; branch if not found
  617.                 subl2   r0,r9                           ; set name length
  618.                 decw    USG$W_DIR_LEN(r6)               ; remove "." from length
  619. 40$:                                                    ; r9 now set properly
  620.                 subw2   r9,USG$W_DIR_LEN(r6)            ; set remaining length
  621.  
  622. ;--------------------------------------------------------------------------------
  623. ;       Scan: scan the nodes in the tree looking for a match with the current
  624. ;       branch name.  Here we have:
  625. ;
  626. ;                       r7 = node p (name) pointer
  627. ;                       r1 = node p name length
  628. ;                       r8 = current branch name pointer
  629. ;                       r9 = current branch name length
  630. ;
  631. ;       Note: cmpc5 uses r0..r3, the shorter name is zero-filled
  632. ;--------------------------------------------------------------------------------
  633. scan:           cvtbw   name_length(r7),r1              ; cmpc needs word operand
  634.                 cmpc5   r9,(r8),#0,r1,(r7)              ; compare current to node p
  635.                 beql    accumulate                      ; branch if match
  636.                 blss    new_sibling                     ; branch if went too far
  637.  
  638.                                                         ; no match, advance to next node
  639.                 movl    r7,r5                           ; save node pointer
  640.                 movl    next_sibling(r7),r7             ; advance along node chain
  641.                 bneq    scan                            ; branch to test next node
  642.  
  643. ;----------------------------------------------------------------------------------
  644. ;       New sibling: we arrive here either if the current branch name is lexically
  645. ;       less than the branch name at node p (r7) or if we're at the end of the
  646. ;       node chain.  In either case, r5 points to the node to which we will link
  647. ;       a new one.  Note that since the initial node has a null name, this also
  648. ;       guarantees at least one pass through the above loop and that r5 will get
  649. ;       a chance to be set to a valid value.
  650. ;
  651. ;       We fill in the fields of the new node according to the FILE record's
  652. ;       fields.  Since filling in the node information encompasses the "accumulate"
  653. ;       step, we can really skip that part.
  654. ;----------------------------------------------------------------------------------
  655. new_sibling:    decl    r10                             ; decrement node count
  656.                 bgeq    10$                             ; branch if we have room
  657.                 brw     no_space                        ; else count had been zero
  658.  
  659. 10$:            movl    #1,file_count(r11)              ; first file in new directory
  660.                 movl    USG$L_USED(r6),blocks_used(r11) ; set blocks used/allocated
  661.                 movl    USG$L_ALLOCATED(r6),blocks_allocated(r11)
  662.                 movl    next_sibling(r5),next_sibling(r11)      ; copy from prev node
  663.                 movl    r11,next_sibling(r5)            ; prev node now points here
  664.                 clrl    first_child(r11)                ; no child nodes yet
  665.                 movb    r9,name_length(r11)             ; name length fits in a byte
  666.                 movc3   r9,(r8),branch_name(r11)        ; movc3 uses r0..r5
  667.                 movl    r11,r7                          ; set node pointer
  668.                 addl2   #node_size,r11                  ; point to next available node
  669.                 brb     follow                          ; we've done "accumulate"
  670.  
  671. ;----------------------------------------------------------------------------------
  672. ;       Accumulate:  usage information for the current FILE record is added into
  673. ;       node p
  674. ;----------------------------------------------------------------------------------
  675. accumulate:     incl    file_count(r7)                  ; another file in this dir
  676.                 addl2   USG$L_USED(r6),blocks_used(r7)  ; set blocks used/allocated
  677.                 addl2   USG$L_ALLOCATED(r6),blocks_allocated(r7)
  678.  
  679. ;----------------------------------------------------------------------------------
  680. ;       Follow:  if there's another branch name in the directory specification,
  681. ;       we set the current branch name to that in preparation for creating a
  682. ;       child node.  Otherwise, we are done with this file.
  683. ;----------------------------------------------------------------------------------
  684. follow:         addl2   r9,r8                           ; possible next branch name
  685.                 incl    r8                              ;
  686.                 cvtwl   USG$W_DIR_LEN(r6),r9            ; get remaining length
  687.                 bneq    10$                             ; branch if more to do
  688.                 brw     new_file                        ; else done with this file
  689.  
  690. 10$:            locc    #^a\.\,r9,(r8)                  ; scan for "."
  691.                 beql    40$                             ; branch if not found
  692.                 subl2   r0,r9                           ; set name length
  693.                 decw    USG$W_DIR_LEN(r6)               ; remove "." from length
  694. 40$:                                                    ; r9 now set properly
  695.                 subw2   r9,USG$W_DIR_LEN(r6)            ; set remaining length
  696.  
  697. ;----------------------------------------------------------------------------------
  698. ;       Descend:  on following the new branch name, if node p has a child node
  699. ;       we set p to that and go back to scan the child nodes.  Otherwise we
  700. ;       create a child node.  The code here is very similar to that in creating
  701. ;       a new sibling.
  702. ;----------------------------------------------------------------------------------
  703. descend:        movl    r7,r0                           ; point to the parent
  704.                 movl    first_child(r7),r7              ; child node?
  705.                 beql    10$                             ; branch if not
  706.                 brw     scan                            ; else scan child nodes
  707.  
  708. 10$:            decl    r10                             ; decrement node count
  709.                 bgeq    20$                             ; branch if we have room
  710.                 brw     no_space                        ; else count had been zero
  711.  
  712. 20$:            movl    r11,first_child(r0)             ; parent points to new child
  713.                 movc3   r9,(r8),branch_name(r11)        ; set name of child node
  714.                 movb    r9,name_length(r11)             ; name length fits in a byte
  715.                 movl    #1,file_count(r11)              ; first file in new directory
  716.                 movl    USG$L_USED(r6),blocks_used(r11) ; set blocks used/allocated
  717.                 movl    USG$L_ALLOCATED(r6),blocks_allocated(r11)
  718.                 clrl    next_sibling(r11)               ; no siblings yet
  719.                 clrl    first_child(r11)                ; no child nodes yet
  720.                 movl    r11,r7                          ; set node pointer
  721.                 addl2   #node_size,r11                  ; point to next available node
  722.                 brb     follow                          ; we've done "accumulate"
  723.  
  724.  
  725. ;--------------------------------------------------------------------------------
  726. ;       Compare and print the tree, with indentation to reflect the hierarchy
  727. ;
  728. ;       If we have an input comparison file, then we traverse both trees
  729. ;       simultaneoulsy and print information on which directories have
  730. ;       changed and by how much.
  731. ;
  732. ;       Here is a simplified overview of the algorithm used to compare the two
  733. ;       trees:
  734. ;
  735. ;       Algorithm C [compare trees]  We have two trees: the current tree and the
  736. ;       comparison tree.  Each node in a tree represents either a directory or
  737. ;       subdirectory.  At any given level, all the nodes in that level are linked
  738. ;       in lexical order.  We let p be a pointer to a node in the current tree and
  739. ;       let q be the corresponding pointer in the comparison tree.  The approach
  740. ;       used is to let p walk through the nodes of p while attempting to track
  741. ;       this movement with q in cmp_tree.  In addition, we have two variables,
  742. ;       p_level and q_level, and two stacks, p_stack and q_stack, which are used
  743. ;       in keeping track of our position.
  744. ;
  745. ;        C1 [initialization]  Push a 0 onto p_stack; when this value gets popped
  746. ;               off, the algorithm will terminate.  Set both p_level and q_level
  747. ;               to 1, set p to the first node in tree, and q to the first node
  748. ;               in cmp_tree.
  749. ;
  750. ;        C2 [clear comparison string]  Set cmp_string to to blanks.  This string
  751. ;               provides the information in the "change" column.  If a directory in
  752. ;               tree has no counterpart in cmp_tree, this string remains blank.
  753. ;
  754. ;        C3 [compare levels]  If q_level is greater than p_level, then pop q_stack
  755. ;               to get new values for q and q_level and repeat this step.
  756. ;               Otherwise, if q_level = p_level then go on to step C4, or if
  757. ;               q_level is less than p_level, go on to step C6.
  758. ;
  759. ;        C4 [compare nodes]  If node q does not exist (i.e. has the value 0),
  760. ;               then go on to step C6.  Otherwise compare name(q) with name(p).
  761. ;               If name(q) is less than name(p), set q to its next sibling and
  762. ;               repeat this step.  If name(q) = name(p) continue with step C5.
  763. ;               If name(q) is greater than name(p), go on to step C6.
  764. ;
  765. ;        C5 [set difference]  Set cmp_string to the difference in blocks_allocated(p)
  766. ;               and blocks_allocated(q).
  767. ;
  768. ;        C6 [output]  Output disk usage information for node p, incorporating the
  769. ;               information in cmp_string.
  770. ;
  771. ;        C7 [next directory]  If node p has any child nodes, go on to step C9.
  772. ;               Otherwise, set p to its next sibling and continue with step C8.
  773. ;
  774. ;        C8 [check continuation]  If node p exists, then continue with step C2.
  775. ;               Otherwise, pop p_stack to get new values for p_level and p.
  776. ;               If p is 0, then the algorithm terminates.  Otherwise, go on
  777. ;               with step C2.
  778. ;
  779. ;        C9 [set p continuation]  If node p has a next sibling, push a pointer
  780. ;               to it onto p_stack along with the current value of p_level.
  781. ;
  782. ;       C10 [descend in tree]  Increment p_level and set p to its child node.
  783. ;
  784. ;       C11 [check q correspondence]  If q_level is equal to what p_level was
  785. ;               just recently (e.g. p_level - 1) and node q has a child node,
  786. ;               then go on to the next step, otherwise go to step C2.
  787. ;
  788. ;       C12 [set q continuation]  If node q has a next sibling, push a pointer
  789. ;               to it onto q_stack along with the current value of q_level.
  790. ;
  791. ;       C13 [descend in cmp_tree]  Increment q_level, set q to its child node,
  792. ;               and go to step C2.
  793. ;
  794. ;       Register usage:
  795. ;
  796. ;           r0..r3   =  scratch, also used by cmpc instruction
  797. ;               r4   =  copy of icmp_state
  798. ;               r5   =  indentation amount
  799. ;               r6   =  p_level
  800. ;               r7   =  p
  801. ;               r8   =  q_level
  802. ;               r9   =  q
  803. ;               r10  =  q_stack pointer
  804. ;               r11  =  pointer to next available node (we need this for later)
  805. ;
  806. ;       NOTE: if no input comparison file is being used, we avoid doing anything
  807. ;       with the comparison tree.
  808. ;--------------------------------------------------------------------------------
  809.  
  810. print_tree:
  811.                 movab   heading_msg,r0                  ; write heading & blank line
  812.                 bsbw    write_msg                       ;
  813.                 bsbw    blank_line                      ;
  814.  
  815. ;--------------------------------------------------------------------------------
  816. ;       Initialization:  initialize stacks, levels, and node pointers.  The
  817. ;       current indentation amount is also saved on the stack.
  818. ;--------------------------------------------------------------------------------
  819.                 pushl   #0                              ; mark p_stack top
  820.                 cvtbl   #1,r6                           ; set p_level to 1
  821.                 pushl   r6                              ; save on p_stack
  822.                 movab   tree,r7                         ; set p to walk through tree
  823.                 clrl    r5                              ; indent amount currently 0
  824.                 pushl   r5                              ; save indent amount
  825.                 cvtbl   icmp_state,r4                   ; use comparison tree?
  826.                 bleq    clear                           ; branch if not
  827.                 movab   q_stack_top,r10                 ; initialize q_stack pointer
  828.                 movl    r6,r8                           ; set q_level to 1
  829.                 movab   cmp_tree,r9                     ; set q to walk through cmp_tree
  830.  
  831. ;--------------------------------------------------------------------------------
  832. ;       The "change" column is blank if there's no comparison file, but when
  833. ;       there is one, we want the "change" column to indicate those directories
  834. ;       that are new and have no equivalent in cmp_tree.
  835. ;--------------------------------------------------------------------------------
  836.  
  837.                 movl    new_string,blank_string+4
  838.  
  839. ;--------------------------------------------------------------------------------
  840. ;       Clear:  clear comparison string.  At this point we can also skip a lot
  841. ;       of comparison code if there is no comparison tree.
  842. ;--------------------------------------------------------------------------------
  843. clear:          movq    blank_string,cmp_string         ; set comparison string to blanks
  844.                 tstl    r4                              ; use comparison tree?
  845.                 bgtr    cmp_levels                      ; branch if yes
  846.                 brw     output                          ; else skip comparisons
  847.  
  848. ;--------------------------------------------------------------------------------
  849. ;       Compare levels:  if we're further down in cmp_tree than in tree, we try
  850. ;       to come back to the same level in cmp_tree.  If we're at the same
  851. ;       level, we go on to compare nodes.  If we're further up in cmp_tree
  852. ;       (meaning there's no branch in cmp_tree that corresponds to where we are
  853. ;       in tree), then we skip the comparisons.
  854. ;--------------------------------------------------------------------------------
  855. cmp_levels:     cmpl    r8,r6                           ; compare q_level to p_level
  856.                 bgtr    10$                             ;
  857.                 beql    cmp_nodes                       ;
  858.                 brw     output                          ;
  859.  
  860. 10$:            movl    (r10)+,r8                       ; pop q_level from q_stack
  861.                 movl    (r10)+,r9                       ; pop q from q_stack
  862.                 brb     cmp_levels                      ; compare again
  863.  
  864. ;--------------------------------------------------------------------------------
  865. ;       Compare nodes:  if node q does not exist (e.g. we've reached the end
  866. ;       of its sibling chain at the current q_level), then we can skip this
  867. ;       comparison.  Otherwise we scan forward in q until we either find a
  868. ;       name that matches that in p, or until we've gone too far.
  869. ;--------------------------------------------------------------------------------
  870. cmp_nodes:      tstl    r9                              ; does node q exist?
  871.                 bneq    10$                             ; branch if yes
  872.                 brw     output                          ;
  873.  
  874. 10$:            cvtbw   name_length(r9),r0              ; get word operands
  875.                 cvtbw   name_length(r7),r2              ;
  876.                 cmpc5   r0,(r9),#0,r2,(r7)              ; compare names
  877.                 beql    set_diff                        ;
  878.                 blss    20$                             ;
  879.                 brw     output                          ;
  880.  
  881. 20$:            movl    next_sibling(r9),r9             ; advance q to next sibling
  882.                 brb     cmp_nodes                       ; compare again
  883.  
  884. ;--------------------------------------------------------------------------------
  885. ;       Set difference:  set cmp_string to reflect the difference in blocks
  886. ;       allocated between p and q.
  887. ;--------------------------------------------------------------------------------
  888. set_diff:       subl3   blocks_allocated(r9),-          ; r0 = allocation difference
  889.                         blocks_allocated(r7),-          ;
  890.                         r0                              ;
  891.  
  892.                 $fao_s  -                               ; set new cmp_string
  893.                         ctrstr  =       cmp_fmt,-       ; format control string
  894.                         outlen  =       fao_outlen,-    ; where to store length
  895.                         outbuf  =       cmp_outbuf,-    ; descriptor for cmp_string
  896.                         p1      =       r0              ; value to convert = difference
  897.                 blbs    r0,output                       ; branch if converted ok
  898.                 movab   cmp_cvt_msg,r0                  ; else write error message
  899.                 bsbw    write_msg                       ;
  900.                 brw     done                            ;
  901.  
  902. ;--------------------------------------------------------------------------------
  903. ;       Output:  here we write a line of information for the file described
  904. ;       by node p.   At this time cmp_string has been set to show the allocation
  905. ;       difference.  If a comparison file is not being used, or if there is no
  906. ;       file corresponding to that at node p, cmp_string is blank.
  907. ;--------------------------------------------------------------------------------
  908.  
  909. output:         cvtbl   name_length(r7),r0              ; need longword arg for FA0
  910.                 $fao_s  -
  911.                         ctrstr  =       tree_fmt,-      ; descriptor for format control string
  912.                         outlen  =       fao_outlen,-    ; where to store length of formatted output
  913.                         outbuf  =       fao_outbuf,-    ; descriptor for formatted output buffer
  914.                         p1      =       r5,-
  915.                         p2      =       #indent_string,-
  916.                         p3      =       r0,-
  917.                         p4      =       r7,-
  918.                         p5      =       file_count(r7),-
  919.                         p6      =       blocks_used(r7),-
  920.                         p7      =       blocks_allocated(r7),-
  921.                         p8      =       #cmp_field_size,-
  922.                         p9      =       #cmp_string
  923.  
  924.                 blbs    r0,10$                          ; branch if converted ok
  925.                 movab   tree_cvt_msg,r0                 ; else set up to write error message
  926.                 bsbw    write_msg                       ;
  927.                 brw     done                            ;
  928.  
  929. 10$:            cvtwb   fao_outlen,lis_outlen           ; set length of formatted output
  930.                 movab   lis_outlen,r0                   ; .ASCIC format
  931.                 bsbw    write_msg                       ; write it
  932.  
  933. ;--------------------------------------------------------------------------------
  934. ;       Next directory:  if node p has any child nodes, then follow that
  935. ;       path.  Otherwise, follow the next_sibling chain.
  936. ;--------------------------------------------------------------------------------
  937. next_dir:       movl    first_child(r7),r0              ; any child nodes?
  938.                 bneq    set_cont                        ; branch if yes
  939.                 movl    next_sibling(r7),r7             ; else go to next sibling
  940.                 beql    10$                             ; branch if none
  941.                 brw     clear                           ; else continue on
  942.  
  943. 10$:            movl    (sp)+,r5                        ; pop indent amount
  944.                 movl    (sp)+,r6                        ; pop p_level
  945.                 movl    (sp)+,r7                        ; pop p
  946.                 beql    20$                             ; branch if no more
  947.                 bsbw    blank_line                      ; blank line when coming up
  948.                 brw     clear                           ; continue on
  949.  
  950. 20$:            brw     write_totals                    ; we're done here
  951.  
  952. ;--------------------------------------------------------------------------------
  953. ;       Set continuation and descend:  if node p has a next sibling, push a
  954. ;       pointer to it on the stack and the current value of p_level.   We then
  955. ;       increment p_level and descend to the child node (a subdirectory).
  956. ;
  957. ;       We arrive here with r0 pointing to the child of node p.
  958. ;--------------------------------------------------------------------------------
  959.  
  960. set_cont:       movl    next_sibling(r7),r1             ; look for next sibling
  961.                 beql    10$                             ; branch if none
  962.                 pushl   r1                              ; save pointer to it
  963.                 pushl   r6                              ; save p_level
  964.                 pushl   r5                              ; save indent amount
  965. 10$:            addl2   #indent_value,r5                ; indent branch name on listing
  966.                 movl    r6,r1                           ; save for reference below
  967.                 incl    r6                              ; indicate one more level down
  968.                 movl    r0,r7                           ; descend to child node
  969.  
  970. ;--------------------------------------------------------------------------------
  971. ;       If node q has a corresponding child node, we check and set its
  972. ;       continuation point and then descend in cmp_tree also.
  973. ;--------------------------------------------------------------------------------
  974.  
  975.                 tstl    r4                              ; use comparison tree?
  976.                 bgtr    20$                             ; branch if yes
  977.                 brw     clear                           ; else ignore it
  978.  
  979. 20$:            cmpl    r8,r1                           ; q_level = p_level?
  980.                 beql    30$                             ; branch if yes
  981.                 brw     clear                           ; else ignore this level
  982.  
  983. 30$:            movl    first_child(r9),r0              ; corresponding child?
  984.                 bneq    40$                             ; branch if yes
  985.                 brw     clear                           ; else ignore
  986.  
  987. 40$:            movl    next_sibling(r9),r1             ; look for next sibling
  988.                 beql    60$                             ; branch if none
  989.                 cmpl    r10,#q_stack                    ; room in q_stack?
  990.                 bgtru   50$                             ; branch if yes
  991.                 movab   q_stack_msg,r0                  ; say out of room
  992.                 bsbw    write_msg                       ;
  993.                 brw     done                            ;
  994.  
  995. 50$:            movl    r1,-(r10)                       ; push next_sibling onto q_stack
  996.                 movl    r8,-(r10)                       ; push q_level onto q_stack
  997. 60$:            incl    r8                              ; down one level in cmp_tree
  998.                 movl    r0,r9                           ; point to child
  999.                 brw     clear                           ; continue on
  1000.  
  1001.  
  1002. ;--------------------------------------------------------------------------------
  1003. ;       Write out totals
  1004. ;--------------------------------------------------------------------------------
  1005. write_totals:   bsbw    blank_line                      ; write blank line
  1006.                 clrl    r1                              ; zero total difference so far
  1007.                 tstl    r4                              ; branch if no comparison file
  1008.                 bleq    10$                             ;
  1009.                 subl3   cmp_tree_total_allocated,-      ; get overall difference
  1010.                         tree_total_allocated,-
  1011.                         r1
  1012. 10$:            $fao_s  -
  1013.                         ctrstr  =       total_fmt,-     ; descriptor for format control string
  1014.                         outlen  =       fao_outlen,-    ; where to store length of formatted output
  1015.                         outbuf  =       fao_outbuf,-    ; descriptor for formatted output buffer
  1016.                         p1      =       tree_total_files,-
  1017.                         p2      =       tree_total_used,-
  1018.                         p3      =       tree_total_allocated,-
  1019.                         p4      =       r1
  1020.                 blbs    r0,20$                          ; branch if converted ok
  1021.                 movab   total_cvt_msg,r0                ; else set up to write error message
  1022.                 bsbw    write_msg                       ;
  1023.                 brw     done                            ;
  1024.  
  1025. 20$:            cvtwb   fao_outlen,lis_outlen           ; set length of formatted output
  1026.                 movab   lis_outlen,r0                   ; .ASCIC format
  1027.                 bsbw    write_msg                       ; write it
  1028.  
  1029.  
  1030. ;--------------------------------------------------------------------------------
  1031. ;       Write out the USAGE.CMP file.  Since r11 points to the next available
  1032. ;       node, it also serves to indicate when to stop writing
  1033. ;--------------------------------------------------------------------------------
  1034. write_cmp:
  1035.                 movw    #cmp_size,ocmp_rab+rab$w_rsz    ; set record size
  1036.                 movab   tree_ident,r2                   ; where to begin output
  1037. 10$:            cmpl    r2,r11                          ; branch out if done
  1038.                 blssu   15$                             ;
  1039.                 brw     done                            ;
  1040.  
  1041. 15$:            movl    r2,ocmp_rab+rab$l_rbf           ; set output address
  1042.                 $put    rab = ocmp_rab                  ; write to .CMP file
  1043.                 blbs    r0,20$                          ; branch if ok
  1044.                 moval   ocmp_rab,r2                     ;
  1045.                 brw     record_error                    ; else abort out
  1046.  
  1047. 20$:            addl2   #cmp_size,r2                    ; advance pointer
  1048.                 brb     10$                             ;
  1049.  
  1050. ;--------------------------------------------------------------------------------
  1051. ;       Arrive here if we're unable to allocate nodes in the tree
  1052. ;--------------------------------------------------------------------------------
  1053. no_space:       movab   no_space_msg,r0
  1054.                 bsbw    write_msg
  1055.                 brw     done
  1056.  
  1057.  
  1058. ;--------------------------------------------------------------------------------
  1059. ;       write_msg
  1060. ;
  1061. ;       A routine to write a message to the USAGE.LIS file.
  1062. ;       Call with:      R0 = address of an .ASCIC string
  1063. ;                       Exits on error.
  1064. ;--------------------------------------------------------------------------------
  1065. write_msg:      cvtbw   (r0)+,lis_rab+rab$w_rsz         ; set message size
  1066.                 movl    r0,lis_rab+rab$l_rbf            ; set message address
  1067.                 $put    rab = lis_rab                   ; write to list file
  1068.                 blbs    r0,10$                          ; branch if ok
  1069.                 moval   lis_rab,r2                      ;
  1070.                 brw     record_error                    ; else abort out
  1071.  
  1072. 10$:            rsb                                     ; else return
  1073.  
  1074.  
  1075. ;--------------------------------------------------------------------------------
  1076. ;       blank_line
  1077. ;
  1078. ;       Prints a blank line
  1079. ;
  1080. ;       (we could imbed CR, LF's inside records, but viewing the USAGE.LIS file
  1081. ;       in some editors won't handle this properly).
  1082. ;--------------------------------------------------------------------------------
  1083. blank_line:     movab   blank_msg,r0
  1084.                 bsbw    write_msg
  1085.                 rsb
  1086.  
  1087.  
  1088. ;--------------------------------------------------------------------------------
  1089. ;       Exit and error handling
  1090. ;
  1091. ;       In this section we close any open files and then exit.
  1092. ;       The status code associated with the error is in r0.
  1093. ;
  1094. ;--------------------------------------------------------------------------------
  1095.  
  1096. record_error:   pushl   r0                              ; save status
  1097.                 bsbb    close_files                     ;
  1098.                 movl    (sp)+,r0                        ; set status
  1099. file_error:     ret                                     ; return w/status in r0
  1100.  
  1101. done:           bsbb    close_files                     ;
  1102.                 ret                                     ; return w/status in r0
  1103.  
  1104. close_files:    $close  fab = data_fab                  ; close USAGE.DAT
  1105.                 $close  fab = lis_fab                   ; close USAGE.LIS
  1106.                 $close  fab = ocmp_fab                  ; close USAGE.CMP (output)
  1107.                 tstb    icmp_state                      ; look at USAGE.CMP (input)
  1108.                 beql    10$                             ; branch if not open
  1109.                 $close  fab = icmp_fab                  ; close USAGE.CMP (input)
  1110. 10$:            rsb                                     ;
  1111.  
  1112.  
  1113.                 .psect  DATA,wrt,noexe,LONG
  1114. ;--------------------------------------------------------------------------------
  1115. ;       Access blocks and buffer for USAGE.DAT
  1116. ;
  1117. ;       At the time of the $open, any filename components provided by the
  1118. ;       user will be reflected in the fna and fns fields of the fab.  Any
  1119. ;       missing components are provided by the dnm (default name) field of
  1120. ;       the fab.  As a result of the $open, the resulting file specification
  1121. ;       (based on any user input and defaults) is parsed and placed in the
  1122. ;       nam block.  The file specification components in the nam block are
  1123. ;       then used as defaults on subsequent $open or $create calls.
  1124. ;--------------------------------------------------------------------------------
  1125.  
  1126.                 .align  long
  1127. data_fab:       $fab    fna = fname_buff,-              ; possibly set by user
  1128.                         dnm = <sys$manager:usage.dat>,- ; provide default components
  1129.                         nam = data_nam,-                ; point to NAM block
  1130.                         fac = <get>,-                   ; GET access only
  1131.                         shr = <nil>,-                   ; exclusive access
  1132.                         fop = sqo                       ; sequential only
  1133.  
  1134.                 .align  long
  1135. data_nam:       $nam    rsa = data_rsa,-                ; NAM used for related filespecs
  1136.                         rss = nam$c_maxrss              ; (maximum resultant string size)
  1137. data_rsa:       .blkb   nam$c_maxrss                    ; buffer for filespec components
  1138.  
  1139.                 .align  long
  1140. data_rab:       $rab    fab = data_fab,-                ; point to FAB
  1141.                         rop = rah,-                     ; read-ahead
  1142.                         ubf = data_buff,-               ; addr of usage data buffer
  1143.                         usz = data_size                 ; data buffer size
  1144.  
  1145. data_buff:      .blkb   USG$C_FILE_LEN                  ; usage data buffer
  1146. data_size       =       . - data_buff
  1147.  
  1148.  
  1149. ;--------------------------------------------------------------------------------
  1150. ;               Access blocks for USAGE.CMP (optional input file)
  1151. ;--------------------------------------------------------------------------------
  1152.  
  1153.                 .align  long
  1154. icmp_fab:       $fab    fnm = <.cmp>,-                  ; primary name
  1155.                         nam = icmp_nam,-                ; point to NAM
  1156.                         fac = <get>,-                   ; GET access only
  1157.                         shr = <nil>,-                   ; exclusive access
  1158.                         fop = <nam,sqo>,-               ; NAM input, sequential only
  1159.                         rfm = fix,-                     ; fixed length records
  1160.                         mrs = cmp_size                  ; record size
  1161.  
  1162.                 .align  long
  1163. icmp_nam:       $nam    rlf = data_nam                  ; provide default components
  1164.  
  1165.                 .align  long
  1166. icmp_rab:       $rab    fab = icmp_fab,-                ; point to FAB
  1167.                         rop = rah                       ; read-ahead
  1168.  
  1169.  
  1170. ;--------------------------------------------------------------------------------
  1171. ;       Access blocks and buffer for USAGE.LIS (created output file)
  1172. ;--------------------------------------------------------------------------------
  1173.  
  1174.                 .align  long
  1175. lis_fab:        $fab    fnm = <.lis>,-                  ; primary name
  1176.                         nam = lis_nam,-                 ; point to NAM
  1177.                         fac = <put>,-                   ; PUT access only
  1178.                         shr = <nil>,-                   ; exclusive access
  1179.                         fop = <nam,sqo>,-               ; NAM input, sequential only
  1180.                         mrs = lis_size,-                ; max record size
  1181.                         rat = cr                        ; implied carriage control
  1182.  
  1183.                 .align  long
  1184. lis_nam:        $nam    rlf = data_nam                  ; provide default components
  1185.  
  1186.                 .align  long
  1187. lis_rab:        $rab    fab = lis_fab,-                 ; point to FAB
  1188.                         rop = wbh                       ; write-behind
  1189.  
  1190. lis_outlen:     .blkb                                   ; length of output
  1191. lis_buff:       .blkb   lis_size                        ; listing buffer
  1192.  
  1193.  
  1194. ;--------------------------------------------------------------------------------
  1195. ;       Access blocks and buffer for USAGE.CMP (created output file)
  1196. ;--------------------------------------------------------------------------------
  1197.  
  1198.                 .align  long
  1199. ocmp_fab:       $fab    fnm = <.cmp>,-                  ; primary name
  1200.                         nam = ocmp_nam,-                ; point to NAM
  1201.                         fac = <put>,-                   ; PUT access only
  1202.                         shr = <nil>,-                   ; exclusive access
  1203.                         fop = <nam,sqo>,-               ; NAM input, sequential only
  1204.                         rfm = fix,-                     ; fixed length records
  1205.                         mrs = cmp_size                  ; record size
  1206.  
  1207.                 .align  long
  1208. ocmp_nam:       $nam    rlf = data_nam                  ; provide default components
  1209.  
  1210.                 .align  long
  1211. ocmp_rab:       $rab    fab = ocmp_fab,-                ; point to FAB
  1212.                         rop = wbh                       ; write-behind
  1213.  
  1214.  
  1215. ;--------------------------------------------------------------------------------
  1216. ;               Messages
  1217. ;--------------------------------------------------------------------------------
  1218.  
  1219. version_msg:            .ascic  \Disk Usage  v2.12\
  1220. ident_len_msg:          .ascic  \invalid record length for IDENT record\
  1221. not_ident_msg:          .ascic  \first record is not IDENT record\
  1222. time_cvt_msg:           .ascic  \unable to convert creation time\
  1223. serial_cvt_msg:         .ascic  \unable to convert serial number\
  1224. strucname_cvt_msg:      .ascic  \unable to convert volume set name\
  1225. volname_cvt_msg:        .ascic  \unable to convert volume name\
  1226. ownername_cvt_msg:      .ascic  \unable to convert owner name\
  1227. format_cvt_msg:         .ascic  \unable to convert format description\
  1228. not_file_msg:           .ascic  \record is not a FILE record\
  1229. no_icmp_msg:            .ascic  \No input comparison file\
  1230. wrong_type_msg:         .ascic  \input comparison file ignored: incorrect type field\
  1231. wrong_volume_msg:       .ascic  \input comparison file ignored: wrong volume\
  1232. wrong_version_msg:      .ascic  \input comparison file ignored: program version mismatch\
  1233. icmp_time_cvt_msg:      .ascic  \unable to convert comparison time\
  1234. heading_msg:            .ascic  \Directories                             File count  blk used/allocated   change\
  1235. no_space_msg:           .ascic  \out of room -- please increase max_nodes and reassemble\
  1236. blank_msg:              .ascic  \\
  1237. total_cvt_msg:          .ascic  \unable to convert totals\
  1238. cmp_cvt_msg:            .ascic  \unable to convert difference in block allocation\
  1239. q_stack_msg:            .ascic  \q_stack overflow -- please increase q_stack_size and reassemble\
  1240. tree_cvt_msg:           .ascic  \unable to convert name or size information\
  1241.  
  1242. ;--------------------------------------------------------------------------------
  1243. ;               Data structures for FA0 messages
  1244. ;--------------------------------------------------------------------------------
  1245. fao_outlen:     .blkw
  1246. fao_outbuf:     string_dsc      lis_buff,lis_size
  1247.  
  1248. indent_string:  .ascic  \\
  1249.  
  1250. time_fmt:       .ascid  \Usage file created:     !%D\
  1251. serial_fmt:     .ascid  \Serial number (octal):  !OL\
  1252. strucname_fmt:  .ascid  \Volume set name:        !AD\
  1253. volname_fmt:    .ascid  \Volume name:            !AD\
  1254. ownername_fmt:  .ascid  \Owner name:             !AD\
  1255. format_fmt:     .ascid  \File format:            !AD\
  1256. icmp_time_fmt:  .ascid  \Comparison file dated:  !%D\
  1257. total_fmt:      .ascid  \Total files  !SL,   blocks used/allocated  !SL/!SL,   change  !SL\
  1258.  
  1259. tree_fmt:       .ascid  \!40<!#AC!AD!>  !8SL  !8SL/!8<!SL!>  !8AD\
  1260.  
  1261. ;--------------------------------------------------------------------------------
  1262. ;       Data structures used in setting the allocation difference
  1263. ;--------------------------------------------------------------------------------
  1264.  
  1265. new_string:     .ascii  \ new\
  1266. blank_string:   .ascii  \        \
  1267. cmp_string:     .ascii  \        \
  1268. cmp_fmt:        .ascid  \!8SL\
  1269. cmp_outbuf:     string_dsc      cmp_string,cmp_field_size
  1270.  
  1271.  
  1272. ;--------------------------------------------------------------------------------
  1273. ;       Miscellaneous data structures
  1274. ;
  1275. ;       icmp_state reflects the state of the optional USAGE.CMP input file:
  1276. ;
  1277. ;               icmp_state < 0  ==>  file exists, but is not to be used
  1278. ;               icmp_state = 0  ==>  file does not exist
  1279. ;               icmp_state > 0  ==>  file exists and is being used
  1280. ;
  1281. ;--------------------------------------------------------------------------------
  1282.  
  1283. icmp_state:             .blkb                           ; state of USAGE.CMP (input)
  1284.  
  1285. ;       This small stack is used while traversing the comparison tree
  1286.  
  1287. q_stack:                .blkl   q_stack_size
  1288. q_stack_top:
  1289.  
  1290.  
  1291. ;       Data structures for optional name to override default (length is first word in dsc)
  1292.  
  1293. fname_buff:     .blkb           max_name_size
  1294. fname_dsc:
  1295. fname_length:   string_dsc      fname_buff,max_name_size
  1296.  
  1297. ;--------------------------------------------------------------------------------
  1298. ;               Data structures for the nodes
  1299. ;--------------------------------------------------------------------------------
  1300.  
  1301. ;       sizes of elements in the node data structure (in bytes)
  1302.  
  1303. branch_name_size        =       max_name_size   ; max size of branch name
  1304. name_length_size        =       1               ; since branch name length < 256 chars
  1305. file_count_size         =       4               ; longword count
  1306. blocks_used_size        =       4               ; longword count
  1307. blocks_allocated_size   =       4               ; longword count
  1308. next_sibling_size       =       4               ; longword pointer
  1309. first_child_size        =       4               ; longword pointer
  1310.  
  1311. ;       offsets within the node data structure
  1312.  
  1313. branch_name             =       0
  1314. name_length             =       branch_name + branch_name_size
  1315. file_count              =       name_length + name_length_size
  1316. blocks_used             =       file_count + file_count_size
  1317. blocks_allocated        =       blocks_used + blocks_used_size
  1318. next_sibling            =       blocks_allocated + blocks_allocated_size
  1319. first_child             =       next_sibling + next_sibling_size
  1320.  
  1321. node_size               =       first_child + first_child_size
  1322.  
  1323.  
  1324. ;--------------------------------------------------------------------------------
  1325. ;       Data structures for the trees.  The tree built from the USAGE.DAT file
  1326. ;       becomes the new comparison file that gets written out.  The comparison
  1327. ;       file read in (if any) constitutes the comparison tree (cmp_tree).
  1328. ;
  1329. ;       To maintain and verify the integrity of the comparison files, the
  1330. ;       IDENT record from the USAGE.DAT file which is used to build the tree
  1331. ;       is also part of the first record in the comparison file.  The contents
  1332. ;       of this first record is described in the cmp_init section.
  1333. ;
  1334. ;       The comparison file is written out in records of cmp_size bytes.  We
  1335. ;       want to make sure that storage for the tree (starting with the IDENT
  1336. ;       part) is allocated to the nearest multiple of cmp_size.  This guarantees
  1337. ;       that for whatever amount gets written out (from tree_ident) there will
  1338. ;       be room for it when being read back later (into cmp_tree_ident).
  1339. ;
  1340. ;       NOTE: various sections of code expect the totals to be grouped together
  1341. ;
  1342. ;--------------------------------------------------------------------------------
  1343. tree_ident:             .blkb   USG$C_IDENT_LEN         ; first part of usage.cmp
  1344. tree_version:           .blkw                           ; for integrity check
  1345. tree_total_files:       .blkl                           ; total files on disk
  1346. tree_total_used:        .blkl                           ; total blocks used
  1347. tree_total_allocated:   .blkl                           ; total blocks allocated
  1348. tree:                   .blkb   max_nodes * node_size   ; tree of nodes
  1349. size                    =       . - tree_ident
  1350. blocks                  =       <size + <cmp_size - 1>> / cmp_size
  1351. fill                    =       <blocks * cmp_size> - size
  1352.                         .blkb   fill
  1353.  
  1354. cmp_tree_ident:         .blkb   USG$C_IDENT_LEN         ; first part of usage.cmp
  1355. cmp_tree_version:       .blkw                           ; for integrity check
  1356. cmp_tree_total_files:   .blkl                           ; total files on disk
  1357. cmp_tree_total_used:    .blkl                           ; total blocks used
  1358. cmp_tree_total_allocated:.blkl                          ; total blocks allocated
  1359. cmp_tree:               .blkb   max_nodes * node_size   ; tree of nodes
  1360.                         .blkb   fill
  1361.  
  1362.                         .end    DISK_USAGE
  1363.