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.