!+ ! NAME ! disk.scn ! ! DESCRIPTION ! This module contains front end/driver routines for the ! disk reporter tool. The disk reporter tool is a utility ! that scans VMS: ! ! $ directory/size=allocated disk$anydisk:[000000...]/out=any.log ! ! log files and stores disk usage information. The tool produces ! several different tabular reports including: ! ! 1) Disk Summary Report - disk totals: #blocks, %full, #files, #roots... ! ! 2) Root Directory Report - summary of all root directory totals across ! all input disks. ! ! 3) Top N Directories Report - list of all the top N space using directories. ! ! 4) Complete Directory Dump - list of all directories in all log files - ! the "total picture". ! ! 5) Merged Directory Dump - report with roots merged into one master root ! by either a lookup table and/or identically named ! roots accross different disks. ! ! ! This tool is a collection of SCAN and C routines. The SCAN routines take advantage ! of the easy toiken recognition to parse the directory log files, extract the CLI ! directions, and handle the root mapping scheme via a TREE. The C routines are used ! to store the directory data in a more efficient manner that SCAN and to use the ! formatted I/O routines. ! ! RETURN VALUE ! ! HISTORY ! 6/88 Bauer Initial version for disk_report tool. ! !- MODULE disk$reporter IDENT 'v1.0'; CONSTANT MAX_BLOCKS_PERCENT = 1; CONSTANT CUM_BLOCKS_PERCENT = 2; CONSTANT MAX_REPORTS = 70; TYPE ROOT_DATA: ! Master root map data structure RECORD disk : INTEGER, ! What Disk # index : INTEGER, ! What directory # from_table : BOOLEAN, ! Form map input table or not? END RECORD; ! ! Global Data Declarations ! DECLARE rootmap : GLOBAL TREE (STRING, STRING) OF ROOT_DATA; ! Master root map DECLARE rootused : GLOBAL TREE (STRING) OF STRING; ! What roots have been used in above map DECLARE dir_count : GLOBAL INTEGER; ! Current directory count DECLARE root_count : GLOBAL INTEGER; ! Current root count DECLARE command_line : GLOBAL STRING; ! Command line used DECLARE disk_name : GLOBAL STRING; ! Name of current disk DECLARE disk_number : GLOBAL INTEGER; ! Current disk number DECLARE percent_type : GLOBAL INTEGER; ! Percent type calculated DECLARE total_disk_space_available : GLOBAL INTEGER; ! How much space? DECLARE input_file_number : GLOBAL INTEGER; ! How many log files DECLARE input_file_tree : GLOBAL TREE (INTEGER) OF STRING; ! List of input log files DECLARE output_file : GLOBAL STRING; ! Output file name DECLARE summary_report_flag : GLOBAL BOOLEAN; ! Summary report or not? DECLARE root_report_flag : GLOBAL BOOLEAN; ! Root report or not? DECLARE top_dir_report_number : GLOBAL INTEGER; ! Top N Dir report or not? DECLARE slash_merged_flag : GLOBAL BOOLEAN; ! Merged report or not? DECLARE master_summary_report_flag : GLOBAL INTEGER; ! 0 = No Master Summary Report, ! 1 = ONLY Master Summary Report - No Master comp., ! 2 = Both Master Summary Rep & master Complete DECLARE complete_report_flag : GLOBAL BOOLEAN; ! Complete Directory report or not? DECLARE table_only : GLOBAL BOOLEAN; ! Only masters from map table DECLARE max_depth : GLOBAL INTEGER; ! Only print dirs up to N depth DECLARE over_block_size : GLOBAL INTEGER; ! Only print dirs over N blocks DECLARE depth : GLOBAL INTEGER; ! How many levels on current dir DECLARE current_dir_name : STRING; ! Current directory name ! ! Forward Procedure Declarations ! FORWARD PROCEDURE add_to_root_map; ! ! Set Declarations ! SET digit ( '0' .. '9' ); SET alpha ( 'a'.. 'z' OR 'A'..'Z'); SET white_space ( ' ' OR s'ht' ); SET id_part ( alpha OR digit OR '_' OR '$' OR ';' OR '.' OR '_'); SET id_start ( alpha OR digit OR '_' OR '$' ); ! ! TOKEN Declarations ! TOKEN files { 'files,' | 'file,' }; TOKEN total_of { 'Total of' }; TOKEN integer_token { digit [digit...] }; TOKEN id { id_start {id_part...} }; TOKEN blanks IGNORE { white_space [white_space...] }; TOKEN colon ALIAS ':' { ':' }; TOKEN LBRKT ALIAS '[' { '[' }; TOKEN RBRKT ALIAS ']' { ']' }; ! ! MACROS ! !+ ! ! This macro recognizes disk/directory names ! !- MACRO Get_dir TRIGGER { the_disk: id ':' dir_name: { '[' { id | integer_token } ']'} }; EXTERNAL PROCEDURE store_disk_name (STRING); ! Store current disk name EXTERNAL PROCEDURE compute_dir_depth (STRING) OF INTEGER; ! How many dots? dir_count = dir_count + 1; ! Bump directory counter IF dir_count = 1 THEN CALL store_disk_name (the_disk); ! Store disk name once per disk disk_name = the_disk; END IF; current_dir_name = dir_name; ! Remember this name ... depth = compute_dir_depth (dir_name); ! How deep? IF slash_merged_flag AND depth = 1 ! If merged table add to root map table THEN CALL add_to_root_map; END IF; END MACRO /* Get_dir */; !+ ! ! This macro gets file and block counts ! for the current directory and passes this ! info on to be stored. ! !- MACRO get_counts TRIGGER { total_of file_n: integer_token files blcks: integer_token }; EXTERNAL PROCEDURE store_dir_data ( VALUE INTEGER, VALUE INTEGER, STRING, VALUE INTEGER ); DECLARE blocks_cnt, files_cnt : INTEGER; files_cnt = INTEGER (file_n); ! Convert counts blocks_cnt = INTEGER (blcks); CALL store_dir_data ( files_cnt, ! And store in C data strucutres blocks_cnt, current_dir_name, depth ); END MACRO /* get_counts */; ! ! PROCEDURES ! !+ ! NAME ! add_to_root_map -- add another root dir to master map ! ! DESCRIPTION ! This routine adds a directory into the root map when the merged complete directory ! report is to be generated. New master roots are created as needed by this routine. ! ! RETURN VALUE ! ! HISTORY ! 6/88 Bauer Initial version for disk_report tool. ! !- PROCEDURE add_to_root_map; DECLARE total_name : STRING; ! Disk + ':' + directory DECLARE wildcard_name : STRING; ! Disk + ':' + '[*]' DECLARE rootmap_index : STRING; total_name = disk_name & ':' & current_dir_name; ! Get total disk/dir name wildcard_name = disk_name & ':[*]'; ! Get total disk/dir name rootmap_index = current_dir_name[2..(LENGTH(current_dir_name)-1)]; IF NOT EXISTS(rootused(total_name)) ! Is it in the root map table? THEN IF NOT EXISTS(rootused(wildcard_name)) ! Is it in the root map table? THEN rootused (total_name) = rootmap_index; ! Note - this will fail if the rootmap_index ! is one of the master roots. rootmap(rootmap_index, total_name).from_table = FALSE; ! NO rootmap(rootmap_index, total_name).disk = disk_number; rootmap(rootmap_index, total_name).index = dir_count; ELSE rootused(total_name) = rootused(wildcard_name); ! Fake out when wildcard root rootmap(rootused(total_name), total_name).from_table = TRUE; ! YES rootmap(rootused(total_name), total_name).disk = disk_number; rootmap(rootused(total_name), total_name).index = dir_count; END IF; ELSE ! YES rootmap(rootused(total_name), total_name).from_table = TRUE; rootmap(rootused(total_name), total_name).disk = disk_number; rootmap(rootused(total_name), total_name).index = dir_count; END IF; END PROCEDURE; !+ ! NAME ! compute_dir_depth -- how many dots in a directory path ! ! DESCRIPTION ! This function tells how many levels deep a directory is by counting dorts in the name. ! ! RETURN VALUE ! ! HISTORY ! 6/88 Bauer Initial version for disk_report tool. ! !- PROCEDURE compute_dir_depth (from_dir : STRING) OF INTEGER; DECLARE found_dot : INTEGER; DECLARE count : INTEGER; DECLARE temp : STRING; temp = from_dir; count = 1; found_dot = INDEX(temp, '.'); ! Search for first dot WHILE (found_dot <> 0); ! Until you can't find no more count = count + 1; temp = temp[(found_dot+1)..]; ! Shrink name found_dot = INDEX(temp, '.'); ! Find next dot END WHILE; return (count); ! I found this many END PROCEDURE; !+ ! NAME ! read_root_map_file -- reads in master root mapping file ! ! DESCRIPTION ! This routine reads in the master root map file. This file has the following format ! restrictions: ! - Exclamation point in column one causes line to be skipped (comment) ! - MASTER root name can be up to 12 chars long and is followed by list ! of root directory names ! - One MASTER root gets followed by all of its roots - one per line. A star ! line marks the end of the list and the start of a new master root. ! ! This routine does not SCAN the file but rather uses SCAN's simple file I/O calls. ! ! ! RETURN VALUE ! ! HISTORY ! 6/88 Bauer Initial version for disk_report tool. ! !- PROCEDURE read_root_map_file ( what_file : STRING ); DECLARE fp : FILE; ! File pointer DECLARE master_root : STRING; ! Current master root name DECLARE line : STRING; ! Current line DECLARE master_flag : BOOLEAN; ! New master? OPEN FILE (fp) AS what_file FOR INPUT; ! Open the master root map file master_flag = TRUE; READ FILE (fp) line; ! Read WHILE NOT ENDFILE (fp); ! Read until EOF line = UPPER(TRIM(line)); IF line[1..1] <> '!' ! Skip comments THEN IF line[1..1] = '*' ! New master follows THEN master_flag = TRUE; ELSE IF master_flag THEN master_root = line; master_flag = FALSE; ELSE rootused(line) = master_root; END IF; END IF; END IF; READ FILE (fp) line; ! Read END WHILE; CLOSE FILE (fp); ! Close the door END PROCEDURE; !+ ! NAME ! read_idl_file -- reads in a list of directory log names ! ! DESCRIPTION ! This routine reads in a list of directory log names that ! are in a file instead of on the command line - this shortens ! the command line greatly! ! ! RETURN VALUE ! ! HISTORY ! 6/88 Bauer Initial version for disk_report tool. ! !- PROCEDURE read_idl_file ( what_file : STRING ); DECLARE fp : FILE; ! File pointer DECLARE line : STRING; ! Current line EXTERNAL PROCEDURE sys$exit ( VALUE INTEGER ); OPEN FILE (fp) AS what_file FOR INPUT; ! Open the master root map file READ FILE (fp) line; ! Read WHILE NOT ENDFILE (fp); ! Read until EOF line = TRIM (line); IF line[1..1] <> '!' ! Skip comments THEN input_file_number = input_file_number + 1; IF input_file_number > MAX_REPORTS THEN write '%DISKRPT-E-TOOMANYLOGS, Maximum number of directory log files exceeded (', MAX_REPORTS, ') - constant can be changed in software'; CALL SYS$EXIT(1); END IF; input_file_tree (input_file_number) = line; END IF; READ FILE (fp) line; ! Read END WHILE; CLOSE FILE (fp); ! Close the door END PROCEDURE; !+ ! NAME ! scan_input_directory_log_files -- scans all log files, one at a time ! add_to_root_map ! ! DESCRIPTION ! This routine is the driver for the scanning of all the directory ! log files. ! ! RETURN VALUE ! ! HISTORY ! 6/88 Bauer Initial version for disk_report tool. ! !- PROCEDURE scan_input_directory_log_files; EXTERNAL PROCEDURE step_initialize; ! Inits between log files EXTERNAL PROCEDURE store_disk_summary (STRING); ! Store totals for this disk EXTERNAL PROCEDURE store_totals; ! Grand totals EXTERNAL PROCEDURE store_data_in_dir_list; DECLARE dump : FIXED STRING(1); FOR disk_number = 1 TO input_file_number STEP 1; dir_count = 0; root_count = 0; CALL step_initialize; START SCAN INPUT FILE input_file_tree(disk_number) ! Scan the log file!!! OUTPUT STRING dump; CALL store_data_in_dir_list; CALL store_disk_summary (disk_name); ! Store summaries CALL store_totals; ! Grand totals END FOR; END PROCEDURE; !+ ! NAME ! write_reports -- driver for calling report routines ! ! DESCRIPTION ! This routine determines what reports are to be written and ! calls appropriate routines to write the reports. ! ! RETURN VALUE ! ! HISTORY ! 6/88 Bauer Initial version for disk_report tool. ! !- PROCEDURE write_reports; EXTERNAL PROCEDURE sort_directories_by_size; EXTERNAL PROCEDURE write_summary_page; EXTERNAL PROCEDURE write_root_report; EXTERNAL PROCEDURE open_output_file (STRING); EXTERNAL PROCEDURE close_output_file; EXTERNAL PROCEDURE write_top_directories_report (VALUE INTEGER); EXTERNAL PROCEDURE write_merged_complete_report; EXTERNAL PROCEDURE write_complete_report; EXTERNAL PROCEDURE write_disK_report_command (STRING); EXTERNAL PROCEDURE write_master_summary_report; CALL open_output_file (output_file); ! Open the file CALL write_disk_report_command (command_line); ! Write command used IF summary_report_flag ! Disk Summary Report THEN CALL write_summary_page; END IF; IF root_report_flag ! Root Dir Report? THEN CALL write_root_report; END IF; IF top_dir_report_number > 0 ! Top Dir Report? THEN CALL sort_directories_by_size; CALL write_top_directories_report (top_dir_report_number); END IF; IF complete_report_flag ! Complete Dir Report? THEN CALL write_complete_report; END IF; IF slash_merged_flag ! Master Root Report? THEN IF master_summary_report_flag <> 0 ! THEN CALL write_master_summary_report; IF master_summary_report_flag = 2 THEN CALL write_merged_complete_report; END IF; ELSE CALL write_merged_complete_report; END IF; END IF; CALL close_output_file; ! Close the file! END PROCEDURE; !+ ! NAME ! disk_report -- Main program ! ! DESCRIPTION ! This routine is the main program for the disk report tool. ! ! RETURN VALUE ! ! HISTORY ! 6/88 Bauer Initial version for disk_report tool. ! !- PROCEDURE disk_report MAIN; EXTERNAL PROCEDURE initialize; EXTERNAL PROCEDURE process_dir_list; EXTERNAL PROCEDURE parse_command_line; CALL initialize; ! Initialize the necessary variables CALL parse_command_line; ! Extract CLI directions CALL scan_input_directory_log_files; ! Scan all directory log files CALL process_dir_list; ! Tally of disk/directory totals CALL write_reports; ! Write the reports END PROCEDURE; END MODULE;