           DIRECTORY SEARCHING IN C WITH THE IBM PC AND DOS

                            Matthew Probert
                           Servile  Software


Amongst the many functions provided by DOS for programmers, are a pair 
of functions; "Find first" and "Find next" which are used to search a 
DOS directory for a specified file name or names. The first function, 
"Find first" is accessed via DOS interrupt 21, function 4E. It takes 
an ascii string file specification, which can include wildcards, and 
the required attribute for files to match. Upon return the function 
fills the disk transfer area (DTA) with details of the located file 
and returns with the carry flag clear. If an error occurs, such as no 
matching files are located, the function returns with the carry flag 
set. 

Following a successful call to "Find first", a program can call "Find 
next", DOS interrupt 21, function 4F, to locate the next file matching 
the specifications provided by the initial call to "Find first". If 
this function succeeds, then the DTA is filled in with details of the 
next matching file, and the function returns with the carry flag 
clear. Otherwise a return is made with the carry flag set. 

Most C compilers for the IBM PC provide non standard library functions 
for accessing these two functions. Turbo C provides "findfirst()" and 
"findnext()". Making use of the supplied library functions shields the 
programmer from the messy task of worrying about the DTA. Microsoft C 
programmers should substitue findfirst() with _dos_findfirst() and 
findnext() with _dos_findnext(). 

The following Turbo C example imitates the DOS directory command, in a 
basic form; 

#include <stdio.h>
#include <dir.h>
#include <dos.h>

void main(void)
{
    /* Display directory listing of current directory */

    int done;
    int day;
    int month;
    int year;
    int hour;
    int min;
    char amflag;
    struct ffblk ffblk;
    struct fcb fcb;

    /* First display sub directory entries */
    done = findfirst("*.",&ffblk,16);

    while (!done)
    {
        year = (ffblk.ff_fdate >> 9) + 80;
        month = (ffblk.ff_fdate >> 5) & 0x0f;
        day = ffblk.ff_fdate & 0x1f;
        hour = (ffblk.ff_ftime >> 11);
        min = (ffblk.ff_ftime >> 5) & 63;

        amflag = 'a';

        if (hour > 12)
        {
            hour -= 12;
            amflag = 'p';
        }

        printf("%-11.11s  <DIR>     %02d-%02d-%02d  %2d:%02d%c\n",
               ffblk.ff_name,day,month,year,hour,min,amflag);
        done = findnext(&ffblk);
    }

    /* Now all files except directories */
    done = findfirst("*.*",&ffblk,231);

    while (!done)
    {
        year = (ffblk.ff_fdate >> 9) + 80;
        month = (ffblk.ff_fdate >> 5) & 0x0f;
        day = ffblk.ff_fdate & 0x1f;
        hour = (ffblk.ff_ftime >> 11);
        min = (ffblk.ff_ftime >> 5) & 63;

        amflag = 'a';

        if (hour > 12)
        {
            hour -= 12;
            amflag = 'p';
        }

        parsfnm(ffblk.ff_name,&fcb,1);

        printf("%-8.8s %-3.3s %8ld  %02d-%02d-%02d  %2d:%02d%c\n",
                fcb.fcb_name,fcb.fcb_ext,ffblk.ff_fsize,
                day,month,year,hour,min,amflag);
        done = findnext(&ffblk);
    }
}

The function "parsfnm()" is a Turbo C library command which makes use 
of the DOS function for parsing an ascii string containing a file 
name, into its component parts. These component parts are then put 
into a DOS file control block (fcb), from where they may be easily 
retrieved for displaying by printf(). 

The DOS DTA is comprised as follows;

Offset  Length  Contents

00       15      Reserved
15       Byte    Attribute of matched file
16       Word    File time
18       Word    File date
1A       04      File size
1E       0D      File name and extension as ascii string

The file time word contains the time at which the file was last 
written to disc and is comprised as follows; 

Bits      Contents

 0 -  4   Seconds divided by 2
 5 - 10   Minutes
11 - 15   Hours

The file date word holds the date on which the file was last written 
to disc and is comprised of; 

Bits      Contents

 0 -  4   Day
 5 -  8   Month
 9 - 15   Years since 1980

To extract these details from the DTA requires a little manipulation, 
as illustrated in the above example. 

The DTA attribute flag is comprised of the following bits being set or 
not; 

Bit     Attribute

0       Read only
1       Hidden
2       System
3       Volume label
4       Directory
5       Archive

