1  Introduction

File Maintenance in dBASE IV

David Howlett

This article will focus on using .BIN files to access  disk
information such as filenames and directory names.  By using  the LOAD
and CALL commands, any type of file or directory is available  to the
dBASE IV user in a way which is most convenient to work with,  by
writing the list to a database file.  Once in a database, this 
information can be totaled, sorted, analyzed, or otherwise
manipulated.



2  Article

The two .BIN files listed in this article can be used to develop
dBASE  IV applications for file and directory maintenance.  For
example,  many network users receive message files deposited in a
specific directory  such as \MSGS.  A database file can be generated
with a list of the  filenames of these messages, then a program can be
quickly constructed  to discard messages older than a certain date or
perform other types  of message maintenance.

Similarly, users may receive a diskette containing one or more
database  files which need to be updated to a master file.  By first
generating  a list of the diskette files, a program can be constructed
to process  them in order.

Creating a hard disk usage report

The application this article will explore is generating a diskspace 
usage report of a hard disk.  This report will display the names of 
all the directories on your hard disk.  As each directory name is 
listed, the diskspace used by the files in that directory is also 
listed.  The listing has the following format:

Directory: \MYFILES
   *.DBF   12365 bytes
   *.NDX   10321 bytes
   *.PRG    5425 bytes Total      27111 bytes

This report is useful for identifying directories that occupy the 
most space on your hard disk.  Directories and file groups that are 
taking a lot of room and are no longer of use to you can easily be 
found and deleted.

Creating the .BIN files

In order to create our application, we must first create the two .BIN 
files we have mentioned.  Figure 1 contains the source code necessary 
to create FDir.BIN.  You will need to type Figure 1 into a text file 
named FDir.ASM.  The text in Figure 2 should be typed into a file 
named FTree.ASM.

To generate the .BIN files you will need an assembly language
compiler  such as Microsoft Macro Assembler version 4.0.  This will
create an  .OBJ file which must be LINKed to create an .EXE file.  The
resultant  .EXE file must then be EXE2BINed into a .BIN file.  Using
Microsoft's  utilities, the syntax to create FDir.BIN would be:

MASM FDir.ASM; LINK FDir.OBJ; EXE2BIN FDir.EXE

The linker will most likely generate the message "Warning: no stack 
segment."  This message is accurate and acceptable for what we are 
doing.  No other error messages should be encountered.  Repeat the 
compiling process for FTree.ASM.

NOTE:   Before testing the .BIN files, MAKE SURE TO  MAKE A BACKUP OF
ANY DATA FILES OF VALUE TO YOU.  If, for example,  you mistype the
program and leave out one or more lines, it is possible  for the
program to behave unpredictably.  The source code as it is  printed in
this magazine has undergone substantial testing.  However,  ANY new
program you use should be introduced with caution.  As stated  on page
2, the responsibility for the protection of your data is yours.  

On that dry note we continue.

Using the .BIN files from dBASE IV

Both .BIN files are designed to write information to an <MI>empty 
.DBF file.  The .DBF file must follow this exact structure:

FILENAME Character  12 FILESIZE Character   9 FILEDATE Character  10
FILETIME Character   8

The first .BIN file, FDir.BIN, requires three pieces of information 
be passed to it.  First, the name of the .DBF file to write to. 
Second,  the file specification (for example *.*, *.dbf, MERGE??.TXT,
etc.).  Third,  the directory to examine.   

These three pieces of information must be contained in a single
character  variable which is passed to FDir.BIN.  Each parameter
should be separated  by a comma.   For example,

x = "test.dbf,*.*,c:\msgs" CALL FDIR WITH x

The preceding CALL to FDir would have written the names of all files 
(*.*) in the directory C:\MSGS to the database Test.DBF.

FTree.BIN requires two pieces of information, the .DBF and the
directory.  For  example,

x= "test.dbf,c:\msgs" CALL FTREE WITH x

The preceding CALL to FTree would have written the names of any
directories  below \MSGS to the file Test.DBF.  Both FTree.BIN and
FDir.BIN can  be used with the dBASE IV CALL() function if desired. 
The syntax  would be

CALL( "FTree", x) or CALL( "FDir", x)

Error Checking

The .BIN files DO NOT check the database to verify the file has the 
correct structure or is empty before writing data.  In fact the only 
error checking that the two .BIN files perform is to check if the 
.DBF and directory exist; if not, the .BIN file will return with no 
action taken. 

It is the responsibility of the user to be sure the .BIN files are 
passed completely valid parameters and that the database it is
instructed  to write to has the correct structure and is empty.  In
addition,  the .DBF file MUST BE CLOSED.

Our application

There are two .PRG files and two .DBF files necessary to generate  our
hard disk usage report.  The structure for both .DBFs is shown  in
Figure 3.  Figure 4 contains the source code for BuildDir.PRG and 
Figure 5 contains the source code for DReport.PRG.  DirNames.DBF is 
used to build a master list of all directories.  The second database, 
Collect.DBF, is used by the .BIN files to collect filenames.

The first .PRG file, BuildDir.PRG, has but one purpose:  it builds 
the DirNames.DBF database.  It does so by starting at the root
directory  and, using FTree.BIN, writes directory names to
Collect.DBF.  The  directory names in Collect.DBF are added to
DirNames.DBF.  Next a  looping process begins in which each and every
directory name which  has been written to DirNames.DBF is analyzed. 
Each directory name  in DirNames.DBF is checked to see if more
directories branch below  it.  In this way, a complete list of all
directories is built in DirNames.DBF.

DReport.PRG INDEXes the directory names in DirNames.DBF
alphabetically.  Next,  each directory name in DirNames.DBF is
analyzed to see what files  are in it.  This is done with CALLs to
FDir.BIN which writes the filenames  to Collect.DBF.  Collect.DBF is
INDEXed on the file extension.  The  filesizes of each group of files
(for example all files with the extension  .DBF) are subtotaled and
printed.  When all files in one directory  have been printed, a total
for the whole directory is printed.  The  process then repeats by
fetching another directory name from DirNames.DBF.

One special note is the use of a variable named clust_size in
DReport.PRG.  Let's  say you have some very small text files:  ten
text files, each of  them is one line of text (80 bytes).  Totaling
the ten files would  give you 800 bytes; this would be inaccurate as
to the actual room  used up by these files.  

Hard disks and floppy disks are partitioned into what is called
clusters.  Looking  at a chess board, one could say it contains 64
clusters, each cluster  or square can only contain one item.  On your
hard disk, a file with  even only 1 byte occupies a cluster. Since no
other file can share  that cluster, the 1 byte file is using up a full
cluster of room on  your hard disk.  Cluster sizes can vary from 1024,
2048 or 4096 bytes. 


The variable clust_size should contain the cluster size for your hard 
disk.  There is an easy way to get this number.  Run a DOS DIR
command;  this will list the available space on your disk as the last
line.  Write  this number down, then create a very small file.  You
can use the  command

 COPY CON: TEST.123

Press Return then press Ctrl-Z.  Run a second DIR command.  Subtract 
the first number you wrote down from the new available space
displayed  on your screen.  <MI>Vala! the cluster size.

In an effort to avoid complexity with the .PRG programs, DReport.PRG 
does not contain any commands to paginate its printout.  It simply 
pours line after line to the printer.  If you desire better
pagination,  the program must be modified.

With two simple changes you can direct the printout to go to a text 
file instead of the printer.  This way the text file can be loaded 
into a word processor and printed with the desired pagination.

Delete the line SET PRINT ON and replace it with 

SET ALTERNATE TO Dreport.TXT SET ALTERNATE ON

Delete the line SET PRINT OFF and replace it with

SET ALTERNATE OFF 





3  Figure 1--FDir.ASM

Figure 1--FDir.ASM

;--- FDir.asm

code          SEGMENT
              ASSUME    cs:code, ds:code
code_1        PROC      FAR
              ORG       0h
start:
              ;---------Beginning of program
              PUSH BX
              PUSH DS

              ;--- move dBASE variable to

               ; bin file
              ;--- First the .DBF name
              MOV  SI, OFFSET dbfname

get_the_dbf_loop:
              MOV  AH, DS:[BX]
              MOV  CS:[SI], AH
              INC  BX
              INC  SI

              CMP  BYTE PTR DS:[BX], ','
              JNZ  get_the_dbf_loop
              MOV  BYTE PTR CS:[SI], 0
              INC  BX

              ;--- Second the filespec
              MOV  SI, OFFSET filespec

get_the_spec_loop:
              MOV  AH, DS:[BX]
              MOV  CS:[SI], AH
              INC  BX
              INC  SI
              CMP  BYTE PTR DS:[BX], ','
              JNZ  get_the_spec_loop
              MOV  BYTE PTR CS:[SI], 0
              INC  BX

              ;--- Third the path
              MOV  SI, OFFSET pathname
get_the_path_loop:
              MOV  AH, DS:[BX]
              MOV  CS:[SI], AH
              INC  BX
              INC  SI
              CMP  BYTE PTR DS:[BX], 0
              JNZ  get_the_path_loop
              MOV  BYTE PTR CS:[SI], 0

              ;--- Set data segment to that

               ;    of bin file
              MOV  AX, CS
              MOV  DS, AX

              ;--- Put a null after filename

               ;    and filespec
              MOV  BYTE PTR null_terminator, 0
              MOV  BYTE PTR null_terminator2, 0
              ;--- Set old path to 0
              MOV  BYTE PTR old_path, 0

              ;--- Get current DTA
              MOV  AH, 2Fh
              INT  21h
              MOV  dta_hi, ES
              MOV  dta_lo, BX

              ;--- Set new DTA
              MOV  AH, 1Ah
              MOV  DX, OFFSET disk_reserved
              INT  21h

              ;--- Open dbfname
              MOV  DX, OFFSET dbfname
              MOV  AL, 2
              MOV  AH, 3Dh
              INT  21h
              JNC  open_was_ok
              JMP  quit_to_dbase

open_was_ok:  MOV  handle, AX
              ;--- move to 5th byte of file
              MOV  BX, AX
              MOV  CX, 0
              MOV  DX, 4
              MOV  AL, 0
              MOV  AH, 42h
              INT  21h
              JNC  move_was_ok
              JMP  close_then_quit

move_was_ok:  ;--- Read the reccount
              MOV  DX, OFFSET reccount
              MOV  BX, handle
              MOV  CX, 4

              MOV  AH, 3Fh
              INT  21h
              JNC  read_was_ok
              JMP  close_then_quit
read_was_ok:  ;--- Move to 9th byte of file
              MOV  BX, AX
              MOV  CX, 0
              MOV  DX, 9
              MOV  AL, 0
              MOV  AH, 42h
              INT  21h
              JC   close_then_quit

              ;--- Read the Header size
              MOV  DX, OFFSET head_size
              MOV  BX, handle
              MOV  CX, 2
              MOV  AH, 3Fh
              INT  21h
              JC   close_then_quit

;--- move to end of dbf header
              MOV  BX, handle
              MOV  CX, 0
              MOV  DX, head_size
              MOV  AL, 0
              MOV  AH, 42h
              INT  21h
              JC   close_then_quit

              ;--- Save the old pathname
              MOV  DL, 0
              MOV  SI, OFFSET old_path
              MOV  AH, 47h
              INT  21h

              ;--- Change path
              MOV  DX, OFFSET pathname
              MOV  AH, 3Bh
              INT  21h
              JC   close_then_quit


              ;--- Find first
              MOV  DX, OFFSET filespec
              MOV  CX, 0
              MOV  AH, 4Eh
              INT  21h
              ;--- If no matches or error,

               ;    then exit
              JC   close_then_quit
              CALL WRITE_A_RECORD

              ;--- loop of find next
loop2:        MOV  AH, 4Fh
              INT  21h
              JC   update_dbf
              CALL WRITE_A_RECORD
              JMP  loop2

update_dbf:   ;--- Write an eof
              MOV  BX, handle
              MOV  DX, OFFSET eof
              MOV  CX, 1
              MOV  AH, 40h
              INT  21h

              ;--- Move to 5th byte of file
              MOV  BX, handle
              MOV  CX, 0
              MOV  DX, 4
              MOV  AL, 0
              MOV  AH, 42h
              INT  21h

              ;--- Write new reccount
              MOV  BX, handle
              MOV  DX, OFFSET reccount
              MOV  CX, 4
              MOV  AH, 40h
              INT  21h

close_then_quit:
              ;--- Close the filehandle
              MOV  BX, handle
              MOV  AH, 3Eh
              INT  21h

quit_to_dbase:
              ;--- Restore old path
              CMP  BYTE PTR old_path, 0
              JE   no_path_change

              MOV  DX, OFFSET full_path
              MOV  AH, 3Bh
              INT  21h

no_path_change: ;--- Reset old DTA
              PUSH DS
              MOV  AX, dta_hi
              MOV  DS, AX
              MOV  DX, dta_lo
              MOV  AH, 1Ah
              INT  21h
              POP  DS

              ;--- Restore the dBASE

               ; segment registers
              POP  DS
              POP  BX

              RET

code_1        ENDP



WRITE_A_RECORD PROC NEAR
               ;--- Write 1 blank for

                ;    delete flag
               MOV  BX, handle
               MOV  DX, OFFSET blanks
               MOV  CX, 1
               MOV  AH, 40h
               INT  21h
               ;--- Search for null left by

                ;    DOS after the filename
               MOV  AX, DS
               MOV  ES, AX
               CLD
               MOV  DI, OFFSET disk_filename
               MOV  CX, 13
               MOV  AL, 0
         REPNE SCASB
               DEC  DI
               CMP  CX, 0
               JE   no_nulls               

              ;--- Replace null and remaining

               ;    characters with spaces

fill_spaces:   MOV  BYTE PTR [DI], ' '
               INC  DI
               SUB  CX, 1
               JNZ  fill_spaces

no_nulls:      ;--- Write the filename
               MOV  BX, handle
               MOV  DX, OFFSET disk_filename
               MOV  CX, 12
               MOV  AH, 40h
               INT  21h

<N>              ;--- Convert the filesize to

                ;    char string
               MOV  DI, OFFSET fsize_string
               MOV  SI, 0
               CALL NUM_TO_ASCII

               ;--- Write the filesize char

               ;    string
               MOV  BX, handle
               MOV  DX, OFFSET fsize_string
               MOV  CX, 9
               MOV  AH, 40h
               INT  21h

               ;--- Convert the date to 

                ;    char string
               ;--- month
               MOV  AX, disk_date
               AND  AX, 0000000111100000b
               MOV  CL, 5
               SHR  AX, CL
               CALL num_to_ascii2

               MOV  date_string, AH
               MOV  date_string+1, AL
               ;--- date
               MOV  AX, disk_date
               AND  AX, 0000000000011111b
               CALL num_to_ascii2
               MOV  date_string+3, AH
               MOV  date_string+4, AL

<N>              ;--- year
               MOV  AX, disk_date

               AND  AX, 1111111000000000b
               MOV  CL, 9
               SHR  AX, CL
               ADD  AL, 80
               CALL num_to_ascii2
               MOV  date_string+6, AH
               MOV  date_string+7, AL

               ;--- Write date_string
               MOV  BX, handle
               MOV  DX, OFFSET date_string
               SUB  DX, 2
               MOV  CX, 10
               MOV  AH, 40h
               INT  21h

<N>              ;--- Convert the time to 

                ;    char string
               ;--- hour
               MOV  AX, disk_time
               AND  AX, 1111100000000000b
               MOV  CL, 11
               SHR  AX, CL
               MOV  BL, 61h
               CMP  AL, 12
               JL   am
               MOV  BL, 70h
               SUB  AL, 12

am:            CMP  AL, 0
               JNE  time_ok
               MOV  AL, 12

time_ok:       MOV  time_string+5, BL
               CALL num_to_ascii2
               MOV  time_string, AH
               MOV  time_string+1, AL

               ;--- minute
               MOV  AX, disk_time
               AND  AX, 0000011111100000b
               MOV  CL, 5
               SHR  AX, CL
               CALL num_to_ascii2
               MOV  time_string+3, AH
               MOV  time_string+4, AL

<N>              ;--- Write time_string
               MOV  BX, handle
               MOV  DX, OFFSET time_string
               SUB  DX, 2
               MOV  CX, 8
               MOV  AH, 40h
               INT  21h

               ;--- Add 1 to new reccount
               ADD  WORD PTR reccount, 1
               ADC  WORD PTR reccount+2, 0
               RET
WRITE_A_RECORD ENDP



num_to_ascii  PROC near
              MOV  leading_zero, 1
div_loop1:
              ;--- quit after 9 loops
              CMP   SI, 9
              JE    div_exit
              ;--- load divisor from table
              SHL   SI, 1
              MOV   DX,table1[si]
              MOV   AX,table2[si]
              SHR   SI, 1

              ;--- Place a '0' in cx register
              MOV   CL, 48

counter_loop:
              ;--- if disk_filesize is 

              ;    less than divisor

              ;    do not inc CX

              CMP   WORD PTR disk_filesize+2,DX
              JB    done_with_cx
              JA    inc_cx
              CMP   word ptr disk_filesize, AX
              JB    done_with_cx

inc_cx:
              INC   CL
              ;--- subtract the divisor from

              ;    disk_filesize
              SUB   WORD PTR disk_filesize, AX
              SBB   WORD PTR disk_filesize+2,DX
              JMP   SHORT counter_loop


done_with_cx:
              CMP   CL, 48
              JNE   write_it
              CMP   SI, 8
              JE    write_it
              CMP   leading_zero, 1
              JNE   write_it
              MOV   CL, 20h
              MOV   [DI], CL
              JMP   more_nums

write_it:     MOV   [DI], CL
              MOV   leading_zero, 0

more_nums:    INC   DI
              INC   SI
              JMP   short div_loop1

div_exit:
              RET
num_to_ascii  ENDP



num_to_ascii2 PROC
              MOV  AH, 30h

ascii2_loop:  CMP  AL, 10
              JL   done_ascii2
              INC  AH
              SUB  AL, 10
              JMP  ascii2_loop

done_ascii2:  ADD  AL, 30h
              RET
num_to_ascii2 ENDP



;--------- Beginning of data

table1        DW  05F5h
              DW  0098h
              DW  000Fh
              DW  0001h

              DW  0000h
              DW  0000h
              DW  0000h
              DW  0000h

              DW  0000h

table2        DW  0E100h
              DW  9680h
              DW  4240h
              DW  86A0h

              DW  2710h
              DW  03E8h
              DW  0064h
              DW  000Ah
              DW  0001h

dbfname               DB  65 DUP (0)
null_terminator       DB  0
filespec              DB  12 DUP (0)
null_terminator2      DB  0
pathname              DB  65 DUP (0)
handle                DW  0

head_size             DW  0
reccount              DD  0
dta_hi                DW  0
dta_lo                DW  0
disk_reserved         DB  21 DUP (0)
disk_attribute        DB  1
disk_time             DW  0
disk_date             DW  0
disk_filesize         DB  4 DUP (0)
disk_filename         DB  14 DUP (0)

blanks                DB  19 DUP (20h)
fsize_string          DB  9 DUP (20h)
eof                   DB  26
                      DB  20h, 20h
date_string           DB  "  -  -  "
                      DB  20h, 20h
time_string           DB  "  :    "
leading_zero          DW  0
full_path             DB  '\'
old_path              DB  65 DUP (0)

;---------End of data ----------------


code          ENDS

              END       start




4  Figure 2--FTree.ASM

Figure 2--FTree.ASM

;--- FTree.asm

code          SEGMENT
              ASSUME    cs:code, ds:code
code_1        PROC      FAR
              ORG       0h
start:
              ;---------Beginning of program
              PUSH BX
              PUSH DS

              ;--- move dBASE variable to

               ;    bin file

              ;--- First the .DBF name

              MOV  SI, OFFSET dbfname


get_the_dbf_loop:
              MOV  AH, DS:[BX]
              MOV  CS:[SI], AH
              INC  BX
              INC  SI
              CMP  BYTE PTR DS:[BX], ','
              JNZ  get_the_dbf_loop
              MOV  BYTE PTR CS:[SI], 0
              INC  BX
              ;--- Second the pathname
              MOV  SI, OFFSET pathname

get_the_path_loop:
              MOV  AH, DS:[BX]
              MOV  CS:[SI], AH
              INC  BX
              INC  SI
              CMP  BYTE PTR DS:[BX], 0
              JNZ  get_the_path_loop
              MOV  BYTE PTR CS:[SI], 0

              ;--- Set data segment to 

               ;    that of bin file
              MOV  AX, CS
              MOV  DS, AX

              ;--- Put a null after filename
              MOV  BYTE PTR null_terminator, 0
              ;--- Set old_path to 0
              MOV  BYTE PTR old_path, 0

              ;--- Get current DTA
              MOV  AH, 2Fh
              INT  21h
              MOV  dta_hi, ES
              MOV  dta_lo, BX

              ;--- Set new DTA
              MOV  AH, 1Ah
              MOV  DX, OFFSET disk_reserved
              INT  21h

              ;--- Open dbfname
              MOV  DX, OFFSET dbfname
              MOV  AL, 2
              MOV  AH, 3Dh
              INT  21h
              JNC  open_was_ok
              JMP  quit_to_dbase
open_was_ok:  MOV  handle, AX
              ;--- move to 5th byte of file
              MOV  BX, AX
              MOV  CX, 0
              MOV  DX, 4
              MOV  AL, 0
              MOV  AH, 42h
              INT  21h
              JNC  move_was_ok
              JMP  close_then_quit

move_was_ok:  ;--- Read the reccount
              MOV  DX, OFFSET reccount
              MOV  BX, handle
              MOV  CX, 4
              MOV  AH, 3Fh
              INT  21h
              JNC  read_was_ok
              JMP  close_then_quit

read_was_ok:  ;--- move to 9th byte of file
              MOV  BX, AX
              MOV  CX, 0
              MOV  DX, 9
              MOV  AL, 0
              MOV  AH, 42h
              INT  21h
              JC   close_then_quit

              ;--- Read the Header size
              MOV  DX, OFFSET head_size
              MOV  BX, handle
              MOV  CX, 2
              MOV  AH, 3Fh
              INT  21h
              JC   close_then_quit

<N>             ;--- move to end of dbf header
              MOV  BX, handle
              MOV  CX, 0
              MOV  DX, head_size

              MOV  AL, 0
              MOV  AH, 42h
              INT  21h
              JC   close_then_quit

              ;--- Save the old pathname
              MOV  DL, 0
              MOV  SI, OFFSET old_path
              MOV  AH, 47h
              INT  21h

              ;--- Change path
              MOV  DX, OFFSET pathname
              MOV  AH, 3Bh
              INT  21h
              JC   close_then_quit

              ;--- Find first
              MOV  DX, OFFSET filespec
              MOV  CX, 10h
              MOV  AH, 4Eh
              INT  21h
              ;--- If no matches or

               ;    error, then exit
              JC   close_then_quit
              CALL WRITE_A_RECORD

              ;--- loop of find next
loop2:        MOV  AH, 4Fh
              INT  21h
              JC   update_dbf
              CALL WRITE_A_RECORD
              JMP  loop2

update_dbf:   ;--- Write an eof
              MOV  BX, handle
              MOV  DX, OFFSET eof
              MOV  CX, 1
              MOV  AH, 40h
              INT  21h

<N>             ;--- Move to 5th byte of file
              MOV  BX, handle
              MOV  CX, 0
              MOV  DX, 4

              MOV  AL, 0
              MOV  AH, 42h
              INT  21h

<N>             ;--- Write new reccount
              MOV  BX, handle
              MOV  DX, OFFSET reccount
              MOV  CX, 4
              MOV  AH, 40h
              INT  21h

close_then_quit:
              ;--- Close the filehandle
              MOV  BX, handle
              MOV  AH, 3Eh
              INT  21h

quit_to_dbase:
              ;--- Restore old path
              CMP  BYTE PTR old_path, 0
              JE   no_path_change
              MOV  DX, OFFSET full_path
              MOV  AH, 3Bh
              INT  21h

no_path_change: ;--- Reset old DTA
              PUSH DS
              MOV  AX, dta_hi
              MOV  DS, AX
              MOV  DX, dta_lo
              MOV  AH, 1Ah
              INT  21h
              POP  DS

              ;--- Restore the dBASE segment

              ;    registers
              POP  DS
              POP  BX

              RET
code_1        ENDP

WRITE_A_RECORD PROC NEAR
               ;--- Make sure attribute of

                ;    filename is a directory
               CMP  disk_attribute, 10h
               JNE  exit_write

<N>              ;--- Make sure the filename is

                ;    not . or ..
               CMP  BYTE PTR disk_filename, '.'
               JE   exit_write

<N>              ;--- Write 1 blank for 

                ;    delete flag
               MOV  BX, handle
               MOV  DX, OFFSET blanks
               MOV  CX, 1
               MOV  AH, 40h
               INT  21h

<N>              ;--- Search for null left by

               ;    DOS after the filename
               MOV  AX, DS
               MOV  ES, AX
               CLD
               MOV  DI, OFFSET disk_filename
               MOV  CX, 13
               MOV  AL, 0
         REPNE SCASB
               DEC  DI
               CMP  BYTE PTR ES:[DI], 0
               JNE   no_nulls

               ;--- Replace null and remaining

               ;    characters with spaces

               INC  CX
fill_spaces:   MOV  BYTE PTR [DI], ' '
               INC  DI
               SUB  CX, 1
               JNZ  fill_spaces

no_nulls:      ;--- Write the filename
               MOV  BX, handle
               MOV  DX, OFFSET disk_filename
               MOV  CX, 12
               MOV  AH, 40h
               INT  21h

<N>              ;--- Write blanks to fill in

                ;    rest of record
               MOV  BX, handle
               MOV  DX, OFFSET blanks
               MOV  CX, 27
               MOV  AH, 40h
               INT  21h

<N>              ;--- Add 1 to new reccount
               ADD  WORD PTR reccount, 1
               ADC  WORD PTR reccount+2, 0
exit_write:    RET
WRITE_A_RECORD ENDP

;--------- Beginning of data

table1        DW  05F5h
              DW  0098h
              DW  000Fh
              DW  0001h
              DW  0000h
              DW  0000h
              DW  0000h
              DW  0000h
              DW  0000h

table2        DW  0E100h
              DW  9680h
              DW  4240h
              DW  86A0h

              DW  2710h
              DW  03E8h
              DW  0064h
              DW  000Ah
              DW  0001h

                      DB  "BEGDATA"
dbfname               DB  65 DUP (0)
null_terminator       DB  0
pathname              DB  65 DUP (0)
filespec              DB  "*.*", 0
handle                DW  0

reccount              DD  0
head_size             DW  0
dta_hi                DW  0
dta_lo                DW  0
disk_reserved         DB  21 DUP (0)
disk_attribute        DB  1
disk_time             DW  0
disk_date             DW  0

disk_filesize         DB  4 DUP (0)
disk_filename         DB  14 DUP (0)
blanks                DB  19 DUP (20h)
fsize_string          DB  9 DUP (20h)
eof                   DB  26
                      DB  20h, 20h                     
date_string           DB  "  -  -  "
                      DB  20h, 20h
time_string           DB  "  :    "
leading_zero          DW  0
full_path             DB  "\"
old_path              DB  65 DUP (0)
                      DB  "ENDDATA"

;---------End of data

code          ENDS


              END       start




5  Figure 3--File Structures

Figure 3--File Structures

Structure for database: C:DIRNAMES.dbf
Number of data records:       0
Date of last update   : 11/15/88
Field  Field Name  Type       Width    Dec
    1  FILENAME    Character     12
    2  FILESIZE    Character      9
    3  FILEDATE    Character     10
    4  FILETIME    Character      8
    5  PATH        Character     65
** Total **                     105

Structure for database: C:COLLECT.dbf
Number of data records:       0
Date of last update   : 11/21/88
Field  Field Name  Type       Width    Dec
    1  FILENAME    Character     12
    2  FILESIZE    Character      9
    3  FILEDATE    Character     10
    4  FILETIME    Character      8
** Total **                      40



6  Figure 4--BuildDir.PRG

Figure 4--BuildDir.PRG

*--- BuildDir.prg


LOAD FTree
CLOSE DATABASES
SET SAFETY OFF
SET TALK OFF

SELECT 1
USE Dirnames
ZAP
APPEND BLANK
REPLACE Filename WITH "\"

all_done = .f.
pointer = 1
DO WHILE .NOT. all_done
   IF pointer <= RECCOUNT()
      GO pointer
      next_path = TRIM(Dirnames->Path) + TRIM(Dirnames->Filename)
      ? "record", RECNO(), next_path
      pointer = pointer + 1

<N>  ELSE
      all_done = .t.
      LOOP
   ENDIF

   oldcount = RECCOUNT()


   SELECT 2
   USE Collect
   ZAP
   USE


   CALL FTree WITH "Collect.DBF," + next_path
   SELECT 1
   APPEND FROM Collect



   IF RECCOUNT() > oldcount
      GO oldcount + 1
      REPLACE REST Path WITH next_path + IIF( "\"=next_path, "", "\")
   ENDIF


ENDDO


CLOSE DATABASES

RELEASE MODULE FTree


SET SAFETY ON
SET TALK ON


RETURN
* EOP: BuildDir 



7  Figure 5--DReport.PRG

Figure 5--DReport.PRG

*--- DReport.PRG

LOAD FDir
CLOSE DATABASES

SET SAFETY OFF
SET TALK OFF

SELECT 1
USE Dirnames
INDEX ON Path - Filename TO Dirnames
SET PRINT ON

clust_size = 2048

DO WHILE .NOT. EOF()
        subdirctry = TRIM(Dirnames->Path) + TRIM(Dirnames->Filename)
        ? "Directory: " + subdirctry

        SELECT 2
        USE Collect
        ZAP
        USE

        x = "Collect.DBF,*.*," + subdirctry
        CALL FDir WITH x

        USE Collect
        INDEX ON IIF( AT(".",Filename)=0, "   ", ;
           SUBSTR(Filename,AT(".",Filename)+1,3)) TO Collect
        GO TOP
        gtotal = 0

        DO WHILE .NOT. EOF()
                run_total = 0
                filetype = IIF( AT(".",Collect->Filename)=0, ;
                  "   ", SUBSTR(Collect->Filename, ;
                  AT(".",Collect->Filename)+1,3))

                DO WHILE filetype = IIF( AT(".",Collect->Filename)=0, 
;
                  "   ", SUBSTR(Collect->Filename,
AT(".",Collect->Filename)+1,3))


                        fsize = VAL(Collect->Filesize) / clust_size
                        IF INT(fsize) <> fsize
                                fsize = INT(fsize) + 1
                        ELSE
                                fsize = INT(fsize)
                        ENDIF
                        run_total = run_total + (fsize * clust_size)
                        SKIP
                ENDDO
                ? "   *." + filetype
                ?? STR( run_total, 10) + " bytes"
                gtotal = gtotal + run_total
        ENDDO

        IF gtotal > 0
                ? "Total   " + STR( gtotal, 10) + " bytes"
        ENDIF
        SELECT 1
        SKIP
        ?
ENDDO

SET PRINT OFF
CLOSE DATABASES
ERASE FILE Collect.NDX
ERASE FILE DirNames.NDX


RELEASE MODULE FDir
RETURN
* EOP: DReport 

                                