PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 1/13 TITLE : EXTENDED FILE I/O LIBRARY The following is public domain information that has been uploaded to our Forum on CompuServe. As a courtesy to our users that do not have immediate access to CompuServe, Technical Support distributes these routines free of charge. However, because these routines are public domain programs, not developed by Borland International, we are unable to provide any technical support or assistance using these routines. If you need assistance using these routines, or are experiencing difficulties, we recommend that you log onto CompuServe and request assistance from the Forum members that developed these routines. Written by Randy Forgaard, CompuServe 70307,521 Translated to Turbo C by Charles Jazdzewski Many thanks to Bela Lubkin, (CompuServe 76703,3015) for masterminding this idea, and Kim Kokkonen, (CompuServe 72457,2131) for helping me debug it. For more discussion of Handle Tables and the implementation of DOS redirection, please see Stan Mitchell, "Command Line Redirection," PC Tech Journal, January 1986, Page 44. Due to a limitation of DOS, Turbo C, version 1.5 only allows up to 15 files at a time to be open. The following module allows you to have up to 96 files open simultaneously under DOS 2.0 or 2.1, or 252 files open simultaneously under DOS 3.0 or greater. This module, in its current form, CANNOT be used with streams nor networks. To use this module edit your CONFIG.SYS file (see the DOS manual for details), so that it includes a line that says "FILES=XXX". XXX should be a version that is 3 greater than the maximum number files you wish to open, and should not exceed 99 (for DOS 2.0/2.1) or 255 (for DOS 3.0 and higher). Under any version of DOS, the minimum allowable value for XXX is 8. Then, reboot your computer so that the FILES=XXX parameter takes hold. Running the sample program at the bottom of this file will tell you the maximum number of files you can open at once. (Usually 96 PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 2/13 TITLE : EXTENDED FILE I/O LIBRARY or 252, unless a resident program has opened some files and they have not been closed). THE TECHNICAL DETAILS: Much of the following information is not documented in the DOS Technical Reference manual. Under DOS 1.0 and 1.1, all files were accessed via File Control Blocks (FCB's). There was no limit to the number of FCB's that a program could use, so there was no limit to the number of files open simultaneously. Under DOS 2.0 and greater, an alternate (and preferable) method of accessing files was introduced which uses a 2-byte integer called a "handle" to refer to a file. A "handle" file is described using a data structure called a Device Control Block (DCB). However, DOS provides the storage space for all DCB's, rather than having the application program store the DCB's, so the number of available DCB's is limited to the amount of space that DOS has set aside for them. The maximum number of files/devices that can be open simultaneously is the number of slots available in the DCB Table created by DOS. The DCB's in the DCB Table are consecutively numbered, starting with 0. DCB's 0, 1, and 2 are predefined by DOS to correspond to the AUX, CON, and PRN devices, respectively. All remaining DCB's in the DCB Table are available for files or devices used by application programs. So that I/O redirection can be supported, the DCB numbers are not used directly when accessing files. Instead, a file "handle" is used. A "handle" is an index into a 20-byte array, called the Handle Table, which is located at offset 18H of the Program Segment Prefix (PSP) for a program (for a general discussion of the PSP, see the DOS Technical Reference manual). Each element of the Handle Table is the DCB number of a file or device. The value at index "handle" in the Handle Table is the DCB number of the file or device that implements that file handle. Thus, if the value 8 is in the 6th byte of the Handle Table, the handle "6" refers to the file (or device) described by the DCB in slot 8 of the DCB Table. If a handle is not currently being used, its entry in the Handle Table is FFH. DOS predefines the first 5 handles to be primary input, primary output, error, auxiliary, PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 3/13 TITLE : EXTENDED FILE I/O LIBRARY and printer, so the first 5 entries in the Handle Table are 1, 1, 1, 0, and 2, corresponding to the DCB numbers for the CON (1), AUX (0), and PRN (2) devices. This leaves only 15 available handles for opening files (or new devices). Every time a new handle file is opened, a new handle gets used. Since there are only 20 slots available in the Handle Table for a program, DOS only allows a "process" to have a maximum of 20 file handles in use simultaneously (and the first 5 entries are predefined, as just noted, unless those handles get closed and reused). Every new handle file requires a unique handle, so only 20 files/devices can be open at the same time by a single process (unless FCB's are used). (A "process" is any program spawned using the DOS EXEC function call. A process can be invoked by COMMAND.COM, or by another program.) There can be many more than 20 DCB's in the DCB Table, so the real limitation is in the size of the Handle Table in the PSP. The size of the DCB Table (i.e., the maximum number of files/devices that can be open simultaneously in the whole computer) is controlled by the FILES=XXX entry in the CONFIG.SYS file. The minimum number of slots is 8. Under DOS 2.0/2.1, the maximum number is 99, and under DOS 3.0 and higher, the maximum is 255. As previously mentioned, the first three of these DCB slots are occupied by the AUX, CON, and PRN devices. A single program can use all of the DCB's in the DCB Table (except for the 3 reserved by DOS) all on its own, by effectively bypassing the Handle Table in the PSP, except on a temporary basis. The program can accomplish this feat by using, say, only one entry in the Handle Table for all of its files. Instead of allowing DOS to store the DCB numbers in the Handle Table, the program can store these numbers elsewhere. Then, to manipulate a file using DOS, the program can temporarily put the DCB number of that file into a designated slot in the Handle Table, pass the index of that table slot (i.e., that "handle") to DOS, and DOS will operate on that handle/DCB number. In this way, DOS can be fooled into accessing up to 96 (or 252) different files/devices using a single handle entry in the Handle Table. To obtain the address of the Handle Table, which is at offset 18H in the PSP, the program needs to find the address of its PSP. To do this we use the DOS function call 62H, "Get Program PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 4/13 TITLE : EXTENDED FILE I/O LIBRARY Segment Prefix Address (PSP)." This function call is available in DOS 3.0 and higher. There is an identical function call in DOS 2.0/2.1, but its function number is 51H, and it is not documented. Function 51H is also available in DOS 3.0/3.1. However, for upward-compatibility reasons with future versions of DOS, we will use the undocumented 51H function with DOS 2.0/2.1 (since we know 51H is available in those versions of DOS), and use 62H for DOS 3.0 and higher (since 62H is a documented function). There is no such function call in DOS 1.0/1.1, but the technique below will not work with those early versions of DOS anyway, since they did not provide file handles. /* EIO.C: Extended IO object module ---------------------------- * This module bypasses the normal 20 open file per-process * limitation of DOS 2.0 and 3.0. These routines are only * limited by the number of files allotted for in the * "FILES" statement in the config.sys file. * ------------------------------------------------------------- */ #include #include #include #include #define UNUSEDHANDLE 0xFF /**************** Static Variables ***************/ /* HandleTable ------------------------------------- * Pointer to the program's file handle table *-------------------------------------------------- */ static unsigned char far *HandleTable = NULL; /* eioHandle ---------------------------------------- * The file handle that this package will use * -------------------------------------------------- */ static int eioHandle = 20; /**************** Internal Functions ****************/ /* GetHandleTableAddr() ------------------------------ * This function gets the process file handle table PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 5/13 TITLE : EXTENDED FILE I/O LIBRARY * location from DOS. * --------------------------------------------------- */ static unsigned char far *GetHandleTableAddr(void) { union REGS regs; /* Determine which DOS function to use to get the PSP * 0x51 in 2.x (undocumented) * 0x62 in 3.x (documented) */ switch( _osmajor ) { case 0: case 1: fputs("This program only works with DOS 2.0 and" " higher\n",stderr); exit(3); case 2: regs.h.ah = 0x51; break; default: regs.h.ah = 0x62; break; } int86(0x21,®s,®s); return (unsigned char far *) MK_FP(regs.x.bx, 0x18); } /************ Externally Assessable routines *************/ /* eopen() ------------------------------------------------ * Opens a file and return its extended file handle. * It replaces open(). * -------------------------------------------------------- */ int eopen(char *pathname, int access, int permiss) { int handle; int SaveDcb; PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 6/13 TITLE : EXTENDED FILE I/O LIBRARY /* Make sure we have the handle table's address */ if ( !HandleTable ) HandleTable = GetHandleTableAddr(); /* Physically open the file */ if ( (handle = open(pathname, access, permiss)) == -1 ) return -1; /* Save the data control block */ SaveDcb = HandleTable[handle]; /* If we don't have a handle to play with, use this one, * else make it free again */ if ( eioHandle > 19 ) eioHandle = handle; else HandleTable[handle] = UNUSEDHANDLE; return SaveDcb; } /* eclose() -------------------------------------------------- * This routine replaces close(). * ----------------------------------------------------------- */ int eclose(int handle) { HandleTable[eioHandle] = handle; return close(eioHandle); } /* eread() --------------------------------------------------- * Replaces read(). * ----------------------------------------------------------- */ int eread(int handle, void *buf, int nbyte) { HandleTable[eioHandle] = handle; return read(eioHandle, buf, nbyte); } /* ewrite() -------------------------------------------------- PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 7/13 TITLE : EXTENDED FILE I/O LIBRARY * Replaces write(). * ----------------------------------------------------------- */ int ewrite(int handle, void *buf, int nbyte) { HandleTable[eioHandle] = handle; return write(eioHandle, buf, nbyte); } /* elseek() -------------------------------------------------- * Replaces lseek(). * ----------------------------------------------------------- */ long elseek(int handle, long offset, int fromwhere) { HandleTable[eioHandle] = handle; return lseek(eioHandle, offset, fromwhere); } /* ecreat() -------------------------------------------------- * Replaces creat(). * ----------------------------------------------------------- */ int ecreat(char *filename, int permiss) { int handle; int SaveDcb; /* Make sure we have the handle table's address */ if ( !HandleTable ) HandleTable = GetHandleTableAddr(); /* Physically open the file */ if ( (handle = creat(filename, permiss)) == -1 ) return -1; /* Save the data control block */ SaveDcb = HandleTable[handle]; /* If we don't have a handle to play with, use this one, * else make it free again */ if ( eioHandle > 19 ) eioHandle = handle; PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 8/13 TITLE : EXTENDED FILE I/O LIBRARY else HandleTable[handle] = UNUSEDHANDLE; return SaveDcb; } /* ecreatnew() ----------------------------------------------- * Replaces creatnew(). * ----------------------------------------------------------- */ int ecreatnew(char *filename, int attrib) { int handle; int SaveDcb; /* Make sure we have the handle table's address */ if ( !HandleTable ) HandleTable = GetHandleTableAddr(); /* Physically open the file */ if ( (handle = creatnew(filename, attrib)) == -1 ) return -1; /* Save the data control block */ SaveDcb = HandleTable[handle]; /* If we don't have a handle to play with, use this one, * else make it free again */ if ( eioHandle > 19 ) eioHandle = handle; else HandleTable[handle] = UNUSEDHANDLE; return SaveDcb; } /* ecreattemp() ---------------------------------------------- * Replaces creattemp(). * ----------------------------------------------------------- */ int ecreattemp(char *filename, int attrib) { PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 9/13 TITLE : EXTENDED FILE I/O LIBRARY int handle; int SaveDcb; /* Make sure we have the handle table's address */ if ( !HandleTable ) HandleTable = GetHandleTableAddr(); /* Physically open the file */ if ( (handle = creattemp(filename, attrib)) == -1 ) return -1; /* Save the data control block */ SaveDcb = HandleTable[handle]; /* If we don't have a handle to play with, use this one, * else make it free again */ if ( eioHandle > 19 ) eioHandle = handle; else HandleTable[handle] = UNUSEDHANDLE; return SaveDcb; } /* eeof() ---------------------------------------------------- * Replaces eeof(). * ----------------------------------------------------------- */ int eeof(int handle) { HandleTable[eioHandle] = handle; return eof(eioHandle); } /* efilelength() -------------------------------------------- * Replaces filelength(). * ---------------------------------------------------------- */ long efilelength(int handle) { HandleTable[eioHandle] = handle; return filelength(eioHandle); PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 10/13 TITLE : EXTENDED FILE I/O LIBRARY } /* egetftime() ---------------------------------------------- * Replaces getftime(). * ---------------------------------------------------------- */ int egetftime(int handle, struct ftime *ftimep) { HandleTable[eioHandle] = handle; return getftime(eioHandle, ftimep); } /* esetftime() --------------------------------------------- * Replaces setftime(). * --------------------------------------------------------- */ int esetftime(int handle, struct ftime *ftimep) { HandleTable[eioHandle] = handle; return setftime(eioHandle, ftimep); } /* eioctl() ----------------------------------------------- * Replace ioctl(). * -------------------------------------------------------- */ int eioctl(int handle, int cmd, int *argdx, int argcx) { HandleTable[eioHandle] = handle; return ioctl(eioHandle, cmd, argdx, argcx); } /* eisatty() --------------------------------------------- * Replaces isatty(). * ------------------------------------------------------- */ int eisatty(int handle) { HandleTable[eioHandle] = handle; return isatty(eioHandle); } PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 11/13 TITLE : EXTENDED FILE I/O LIBRARY /* esetmode() -------------------------------------------- * Replaces setmode(). * ------------------------------------------------------- */ int esetmode(int handle, unsigned mode) { HandleTable[eioHandle] = handle; return setmode(eioHandle, mode); } /* etell() ----------------------------------------------- * Replaces tell(). * ------------------------------------------------------- */ long etell(int handle) { HandleTable[eioHandle] = handle; return tell(eioHandle); } /*---------------------------------------------------------- * EIO.H: Header file for the extended file system * This file should be include in any program * using the EIO module. *---------------------------------------------------------- */ #include int eopen(char *pathname, int access, ... ); int eclose(int handle); int eread(int handle, void *buf, int nbyte); int ewrite(int handle, void *buf, int nbytes); long elseek(int handle, long offset, int fromwhere); int ecreat(char *filename, int permiss); int ecreatnew(char *filename, int attrib); int ecreattemp(char *filename, int attrib); int eeof(int handle); long efilelength(int handle); int egetftime(int handle, struct ftime *ftimep); int esetftime(int handle, struct ftime *ftimep); PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 12/13 TITLE : EXTENDED FILE I/O LIBRARY int eioctl(int handle, int cmd, ... ); int eisatty(int handle); int esetmode(int handle, unsigned mode); long etell(int handle); /* Example program -- This program opens as many Text files as it can, until DOS runs out of room in its DCB Table. It then reports how many files were successfully opened, writes a line to each of them, then closes and erases each of them. Note: The value of the FILES=XXX parameter in the CONFIG.SYS file must be set to an appropriate value (see above). If you change the FILES=XXX value, be sure to reboot before running this program. This program takes a while to run, due to the heavy disk I/O, so running it on a hard disk (or, even better, a RAM disk) is recommended. Make sure that you are running the program in a subdirectory, so that you don't run up against the DOS limit on the number of allowable files in the root directory of a drive. */ #include #include #include #include #include #include "eio.h" #define MAXCOUNT 255 int eiofiles[MAXCOUNT]; char filename[MAXCOUNT][30]; int main(void) { int i; int count; printf("Opening file...\n"); for( i=0; ;i++) { strcpy(filename[i],".\\"); if ( (eiofiles[i] = ecreattemp(filename[i],0)) == -1) PRODUCT : TURBO C NUMBER : 418 VERSION : 1.0/1.5 OS : MS-DOS DATE : February 23, 1988 PAGE : 13/13 TITLE : EXTENDED FILE I/O LIBRARY break; printf("%s opened\n",filename[i]); } count = i; printf("Successfully opened %d files at the same time.\n" "Writing to each file...\n", count); for (i=0; i