[comp.os.msdos.programmer] ANSWER: How do I open more than 20 files under DOS

hagins@gamecock.rtp.dg.com (Jody Hagins) (03/20/91)

How do I open more than 20 files under DOS (3.2 and below)?


-----  Sorry if this gets to you twice, but it never got back to my
-----  site, so I am reposting, assuming that it never got out.


I have seen this question posted many times.  Hopefully this will help
clear things up.  The following code, and text, is taken without 
permission from "Systems Programming in Turbo C" by Michael J. Young,
published by SYBEX.  The book is fairly well written on other topics
as well.  It is not the best book on the market, but serves as a
good reference in my library.  Text inside brackets, [], is placed
there by me.



[ There are many more functions in the file management section
of this book.  For this reason, he uses error code.  I have removed
the error code stuff from this code.  Also, he includes several
header files, but only one/two are needed for these routines.  I'll
let you figure out which ones you don't need. ]


#include <stdio.h>
#include <dos.h>
#include <fcntl.h>
#include <io.h>
#include <ctype.h>
#include <process.h>
#include <stdlib.h>
#include <sys/stat.h>



int FilOpen(char *Path, int Access, int Permission)
/*
	This function returns a file number that must be passed by the
	application to 'FilHandle' immediately prior to calling a
	standard low-level C file handle function.  If an error is
	encountered, it returns -1.
*/
{
unsigned char far *FtPtr;	/* Far pointer to the PSP file table */
				/* entry for handle 5.               */

FtPtr = MK_FP(_psp, 0x1d);	/* Assign file table address         */
*FtPtr = 0xff;			/* Mark this handle free.            */
if (open(Path, Access, Permission) == -1)
    return(-1);
return(*FtPtr);			/* Return the internal DOS file      */
				/* number to be used to refer to the */
				/* file in subsequent functions.     */
}



int FilHandle(int FileNumber)
/*
	This function takes hte 'FileNumber' that was passed to the
	application by 'FilOpen' and places it in the DOS file table
	entry for handle 5, so that the next low-level function call
	that is passed handle 5 will access the appropriate file.
	For convenience, this function returns the file ahndle value of
	5 so that the function call can be used as a parameter to the 
	C file handle function
*/
{
unsigned char far *FtPtr;	/* Far pointer to the PSP file table */
				/* entry for handle 5.               */

FtPtr = MK_FP(_psp, 0x1d);	/* Assign file table address.        */
*FtPtr = FileNumber;		/* Place file number in file table   */
return(5);
}


===========================
Using FilOpen and FilHandle

	One of the most notorious limitations of MS-DOS versions through
3.2 is that a program cannot have more than 20 file handles open simul-
taneously. (DOS interrupt 21h, function 67h, removes this limitation,
but is available only beginning with version 3.3).  However, by using
the two functions FilOpen and FilHandle, your programs can open up to
255 handles at the same time, under all versions of DOS beginning with
2.0.  As these two functions are implemented, they allow you to use the
low-level C file functions, such as read or write, but not the high-
level stream functions such as fprintf or fwrite.  It is a simple matter
however, to convert these functions for use with stream file functions,
and [now get this] the task is left as an exercise for the reader.  The
following are the required steps for using these functions to circumvent
the MS-DOS limit of 20 file handles.
	The first step is to specify the desired number of handles in
the config.sys file.  For example, if you want to be able to open 31
handles simultaneously, place the following line in config.sys:

		FILES=31

	Second, open each file by calling the function FilOpen, saving
the integer that is returned.  This integer will be called a 
<file number> (to distinguish it from a file handle), and must be used
to perform any further operations with the file, as shown below.  You
may open up to the total number of files specified in the configuration
file (the maximum is 255); however, if you are using FilOpen, DO NOT USE
ANY OTHER FILE OPENING FUNCTIONS, such as open, _open, creat, _creat, or
fopen.  The prototype for FilOpen is

		int FilOpen(char *Path, int Access, int Permission)

The three parameters are the same three that are passed to the standard
C function open.  <Permission> is required to specify a file permission
only if O_CREAT is included in <Access>l if <Permission> is not needed,
you may simply pass in a dummy value such as 0 to prevent the compiler
from complaining.  If FilOpen encounters an error, it returns -1,
otherwise it returns the file number.  The following example opens a
single file and saves the file number:

	int FileNumber;
	FileNumber = FilOpen(FileName,O_RDWR|O_CREAT,S_IREAD|S_IWRITE);

Or, if you want to check for an error, use the following expression:

	if ((FileNumber = FilOpen(FileName,
                                  O_RDWR|O_CREAT,
                                  S_IREAD|S_IWRITE)) == -1)
	    /* Call error routine */

	The third step is to freely use normal low-level C file 
functions such as read, write, tell, lseek, and close.  However, rather
than passing a file handle to these functions (remember you haven't
saved any file handles), you must first call FilHandle, passing it the
file number for the appropriate file, and then pass the returned value
to the C function, such as:

	write(FilHandle(FileNumber), Buffer, BufferSize);

where the expression FilHandle(FileNumber) is used in place of a
normal file handle.
	The program that follows is an example of opening 26 files.


#include <stdio.h>
#include <sys/stat.h>
#include <io.h>
#include <fcntl.h>
#include <process.h>

main()
{
int		file_numbers[26];
static char	file_name[6] = "TESTx";
int		i;

for (i=0; i<26; i++)
    {
    file_name[4] = 'A' + i;
    if ((file_number[i] = FilOpen(file_name, 
                                  O_RDWR | O_CREAT,
                                  S_IREAD | S_IWRITE)) == -1)
        {
        printf("Error opening files\n");
        exit(1);
        }
    }
		/* At this point, 31 files are open at the same time */
		/* 26 new ones + 5 defaults (0-4)                    */
for (i=0; i<26; i++)
    close(FilHandle(file_number[i]));
}



==============================
How FilOpen and FilHandle Work


	When MS-DOS loads a program, it sets up a file table in the
program segment prefix for that program (or PSP, a 100h-byte preamble
to your program containing a variety of important information).  Each
time you open a file, DOS places its own internal file number in the
first available entry in the file table.  DOS does not pass this number
to your program; rather, it passes your program an index into the file
table, which points to the actual file number.  This index is known
as a file handle, and since there is room for only 20 entries in the
file table, there can be only 20 file handles.  Basically, FilOpen and 
FilHandle work by using the actual DOS file numbers (of which there can
be up to 255), rather than the file handles (of which there can be only
20).
	FilOpen first initializes a far character pointer to point to
entry number 5 of the file table (that is, the entry corresponding to
handle 5).  Why entry 5?  Entries 0 through 4 are used by the standard
preopened file handles (standard input, output, error, auxiliary device,
and printer).  FilOpen now assigns a value of FFh to this file table 
position, since FFh is the value used by DOS to mark a free entry in
the table.  It next calls open, which requests that DOS open a file
and return a handle.  Since DOS always uses the first available position
in the file table, it is sure to place its internal number in position
5, and therefore return handle 5.  FilOpen is not interested in the
file handle (which it already knows will be 5); rather, it obtains the
internal DOS file number by reading directly from the file table, and
returns this value to the calling program.  The calling program now
saves this file number and uses it as explained above.
	The story continues when your program is about to perform a file
operation, and calls FilHandle.  The parameter passed to FilHandle is
the same internal DOS file number that was obtained by FilOpen.
FilHandle now takes this number and places it back in entry number 5 of
the file table (again, using a far pointer).  Therefore, when DOS 
performs the next operation of file handle 5, it will go to entry 5 of
the file table and find the internal file number for the file.
FilHandle then returns 5 for the convenience of the calling program,
which can simply substitute the call to FilHandle for a file handle
parameter parameter to a C file function.
	If you have followed the above strategy, which is not as complex
as it seems, you can see that this method allows opening up to 255
files simultaneously using only one file handle!




[ I hope this helps.  As you can see, the author goes to great lengths
to make everything very simple, and very easy to understand.  He treats
all his routines in this way.  If you find these routines useful, I
suggest that you invest in the book since it contains quite a few useful
tips and routines.  It can be found in almost any bookstore, and is
fairly cheap.  Disclaimer: I am not affiliated with the book, author,
or publishing company in any way.  The only affiliation I have with
the book is that I own a copy. 

Please let me know if this helps you out! ]


-- 

Jody Hagins             
hagins@gamecock.rtp.dg.com    
Data General Corp.      
62 Alexander Dr.        
RTP, N.C.  27709        
(919) 248-6035          

Nothing I ever say reflects the opinions of DGC.