PROGRAM Oak_tree;

(*                 XXX     X    X   X  XXXXX  XXXX   XXXXX  XXXXX
  July 14, 1986   X   X   X X   X  X     X    X   X  X      X
                  X   X  X   X  X X      X    X   X  X      X
                  X   X  X   X  XX       X    XXXX   XXX    XXX
                  X   X  XXXXX  X X      X    X X    X      X
                  X   X  X   X  X  X     X    X  X   X      X
                   XXX   X   X  X   X    X    X   X  XXXXX  XXXXX
*)

CONST  page_size = 66;
       max_lines = 55;

TYPE   command_string = STRING[127];

       output_type = (directories,files);

       REGPACK = RECORD
                 AX,BX,CX,DX,BP,SI,DI,DS,ES,FLAGS:INTEGER;
                 END;

       dir_rec     = ^DIRTREE;     (* Dynamic storage for dir names *)
       DIRTREE     = RECORD
         next      : dir_rec;
         dir_name  : STRING[15];
      END;

       filerec     = ^FILETREE;          (* Dynamic storage for the *)
       FILETREE    = RECORD              (* filename sorting tree   *)
         left      : filerec;
         right     : filerec;
         CASE BOOLEAN OF
         TRUE :  (attribute : BYTE;
                  file_time : ARRAY[1..2] OF BYTE;
                  file_date : ARRAY[1..2] OF BYTE;
                  file_size : ARRAY[1..4] OF BYTE;
                  file_name : ARRAY[1..13] OF CHAR);
         FALSE : (file_rec  : ARRAY[1..23] OF CHAR);
       END;

VAR   file_point     : filerec;        (* Pointer to a file record *)
      page_number    : INTEGER;
      line_number    : INTEGER;
      directory_count : INTEGER;
      recpack        : REGPACK;
      dta            : ARRAY[1..43] OF CHAR;   (* Disk xfer address *)
      file_request   : STRING[25];
      root_mask      : command_string; (* Used for vol-label search *)
      starting_path  : command_string;
      cluster_size   : INTEGER;
      sectors_per_cluster : INTEGER;
      free_clusters  : INTEGER;
      bytes_per_sector : INTEGER;
      total_clusters : INTEGER;
      do_we_print    : BOOLEAN;            (* Print or not          *)
      do_all_stats   : BOOLEAN;            (* List all disk stats?  *)
      no_files_out   : BOOLEAN;            (* List no files         *)
      which_list     : output_type;
      real_size      : REAL;
      r1,r2,r3       : REAL;
      total_cbytes   : REAL;
      total_bytes    : REAL;
      all_files      : INTEGER;
      req_files      : INTEGER;

(* ***************************************************** Initialize *)
(* This procedure is used to initialize some variables and strings  *)
(* prior to starting the disk search.                               *)
PROCEDURE initialize;
BEGIN
   page_number := 1;
   line_number := 1;
   directory_count := 0;
   total_cbytes := 0;
   total_bytes := 0;
   all_files := 0;
   req_files := 0;
   root_mask := 'C:\*.*';
   root_mask[length(root_mask) + 1] := chr(0);
                            (* Get the current default drive letter *)
   recpack.AX := $1900;
   intr($21,recpack);
   root_mask[1] := chr(recpack.AX AND $F + ord('A'));
END;

(* ******************************* Read And Parse Command Arguments *)
(* This procedure reads in the command line arguments, parses them, *)
(* and sets up the switches and defaults for the disk searches.     *)
PROCEDURE read_and_parse_command_arguments;
VAR    parameters_input   : command_string ABSOLUTE CSEG:$80;
       parameters         : command_string;
       index              : BYTE;
       temp_store         : CHAR;
BEGIN
   do_we_print := FALSE;
   do_all_stats := FALSE;
   no_files_out := FALSE;

            (* First, preserve the input area to allow F3 to repeat *)
   FOR index := 0 TO length(parameters_input) DO
      parameters[index] := parameters_input[index];
   FOR index := 1 TO length(parameters) DO
      BEGIN
                                    (* Find designated drive letter *)
      IF ((parameters[index] = ':') AND (index > 1)) THEN
         BEGIN
         root_mask[1] := parameters[index-1];
         parameters[index-1] := ' ';
         parameters[index] := ' ';
         END;
                                      (* Find command line switches *)
      IF (parameters[index] = '/') AND (index < length(parameters))
         THEN
         BEGIN
         temp_store := upcase(parameters[index + 1]);
         IF temp_store = 'P' THEN do_we_print := TRUE;
         IF temp_store = 'N' THEN no_files_out := TRUE;
         IF temp_store = 'S' THEN do_all_stats := TRUE;
         parameters[index] := ' ';
         parameters[index+1] := ' ';
         END;
      END;
                      (* get the current path on the selected drive *)
      getdir(ord(root_mask[1])-ord('A') + 1,starting_path);
      IF length(starting_path) > 3 THEN
         starting_path := starting_path + '\';

                  (* Finally, find the file name mask for searching *)
   REPEAT                                  (* Remove leading blanks *)
      IF parameters[1] = ' ' THEN delete(parameters,1,1);
   UNTIL (parameters[1] <> ' ') OR (length(parameters) = 0);

   index := 0;        (* Remove everything trailing the first entry *)
   REPEAT
      index := index + 1;
   UNTIL (parameters[index] = ' ') OR (index=length(parameters) + 1);
   delete(parameters,index,127);
   IF parameters = '' THEN
      file_request := '*.*'
   ELSE
      file_request := parameters;
END;

(* ********************************************* count print lines *)
PROCEDURE count_print_lines(line_count : BYTE);
VAR count : BYTE;
BEGIN
   IF do_we_print THEN
   BEGIN
      IF line_count > 250 THEN (* This signals the end of the tree *)
         BEGIN                 (* Space up to a new page           *)
         FOR count := line_number TO (page_size - 3) DO
            WRITELN(lst);
         line_number := 1;
         line_count := 0;
         END;
      line_number := line_number + line_count;
      IF line_number > max_lines THEN
         BEGIN
         page_number := page_number +1;
         FOR count := line_number TO (page_size - 2) DO
            WRITELN(lst);
         WRITELN(lst,'                           Page',page_number:4);
         WRITELN(lst);
         line_number := 1;
         END;
   END;
END;

(* *************************************************** Print Header *)
(* In this section of code, the volume label is found and displayed *)
(* and the present time and date are determined and displayed.      *)
PROCEDURE print_header;
VAR month,day,hour,minute : STRING[2];
    year                  : STRING[4];
    error                 : INTEGER;
    attribute             : BYTE;
    temp                  : BYTE;
    done                  : BOOLEAN;
    index                 : INTEGER;
BEGIN
   IF do_we_print THEN
      BEGIN
      WRITELN(lst);
      WRITELN(lst);
      WRITELN(lst);
      WRITE(lst,'          Directory for ');
      END;
   WRITE('          Directory for ');
   recpack.AX := $1A00;                           (* Set up the DTA *)
   recpack.DS := seg(dta);
   recpack.DX := ofs(dta);
   msdos(recpack);                            (* DTA setup complete *)
   error := recpack.AX AND $FF;
   IF error > 0 THEN WRITELN('DTA setup error ',error);

   recpack.AX := $4E00;                        (* Get the volume ID *)
   recpack.DS := seg(root_mask[1]);
   recpack.DX := ofs(root_mask[1]);
   recpack.CX := 8;
   intr($21,recpack);
   error := recpack.AX AND $FF;
   attribute := $1F AND mem[seg(dta):ofs(dta) + 21];

   IF ((error > 0) OR (attribute <> 8)) THEN
      BEGIN
      IF do_we_print THEN
         WRITE(lst,' <no vol label> ');
      WRITE(' <no vol label> ');
      END
   ELSE
      BEGIN                               (* Write out Volume Label *)
      done := FALSE;
      FOR index := 30 TO 40 DO
          BEGIN
          temp := mem[seg(dta):ofs(dta) + index];
          IF temp = 0 THEN done := TRUE;
          IF done = FALSE THEN
             BEGIN
             IF do_we_print THEN
                WRITE(lst,chr(temp));
             WRITE(chr(temp));
             END;
          END;
      END;

   WRITE('             ');
   IF do_we_print THEN
      WRITE(lst,'             ');
   recpack.AX := $2A00;                     (* Get the present date *)
   msdos(recpack);
   str(recpack.CX:4,Year);
   str((recpack.DX MOD 256):2,day);
   str((recpack.DX SHR 8):2,month);
   IF day[1] = ' ' THEN day[1] := '0';
   WRITE(month,'/',day,'/',year);
   IF do_we_print THEN
      WRITE(lst,month,'/',day,'/',year);
   recpack.AX := $2C00;                    (* Get the present time *)
   msdos(recpack);
   str((recpack.CX SHR 8):2,hour);
   str((recpack.CX MOD 256):2,minute);
   IF minute[1] = ' ' THEN minute[1] := '0';
   WRITELN('    ',hour,':',minute);
   WRITELN;
   IF do_we_print THEN
      BEGIN
      WRITELN(lst,'    ',hour,':',minute);
      WRITELN(lst);
      count_print_lines(2);
      END;
                                  (* get all of the disk constants *)
   recpack.AX := $3600;
   recpack.DX := (ord(root_mask[1]) - 64) AND $F;
   msdos(recpack);
   sectors_per_cluster := recpack.AX;
   free_clusters := recpack.BX;
   bytes_per_sector := recpack.CX;
   total_clusters := recpack.DX;

   cluster_size := bytes_per_sector * sectors_per_cluster;

   IF do_all_stats THEN  (* Print out disk statistics if asked for *)
      BEGIN
      WRITE('             bytes/sector =',bytes_per_sector:6);
      r1 := total_clusters;
      r2 := cluster_size;
      r1 := r1 *r2;
      WRITELN('       total disk space =',r1:12:0);
      WRITE('            bytes/cluster =',cluster_size:6);
      r3 := free_clusters;
      r2 := r3 * r2;
      WRITELN('        free disk space =',r2:12:0);
      WRITELN;
      IF do_we_print THEN
         BEGIN
         WRITE(lst,'             bytes/sector =',bytes_per_sector:6);
         WRITELN(lst,'       total disk space =',r1:12:0);
         WRITE(lst,'            bytes/cluster =',cluster_size:6);
         WRITELN(lst,'        free disk space =',r2:12:0);
         WRITELN(lst);
         count_print_lines(3);
         END;
      END;
END;

(* *************************************** Position a new filename *)
(* When a new filename is found, this routine is used to locate it *)
(* in the B-TREE that will be used to sort the filenames alphabet- *)
(* ically.                                                         *)
PROCEDURE position_a_new_filename(root, new : filerec);
VAR    index   : INTEGER;
       done    : BOOLEAN;
BEGIN
   index := 1;
   done := FALSE;
   REPEAT
      IF new^.file_name[index] < root^.file_name[index] THEN
         BEGIN
         done := TRUE;
         IF root^.left = nil THEN root^.left := new
         ELSE
            position_a_new_filename(root^.left,new);
         END
      ELSE IF new^.file_name[index] > root^.file_name[index] THEN
         BEGIN
         done := TRUE;
         IF root^.right = nil THEN root^.right := new
         ELSE
            position_a_new_filename(root^.right,new);
         END;
      index := index +1;
   UNTIL (index = 13) OR done;
END;
(* *************************************************** Print a file *)
(* This is used to print the data for one complete file.  It is     *)
(* called with a pointer to the root and an attribute that is to be *)
(* printed.  Either the directories are printed (attribute = $10),  *)
(* or the files are printed.                                        *)
PROCEDURE print_a_file(root : filerec;
                       which_list : output_type);
VAR index,temp : BYTE;
    temp_string : STRING[25];
    day         : STRING[2];
BEGIN
   temp := root^.attribute;
   IF ((temp =  $10) AND (which_list = directories)) OR
                        ((temp <> $10) AND (which_list = files)) THEN
      BEGIN
      WRITE('                ');
      CASE temp OF
         $27 : WRITE('<HID>  ');
         $10 : WRITE('<DIR>  ');
         $20 : WRITE('       ')
         ELSE  WRITE('<',temp:3,'>  ');
      END;   (* of CASE *)
      IF do_we_print THEN
         BEGIN
         WRITE(lst,'                ');
            CASE temp OF
               $27 : WRITE(lst,'<HID>  ');
               $10 : WRITE(lst,'<DIR>  ');
               $20 : WRITE(lst,'       ')
               ELSE  WRITE(lst,'<',temp:3,'>  ');
            END;   (* of CASE *)
         END;
      temp_string := '               ';
      index := 1;
      REPEAT
         temp := ord(root^.file_name[index]);
         IF temp > 0 THEN
            temp_string[index] := root^.file_name[index];
         index := index + 1;
      UNTIL (temp = 0) OR ( index = 14);
      temp_string[0] := chr(15);
      WRITE(temp_string);
      IF do_we_print THEN
         WRITE(lst,temp_string);

                                         (* Write out the file size *)
      r1 := root^.file_size[1];
      r2 := root^.file_size[2];
      r3 := root^.file_size[3];
      real_size := r3*65536.0 + r2 * 256.0 + r1;
      WRITE(real_size:9:0);
      IF do_we_print THEN
         WRITE(lst,real_size:9:0);
                                         (* Write out the file date *)
      temp := ((root^.file_date[1] SHR 5) AND $7);
      WRITE('   ',(temp + ((root^.file_date[2] AND 1) SHL 3)):2,'/');
      IF do_we_print THEN
      WRITE(lst,'   ',
                    (temp+((root^.file_date[2] AND 1) SHL 3)):2,'/');
      str((root^.file_date[1] AND $1F):2,day);
      IF day[1] = ' ' THEN day[1] := '0';
      WRITE(day,'/');
      WRITE(80 + ((root^.file_date[2] SHR 1) AND $7F),'   ');
      IF do_we_print THEN
         BEGIN
         WRITE(lst,day,'/');
         WRITE(lst,80 + ((root^.file_date[2] SHR 1) AND $7F),'   ');
         END;

                                         (* Write out the file time *)
      WRITE('  ',((root^.file_time[2] SHR 3) AND $1F):2,':');
      IF do_we_print THEN
         WRITE(lst,'  ',((root^.file_time[2] SHR 3) AND $1F):2,':');
      temp := ((root^.file_time[2]) AND $7) SHL 3;
      str((temp + ((root^.file_time[1] SHR 5) AND $7)):2,day);
      IF day[1] = ' ' THEN day[1] := '0';
      WRITELN(day);
      IF do_we_print THEN
         BEGIN
         WRITELN(lst,day);
         count_print_lines(1);
         END;
      END;

END;

(* ********************************************** Print a directory *)
(* This is a recursive routine to print out the filenames in alpha- *)
(* betical order.  It uses a B-TREE with "infix" notation.  The     *)
(* actual printing logic was removed to another procedure so that   *)
(* the recursive part of the routine would not be too large and     *)
(* up the heap too fast.                                            *)
PROCEDURE print_a_directory(root         : filerec;
                            which_list   : output_type);
BEGIN
   IF root^.left <> nil THEN
      print_a_directory(root^.left,which_list);
                                          (* Write out the filename *)
   print_a_file(root,which_list);

   IF root^.right <> nil THEN
      print_a_directory(root^.right,which_list);
END;

(* ***************************************************** Erase tree *)
(* After the directory is printed and counted, it must be erased or *)
(* the "heap" may overflow for a large disk with a lot of files.    *)
PROCEDURE erase_tree(root : filerec);
BEGIN
   IF root^.left  <> nil THEN erase_tree(root^.left);
   IF root^.right <> nil THEN erase_tree(root^.right);
   dispose(root);
END;

(* ************************************************* Do A Directory *)
(* This procedure reads all entries in any directory and sorts the  *)
(* filenames alphabetically.  Then it prints out the complete stat- *)
(* istics, and calls itself to do all of the same things for each   *)
(* of its own subdirectories.  Since each subdirectory also calls   *)
(* each of its subdirectories, the recursion continues until there  *)
(* are no more subdirectories.                                      *)
PROCEDURE do_a_directory(input_mask : command_string);
VAR   mask          : command_string;
      count,index   : INTEGER;
      error         : BYTE;
      cluster_count : INTEGER;
      byte_count    : REAL;
      tree_root     : filerec;                 (* Root of file tree *)
      dir_root      : dir_rec;
      dir_point     : dir_rec;
      dir_last      : dir_rec;

    (* This embedded procedure is called upon to store all of the  *)
    (* directory names in a linear linked list rather than a       *)
    (* B-TREE since it should be rather short and efficiency of    *)
    (* sorting is not an issue.  A bubble sort will be used on it. *)
    PROCEDURE store_dir_name;
    VAR temp_string : STRING[15];
        temp        : BYTE;
        index       : BYTE;
    BEGIN
       temp := mem[seg(dta):ofs(dta) + 21];           (* Attribute *)
       IF temp = $10 THEN  (* Pick out directories *)
          BEGIN
          index := 1;
          REPEAT
             temp := mem[seg(dta):ofs(dta) + 29 + index];
             IF temp > 0 THEN
                temp_string[index] := chr(temp);
             index := index + 1;
          UNTIL (temp = 0) OR (index = 14);
          temp_string[0] := chr(index - 2);
                     (* Directory name found, ignore if it is a '.' *)
          IF temp_string[1] <> '.' THEN
             BEGIN
             new(dir_point);
             dir_point^.dir_name := temp_string;
             dir_point^.next := nil;
             IF dir_root = nil THEN
                dir_root := dir_point
             ELSE
                dir_last^.next := dir_point;
             dir_last := dir_point;
             END;
          END;
     END;

     (* This is the procedure that sorts the directory names after *)
     (* they are all accumulated.  It uses a bubble sort technique *)
     (* which is probably the most inefficient sort available.  It *)
     (* is perfectly acceptable for what is expected to be a very  *)
     (* short list each time it is called.  More than 30 or 40     *)
     (* subdirectories in one directory would not be good practice *)
     (* but this routine would sort any number given to it.        *)
     PROCEDURE sort_dir_names;
     VAR change      : BYTE;
         save_string : STRING[15];
         dir_next    : dir_rec;
     BEGIN
        REPEAT
           change := 0;
           dir_point := dir_root;
           WHILE dir_point^.next <> nil DO
              BEGIN
              dir_next := dir_point^.next;
              save_string := dir_next^.dir_name;
              IF save_string < dir_point^.dir_name THEN
                 BEGIN
                 dir_next^.dir_name := dir_point^.dir_name;
                 dir_point^.dir_name := save_string;
                 change := 1;
                 END;
              dir_point := dir_point^.next;
              END;
        UNTIL change = 0;    (* No swaps in this pass, we are done *)
     END;

BEGIN
   count := 0;
   cluster_count := 0;
   dir_root := nil;
   mask := input_mask + '*.*';
   mask[length(mask) + 1] := chr(0);     (* A trailing zero for DOS *)
                                    (* Count all files and clusters *)
   REPEAT
      IF count = 0 THEN
         BEGIN                         (* Get first directory entry *)
         recpack.AX := $4E00;
         recpack.DS := seg(mask[1]);
         recpack.DX := ofs(mask[1]);
         recpack.CX := $17;             (* Attribute for all files *)
         intr($21,recpack);
         END
      ELSE
         BEGIN                 (* Get additional directory entries *)
         recpack.AX := $4F00;
         intr($21,recpack);
         END;
      error := recpack.AX AND $FF;
      IF error = 0 THEN                (* A good filename is found *)
         BEGIN
         count := count +1;            (* Add one for a good entry *)

                           (* Count up the number of clusters used *)
         r1 := mem[seg(dta):ofs(dta) + 26];
         r2 := mem[seg(dta):ofs(dta) + 27];
         r3 := mem[seg(dta):ofs(dta) + 28];
         real_size := r3*65536.0 + r2 * 256.0 + r1; (*Nmbr of bytes*)
         r1 := cluster_size;
         r1 := real_size/r1;                 (* Number of clusters *)
         index := trunc(r1);
         r2 := index;
         IF (r1 - r2) > 0.0 THEN
            index := index +1;             (* If a fractional part *)
         cluster_count := cluster_count + index;
         IF index = 0 THEN     (* This is a directory, one cluster *)
            cluster_count := cluster_count +1;
         store_dir_name;
         END;
   UNTIL error > 0;
   r1 := cluster_count;
   r2 := cluster_size;
   r1 := r1 * r2;
   directory_count := directory_count + 1;
   WRITE('    ',directory_count:3,'. ');
   WRITE(input_mask);
   FOR index := 1 TO (32 - length(input_mask)) DO WRITE(' ');
   WRITELN(count:4,' Files  Cbytes =',r1:9:0);
   IF do_we_print THEN
      BEGIN
      WRITE(lst,'    ',directory_count:3,'. ');
      WRITE(lst,input_mask);
      FOR index := 1 TO (32 - length(input_mask)) DO WRITE(lst,' ');
      WRITELN(lst,count:4,' Files  Cbytes =',r1:9:0);
      count_print_lines(1);
      END;
   total_cbytes := total_cbytes + r1;
   all_files := all_files + count;

                            (* files counted and clusters counted   *)
                            (* Now read in only the requested files *)

   count := 0;
   byte_count := 0;
   tree_root := nil;
   IF no_files_out <> TRUE THEN
      BEGIN
      mask := input_mask + file_request;
      mask[length(mask) + 1] := chr(0); (* A trailing zero for DOS *)
      REPEAT
         new(file_point);
         IF count = 0 THEN
            BEGIN                     (* Get first directory entry *)
            recpack.AX := $4E00;
            recpack.DS := seg(mask[1]);
            recpack.DX := ofs(mask[1]);
            recpack.CX := $17;          (* Attribute for all files *)
            intr($21,recpack);
            END
         ELSE
            BEGIN              (* Get additional directory entries *)
            recpack.AX := $4F00;
            intr($21,recpack);
            END;
         error := recpack.AX AND $FF;
         IF error = 0 THEN             (* A good filename is found *)
            BEGIN
            count := count +1;         (* Add one for a good entry *)
            file_point^.left := nil;
            file_point^.right := nil;
            FOR index := 1 TO 23 DO
               file_point^.file_rec[index] :=
                           char(mem[seg(dta):ofs(dta) + 20 + index]);
            IF tree_root = nil THEN
               BEGIN             (* Point to first element in tree *)
               tree_root := file_point;
               END
            ELSE
               BEGIN       (* Point to additional elements in tree *)
                  position_a_new_filename(tree_root,file_point);
               END;

                              (* Count up the number of bytes used *)
            r1 := file_point^.file_size[1];
            r2 := file_point^.file_size[2];
            r3 := file_point^.file_size[3];
            real_size := r3*65536.0 + r2 * 256.0 + r1; (*Number of *)
                                                    (* bytes used. *)
            byte_count := byte_count + real_size;
            END;
      UNTIL error > 0;
   END;

   which_list := directories;
   IF tree_root <> nil THEN
      print_a_directory(tree_root,which_list);
   IF tree_root <> nil THEN
      print_a_directory(tree_root,succ(which_list));
   IF count > 0 THEN
      BEGIN
      WRITELN('                  ',count:5,' Files ',
                                 byte_count:17:0,' Bytes');
      WRITELN;
      IF do_we_print THEN
         BEGIN
         WRITELN(lst,'                  ',count:5,' Files ',
                                    byte_count:17:0,' Bytes');
         WRITELN(lst);
         count_print_lines(2);
         END;
      total_bytes := total_bytes + byte_count;
      req_files := req_files + count;
      END;
                            (* Now go do all of the subdirectories *)
   IF dir_root <> nil THEN sort_dir_names;
   dir_point := dir_root;
   WHILE dir_point <> nil DO
      BEGIN
      mask := input_mask + dir_point^.dir_name + '\';
      do_a_directory(mask);
      dir_point := dir_point^.next;
      END;
                           (* Finally, erase the tree and the list *)
   IF tree_root <> nil THEN
      erase_tree(tree_root);

   WHILE dir_root <> nil DO
      BEGIN
      dir_point := dir_root^.next;
      dispose(dir_root);
      dir_root := dir_point;
      END;
END;

(* ******************************************* Output Summary Data *)
PROCEDURE output_summary_data;
BEGIN
   WRITELN;
   WRITE('                     ',req_files:5,' Files');
   WRITELN(total_bytes:15:0,' Bytes in request');
   WRITE('                     ',all_files:5,' Files');
   WRITELN(total_cbytes:15:0,' Cbytes in tree');
   WRITE('                                   ');
   r1 := free_clusters;
   r2 := cluster_size;
   r1 := r1 * r2;
   WRITELN(r1:12:0,' Bytes free on disk');
   IF do_we_print THEN
      BEGIN
      WRITELN(lst);
      WRITE(lst,'                     ',req_files:5,' Files');
      WRITELN(lst,total_bytes:15:0,' Bytes in request');
      WRITE(lst,'                     ',all_files:5,' Files');
      WRITELN(lst,total_cbytes:15:0,' Cbytes in tree');
      WRITE(lst,'                                   ');
      WRITELN(lst,r1:12:0,' Bytes free on disk');
      count_print_lines(4);      (* Signal the end, space paper up *)
      END;
END;

BEGIN  (* Main program - Oak Tree ********************************* *)
  initialize;
  read_and_parse_command_arguments;
  print_header;
  do_a_directory(starting_path);
  output_summary_data;
  count_print_lines(255);
END.  (* Main Program *)
