home *** CD-ROM | disk | FTP | other *** search
-
-
-
-
-
-
-
- PRODUCT : Turbo Pascal NUMBER : 174
- VERSION : 3.01A
- OS : PC DOS
- DATE : August 1, 1986 PAGE : 1/9
-
- TITLE : Files Open Extend
-
-
-
-
- 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
- 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 Pascal version 3.0 only allows
- up to 15 files at a time to be open. The following method 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.
-
- TO USE THIS TECHNIQUE:
-
- You need the routines and global declarations below. Everywhere
- in your program that you Reset or Rewrite a file "f" for the
- first time, insert an "OpenExtend(f);" invocation immediately
- after the Reset or Rewrite. Each time your program calls one of
- Turbo's built-in routines (other than Assign) for handling files
- (e.g., Read, Write, Close, Seek, Reset, Rewrite, etc.), put an
- "UnExtend(f);" invocation immediately prior to the call, and a
- "ReExtend(f);" invocation immediately after the call. Do not
- insert UnExtend and ReExtend calls around the very first Reset or
- Rewrite that you use to initially open a file.
-
- At the top of your program, prior to the "program" statement, put
- the compiler directive {$F252}. (You may use a value smaller than
- 252 if you wish. Under DOS 2.0/2.1, values above 96 have
-
-
-
-
-
-
-
-
-
-
-
-
-
- PRODUCT : Turbo Pascal NUMBER : 174
- VERSION : 3.01A
- OS : PC DOS
- DATE : August 1, 1986 PAGE : 2/9
-
- TITLE : Files Open Extend
-
-
-
-
- no additional benefit. Each larger value for the {$F} directive
- uses 2 additional bytes in the program's global data space.) The
- value you specify for the {$F} directive is the maximum number of
- files you will be able to have open at the same time in your
- program.
-
- 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
- number that is 3 greater than the value you specified for the
- {$F} directive (larger values will provide no additional benefit
- with respect to your individual program), 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
- or 252, unless a resident program has opened some files and
- hasn't closed them yet.)
-
- 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.
-
-
-
-
-
-
-
-
-
-
-
-
-
- PRODUCT : Turbo Pascal NUMBER : 174
- VERSION : 3.01A
- OS : PC DOS
- DATE : August 1, 1986 PAGE : 3/9
-
- TITLE : Files Open Extend
-
-
-
-
- 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, 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
-
-
-
-
-
-
-
-
-
-
-
-
-
- PRODUCT : Turbo Pascal NUMBER : 174
- VERSION : 3.01A
- OS : PC DOS
- DATE : August 1, 1986 PAGE : 4/9
-
- TITLE : Files Open Extend
-
-
-
-
- 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. After the DOS call, the
- program can remove that DCB number from the designated Handle
- Table slot, freeing up that handle for use in another DOS call
- for another file. 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.
-
- The OpenExtend, UnExtend, and ReExtend routines below use this
- technique. OpenExtend(f) is used on a previously-opened file,
- "f." It removes f's DCB number from the Handle Table, and stores
- that DCB number in place of the file handle in Turbo's data
- structure for "f." UnExtend(f) copies the current DCB number (if
- any) in the last slot of the Handle Table to a safe place, copies
- the DCB number of "f" to that slot, and then puts the handle of
- that slot into the "handle" position of the data structure for
- "f," in preparation for its use by Turbo/DOS. ReExtend(f)
- replaces the file handle of "f" with the DCB number for "f" in
- the data structure for "f," and restores the previous value (if
- any) of the last slot in the Handle Table. Note that the last
- slot of the Handle Table was chosen totally arbitrarily. Any H
- Table entry can be used for fooling DOS, and (since we are saving
- the previous DCB number in that entry) it does not preclude also
- using that designated slot as a legitimate handle.
-
- 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.
- Normally, this is very easy: when DOS loads a .COM file, the
- address of the PSP is just CS:0000. Using CS:0000 in this manner
- would be viable as long as we compile the program to a .COM file
- and execute the .COM file. However, if we run the program in
- DOS, it still thinks of Turbo's PSP as being the "official" PSP
- for the running program, since DOS did not "see" Turbo invoke
- the program. Hence, CS:0000 is not the valid PSP address when the
- program is running in memory, since that is the address of the
- program's "fake" PSP rather than Turbo's PSP.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- PRODUCT : Turbo Pascal NUMBER : 174
- VERSION : 3.01A
- OS : PC DOS
- DATE : August 1, 1986 PAGE : 5/9
-
- TITLE : Files Open Extend
-
-
-
-
- To allow the program to work correctly both when running in
- memory and when run as a .COM file, we use the DOS function call
- 62H, "Get Program 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.
- To decide whether to use function 51H or 62H, we call DOS
- function 30H, "Get DOS Version Number," to determine which
- version of DOS is running. This strategy for obtaining the Handle
- Table address is implemented in the GetHandleTableAddr function,
- below, which gets called only once (the first time that
- OpenExtend is called).
-
- Note: This technique will not interfere with overlays in your
- program (since it only uses the Handle Table slot temporarily),
- provided that your program leaves at least one DCB available for
- use by the Turbo run-time library to read in overlay files.
-
- {$F252}
-
- const
- LastHandle = 19; {Highest-numbered handle}
- UnusedHandle = $FF; {DcbTable entry that denotes an unused
- handle}
- type
- HandleTable = array[0..LastHandle] of Byte;
- HandleTablePtr = ^HandleTable;
- const
- TablePtrOk: Boolean = false; {"True" iff TablePtr is
- initialized}
- var
- TablePtr: HandleTablePtr; {Points to Handle Table for this
- process}
- SaveDcb: Byte; {Temporary variable for a DCB number during a
- function
- call}
-
-
-
-
-
-
-
-
-
-
-
-
-
- PRODUCT : Turbo Pascal NUMBER : 174
- VERSION : 3.01A
- OS : PC DOS
- DATE : August 1, 1986 PAGE : 6/9
-
- TITLE : Files Open Extend
-
-
-
-
- {Internal routine. Returns the address of the Handle Table,
- which is at offset 18H in the PSP.}
-
- function GetHandleTableAddr: HandleTablePtr;
- var
- regs: record
- case Integer of
- 1: (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags:
- Integer);
- 2: (AL, AH, BL, BH, CL, CH, DL, DH: Byte)
- end;
-
- begin
- regs.AH := $30;
- MsDos(regs); {Get DOS version number}
- case regs.AL of
- 0: begin
- writeln('This program only works with DOS 2.0 and
- higher');
- Halt
- end;
- 2: regs.AH := $51; {Undocumented, but works with DOS 2.0/2.1
- (and 3.X)}
- else regs.AH := $62 {Works with DOS 3.0 and higher}
- end;
- MsDos(regs); {Get PSP address}
- GetHandleTableAddr := Ptr(regs.BX, $18)
- end {GetHandleTableAddr};
-
-
- {Causes "f" to become an "extended" file; i.e., to remain open
- without using up any file handles. The parameter "f" must be any
- Turbo file; e.g., a File, a File of Byte, a File of Foo, Text,
- etc. This routine should be called immediately after the Reset or
- Rewrite that is initially used to open "f." After "f" has become
- extended, it cannot be used by Turbo's built-in file routines
- until it has been unextended using UnExtend.}
-
- procedure OpenExtend (var f);
- var
- handle: Integer absolute f;
- begin
- if not TablePtrOk then
-
-
-
-
-
-
-
-
-
-
-
-
-
- PRODUCT : Turbo Pascal NUMBER : 174
- VERSION : 3.01A
- OS : PC DOS
- DATE : August 1, 1986 PAGE : 7/9
-
- TITLE : Files Open Extend
-
-
-
-
- begin
- TablePtr := GetHandleTableAddr;
- TablePtrOk := true
- end;
- SaveDcb := TablePtr^[handle];
- TablePtr^[handle] := UnusedHandle;
- handle := SaveDcb
- end {OpenExtend};
-
-
- {Unextends the extended file "f," so that it can be used by any
- of Turbo's built-in file routines. Note that "f" must have been
- converted to an extended file by OpenExtend before invoking
- UnExtend(f). After calling UnExtend, and then invoking the Turbo
- file function on "f," ReExtend(f) should be invoked immediately
- to re-extend "f" and restore the DOS file state information.}
-
- procedure UnExtend (var f);
- var
- handle: Integer absolute f;
- begin
- SaveDcb := TablePtr^[LastHandle];
- TablePtr^[LastHandle] := Lo(handle);
- handle := LastHandle
- end {UnExtend};
-
-
- {Re-extends "f" into an extended file. Note that "f" must have
- been converted to an extended file by OpenExtend, and then
- unextended using UnExtend, before "f" can re-extended using
- ReExtend. ReExtend(f) should be invoked immediately after any
- normal Turbo file function call using "f."}
-
- procedure ReExtend (var f);
- var
- handle: Integer absolute f;
- begin
- handle := TablePtr^[LastHandle];
- TablePtr^[LastHandle] := SaveDcb
- end {ReExtend};
-
- {Example program -- This program opens as many Text files as it
- can, until DOS runs out of room in its DCB Table. It then
-
-
-
-
-
-
-
-
-
-
-
-
-
- PRODUCT : Turbo Pascal NUMBER : 174
- VERSION : 3.01A
- OS : PC DOS
- DATE : August 1, 1986 PAGE : 8/9
-
- TITLE : Files Open Extend
-
-
-
-
- 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.}
-
- const
- MaxCount = 255;
- var
- num: string[6];
- f: array[1..MaxCount] of Text;
- i, count: Integer;
- result: Byte;
- begin
- writeln('Opening files...');
- i := 0;
- repeat
- i := i + 1;
- Str(i, num);
- Assign(f[i], 'junk' + num + '.txt');
- {$I-} Rewrite(f[i]); {$I+}
- result := IOResult;
- if result = 0 then OpenExtend(f[i])
- until result <> 0;
- count := i - 1;
- writeln('Successfully opened ', count, ' files at the same
- time. ',
- 'Writing to each file...');
- for i := 1 to count do
- begin
- UnExtend(f[i]);
- writeln(f[i], 'This is a test');
- ReExtend(f[i])
- end;
- writeln('Closing and erasing each file...');
- for i := 1 to count do
- begin
-
-
-
-
-
-
-
-
-
-
-
-
-
- PRODUCT : Turbo Pascal NUMBER : 174
- VERSION : 3.01A
- OS : PC DOS
- DATE : August 1, 1986 PAGE : 9/9
-
- TITLE : Files Open Extend
-
-
-
-
- UnExtend(f[i]);
- Close(f[i]);
- Erase(f[i]);
- ReExtend(f[i])
- end;
- writeln('Done.')
- end.
- (**)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-