Category : Pascal Source Code
Archive   : PASS_ALL.ZIP
Filename : TI174.ASC

 
Output of file : TI174.ASC contained in archive : PASS_ALL.ZIP








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.
(**)