[comp.sys.misc] Seeking a method to "read" a DOS directory

jrm@cblpe.ATT.COM (John Miller) (02/11/88)

Hi, in a UNIX environment (using the 'C' language), it is possible to "open"
a directory. The result is a list of the files that are in the particular
directory - very straightforward.

I would like obtain a list of files that are in a DOS directory using
Microsoft 'C', version 4 or 5. Near as I can tell, you are not permitted
to "open" a directory in dos. Further, I have not been able to find a
function in the MSC library, or a function in the DOS or BIOS library
that will permit me to find out what files are in a given directory.

This seems like a gross oversight on Microsoft's part.

The best method I know of so far (and this is more of an attack plan than
a sure and tried solution) - is to use a DOS interrupt function to gain
information about the FAT. From that information, I should be able to
calculate the location, on disk, of the beginning sector containing
the DOS directory table. Once in the table, I should be able to traverse
through the chains to find all the files that are in the directory I
am interested in.

Yuuck! There has to be a better way! I could see where, after weeks of work,
I could make this scheme work - for all disk types and formats. The price
is very high!

Does anyone know of a better way? More direct? I would be more that willing
to attach an assembler routine to the 'C' product I am building (I have
2 already part of the product). But from what I can tell, there is nothing
available anywhere, at any level that can help - have I overlooked something?

-- 
               J.R. Miller, AT&T Bell Labs, Columbus, OH
                      CB 1C-339 (614) 860-4314
			ihnp4!cblpe!jrm

psfales@ihlpe.ATT.COM (Pete Fales) (02/12/88)

In article <902@cblpe.ATT.COM>, jrm@cblpe.ATT.COM (John Miller) writes:
> I would like obtain a list of files that are in a DOS directory using
> Microsoft 'C', version 4 or 5. Near as I can tell, you are not permitted
> to "open" a directory in dos. Further, I have not been able to find a
> function in the MSC library, or a function in the DOS or BIOS library
> that will permit me to find out what files are in a given directory.
> 
> The best method I know of so far (and this is more of an attack plan than
> a sure and tried solution) - is to use a DOS interrupt function to gain
> information about the FAT. From that information, I should be able to
> calculate the location, on disk, of the beginning sector containing
> the DOS directory table. Once in the table, I should be able to traverse
> through the chains to find all the files that are in the directory I
> am interested in.

There is a better way!  Some compilers have library routines to do this
(my Ecosoft compiler, for example) but that doesn't help you much if
yours doesn't.  Take a look at the DOS functions "find first" and "find
next."  These functions take a complete path name (including wild cards)
and return all files that match the template.  If you give it a path
name terminating in *.*, it will find all the files in the directory.
There are bits you can set in the paramter block to include/exclude
subdirectories and hidden or system files.  

This should do what you want in a much more device independent manner.

Pete
-- 
Peter Fales		UUCP:	...ihnp4!ihlpe!psfales
			work:	(312) 979-7784
				AT&T Information Systems, IW 1Z-243
				1100 E. Warrenville Rd., IL 60566

mouse@mcgill-vision.UUCP (der Mouse) (03/14/88)

In article <902@cblpe.ATT.COM>, jrm@cblpe.ATT.COM (John Miller) writes:
> I would like obtain a list of files that are in a DOS directory using
> Microsoft 'C', version 4 or 5.

> The best method I know of so far [...] is to use a DOS interrupt
> function to gain information about the FAT.  [and then read the disk
> to find the directory]

One of the DOS interrupts expands a wildcard name.  Tell it to expand
*.* and you should get a list of everything.  (Actually, one entry
tells it "get the first name matching this" and another says "get the
next name matching the last thing I told you to look for", as I
recall.)  Unfortunately, the match routine is rather badly broken.
Asking for *C.*, for example, will return the same thing as *.*,
regardless of whether the names in the directory contain `C'.  Or at
least it did for me.  But for what you are doing, it should work to
just give *.*.

					der Mouse

			uucp: mouse@mcgill-vision.uucp
			arpa: mouse@larry.mcrcim.mcgill.edu

jep@oink.UUCP (James E. Prior) (03/26/88)

In article <1001@mcgill-vision.UUCP> mouse@mcgill-vision.UUCP (der Mouse) writes:
>In article <902@cblpe.ATT.COM>, jrm@cblpe.ATT.COM (John Miller) writes:
>> I would like obtain a list of files that are in a DOS directory using
>> Microsoft 'C', version 4 or 5.

Sample code is included further down in this article.

I don't remember whether it was for Turbo C or usoft C.  It's very
similar either way.

>> The best method I know of so far [...] is to use a DOS interrupt
>> function to gain information about the FAT.  [and then read the disk
>> to find the directory]
>
>One of the DOS interrupts expands a wildcard name.  Tell it to expand
>*.* and you should get a list of everything.  (Actually, one entry
>tells it "get the first name matching this" and another says "get the
>next name matching the last thing I told you to look for", as I
>recall.)  Unfortunately, the match routine is rather badly broken.
>Asking for *C.*, for example, will return the same thing as *.*,
>regardless of whether the names in the directory contain `C'.  Or at
>least it did for me.  But for what you are doing, it should work to
>just give *.*.

MS-DOS (and its predecessor CP/M) treat wildcard characters differently
than UNIX.  This has bitten many a programmer learning one after the 
other.  MS-DOS and CP/M filenames have a first name and a last name.  
The first name can be up to eight characters.  The last name can be
up to three characters long.  The dot is always present whether you
like it or not.  i.e. writing to file "abcd" will be the same as file
"abcd." .  An asterisk will match any character (including void chars)
from its position in a name until the end of that name.  The first name
is treated separately from the last name in this matter.

Here's the code a friend gave me to do wildcard searches on a PC.  
I've never used it, so don't ask me anything about it.  Instead ask
Steve Crawford {ihnp4|osu-cis}!n8emr!oink!snc about it.

File readme.jim: ***********************************************************
Hey,

find_first and find_next are in file find3.asm

dofind.c is a little test program I wrote to see that it does
indeed work.  

Note in the beginning of find3.asm the db string called "fcb".
This is where the search pattern is defined; the ????????ASM matching
all files of type .ASM.  You must change this to be whatever you want.
There's probably a better way to do search patterns (like passing a search
string to the find_ routines), but this is exactly what I needed for my
purposes.  If either find_first or find_next don't find anything, they
return a 0.

The find_ routines were written for assembly under Microsoft Macro
Assembler V4.0 (or something like that).  The C program is for msc.
Enjoy, and remember me in your will.  Thanks for your help!!!

c'bag
File dofind.c: *************************************************************
#include <stdio.h>
#include <string.h>

char *fnames[20];

main()
{
	int i;
	char *ff,*sf;			/* storage for file names */
	char *malloc(),*find_first(), *find_next();

	sf=malloc(13);
	ff=find_first();
	strcpy(sf,ff);
	fnames[0]=sf;
	 
	for(i=1; (ff=find_next()); i++) {
	  sf=malloc(13);		/* get pointer to string */
	  strcpy(sf,ff);		/* copy file name */
	  fnames[i]=sf;			/* store pointer */
	}

	for(i=0; (fnames[i]); i++) {
	  printf("file %d = %s\n", i, fnames[i]);
	}
}
File find3.asm: **************************************************************
;
; find3.asm - find a file in the current directory
; 1/29/88 snc
;
; This is the third attempt.  This routine is callable from C, fills
; an external array with the found names, and returns a pointer to the
; last array element filled.
;
	DGROUP	GROUP _DATA
	ASSUME	DS:DGROUP
	_DATA	SEGMENT WORD PUBLIC 'DATA'
	sf	db	"NOT FOUND ON INT 11.$"
	no	db	"FILE DOES NOT EXIST.$"
	fcb	db	0,"????????ASM"
		db	25 dup(?)
	buffer	db	128 dup(?)
	fbuf	db	12 dup(?)		;file name buffer
	_DATA	ENDS

	P_ENTER	MACRO
		PUSH	BP
		MOV	BP,SP
		push	si
		push	di
		PUSH	ES
		push	ds
		mov	ax,DGROUP
		mov	ds,ax
		ENDM

	P_EXIT	MACRO
		pop	ds
		POP	ES
		pop	di
		pop	si
		mov	sp,bp
		POP	BP
		ENDM

	_TEXT	SEGMENT BYTE PUBLIC 'CODE'
	ASSUME	CS:_TEXT
	PUBLIC	_find_first, _find_next

	_find_first	PROC NEAR
	P_ENTER

	lea	dx,buffer		;create a DTA
	mov	ah,1ah			;set DTA function
	int	21h

	lea	dx,fcb			;do the search
	mov	ah,11h
	int	21h

	cmp	al,0ffh			;find it?
	jz	short not_found		;exit if not
	call	output			;else, fix up file name
	lea	ax,fbuf
	jmp	short done

not_found:
	xor	ax,ax			;if no match, return 0
done:
	P_EXIT
	ret
	_find_first	ENDP


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	_find_next	PROC NEAR
	P_ENTER

	lea	dx,fcb
	mov	ah,12h		;get next file
	int	21h
	cmp	al,0ffh		;no more files?
	jz	short no_more	;exit routine if so
	call	output

	lea	ax,fbuf
	jmp	short go_back
no_more:
	xor	ax,ax		;if no files, return 0
go_back:
	P_EXIT
	ret
	_find_next	ENDP

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	output	PROC NEAR

	mov	si,1
	lea	bx,buffer
	lea	di,fbuf
outchar:
	mov	dl,[bx][si]
	cmp	dl,20h		;blank space?
	jz	short skip	;skip it if so
	mov	[di],dl		;store character
	inc	di
skip:
	inc	si
	cmp	si,9		;9 is 8 characters later from starting si
	jz	short period
	jmp	short outchar
period:
	mov	dl,2eh		;a period
	mov	[di],dl		;store character
	inc	di
outext:
	mov	dl,[bx][si]
	mov	[di],dl		;store character
	inc	di
	inc	si
	cmp	si,12
	jnz	short outext

	mov	[di],byte ptr 00	;null terminate string
	ret
	output	ENDP

	_TEXT	ENDS
	END
End of files ****************************************************************

Remember to thank/flame Steve Crawford for the code.

-- 
Jim Prior    {ihnp4|osu-cis}!n8emr!oink!jep    jep@oink.UUCP

Pointers are my friend.

jwg@duke.cs.duke.edu (Jeffrey William Gillette) (03/28/88)

[]

	Actually, reading a DOS directory is a bit more involved than
simply setting up a set of Find First / Find Next calls (int 21h, AH =
4Eh / 4Fh).

	Whoever set up the Find First / Find Next interface (perhaps a
disgruntled employee in his last week at Microsoft :-) decided that the
logical portion of memory to exchange directory information was the Disk
Transfer Area (DTA - starts out as bytes 80h - FFh in the Program Segment
Prefix).  Since your compiler may well change the location of the DTA, you
will need to get the current Disk Transfer Address (int 21h, AH = 2Fh) and
declare a data structure like "FCB far *fcb = dta_address".  FCB is the
File Control Block which DOS will set up, but expects *you* to keep track
of!

	If you intend to do any disk activity while searching the
directory, you will need to assume responsibility for the integrity of
the FCB buffer.  You will need either to preserve a copy of the FCB after
each Find First / Find Next call (and place a current image of the FCB in
the DTA immediately before the next call), or you will need to change the
DTA temporarily every time you make a Find First / Find Next call.

	N.B.  Perhaps this mess is the reason Microsoft didn't bother 
including a reasonable directory search routine in Windows 1.x.  Given
the obvious dangers in playing with the Disk Transfer Address in a quasi-
multitasking environment, I sent a TAR to Microsoft.  The response translated
to, "We're going to bury our heads in the sand until the problem goes away, 
or until we can't ignore it any longer."

Peace,

Jeff

Jeffrey William Gillette	uucp:   jwg at duke.edu
Duke University                 bitnet: DYBBUK at TUCCVM



	

-- 
Jeffrey William Gillette	uucp:   jwg at duke.edu
Duke University                 bitnet: DYBBUK at TUCCVM

leonard@bucket.UUCP (Leonard Erickson) (03/28/88)

In article <1001@mcgill-vision.UUCP> mouse@mcgill-vision.UUCP (der Mouse) writes:
<recall.)  Unfortunately, the match routine is rather badly broken.
<Asking for *C.*, for example, will return the same thing as *.*,
<regardless of whether the names in the directory contain `C'.  Or at

It is behaving correctly. MS-DOS wildcards are not like UNIX wildcards.
"*" is translated as "fill rest of [filename,extension] with "?". So 
*C* becomes ???????. 

anything after a * is ignored...

-- 
Leonard Erickson		...!tektronix!reed!percival!bucket!leonard
CIS: [70465,203]
"I used to be a hacker. Now I'm a 'microcomputer specialist'.
You know... I'd rather be a hacker."

des@uucsbb.UUCP (Don Shope) (04/01/88)

In article <902@cblpe.ATT.COM>, jrm@cblpe.ATT.COM (John Miller) writes:
> I would like obtain a list of files that are in a DOS directory using
> Microsoft 'C', version 4 or 5.
>
> The best method I know of so far [...] is to use a DOS interrupt
> function to gain information about the FAT.  [and then read the disk
> to find the directory]
>

Following is a little program I wrote to illustrate the use of
two new functions in level 5.0 of the Microsoft C compiler:

/*  Directory functions. The second argument to _dos_findfirst()
 *  specifies the necessary attributes needed for a file to be
 *  selected (whose name also matches the pattern). Values
 *  for attribute are: _A_NORMAL, _A_RDONLY, _A_HIDDEN, _A_SYSTEM,
 *  _A_VOLID, _A_SUBDIR, _A_ARCH (which can be or'd together). 
 *  Return value is 0 upon success, errno is set on failure to ENOENT.
 *
 *  Following is the function prototype and struct definition:
 *
 *  unsigned _dos_findfirst(path,attributes,buffer);
 *  unsigned _dos_findnext(buffer);
 *  char *path;               - target filename (* and ? allowed)  
 *  unsigned attributes;      - target attributes  
 *  struct find_t { 
 *        char reserved[21];  - reserved for use by MS-DOS  
 *        char attrib;        - attribute byte for matched path  
 *        unsigned wr_time;   - Time of last write to file  
 *        unsigned wr_date;   - Date of last write to file  
 *        long size;          - Length of file in bytes  
 *        char name[13];      - filename/directory name  
 *        } *buffer;
 */

#include <dos.h>
main(argc, argv)
int argc;
char *argv[];
{
   struct find_t dosfile;
   char pattern[81];

   if( argc != 2 ) {
      printf( "Enter DOS search pattern: " );
      scanf( "%s", pattern );
      }
   else
      strcpy( pattern, argv[1] );

   printf( "Directory listing of '%s':\n\n", pattern );

   if( _dos_findfirst( pattern, _A_NORMAL, &dosfile ) == 0 ) {
      printf( "Size: %6ld       File: %s\n", dosfile.size, dosfile.name );
      while( _dos_findnext( &dosfile ) == 0 )
         printf( "Size: %6ld       File: %s\n", dosfile.size, dosfile.name );
      }
   else
      printf( "Pattern '%s' not found.\n", pattern );
}

I hope this helps you out. Also, I am a new user, and I hope I have
done things correctly in this posting.