[comp.lang.pascal] Text Files, I/O Redirection

defaria@hpcupt3.cup.hp.com (Andy DeFaria) (05/22/91)

I have been having three problems lately in TP:

	1) How do you perform I/O redirection using  Exec?  I remember this
	   being discussed  and had thought  that  my home-brewed shell did
	   this properly  then  found out it doesn't and   the  posting has
	   since timed out.   I thought that  I needed to  Exec Command.Com
	   and specify the ">" or "|" options  in the Options parameter but
	   this not working.

	2) How do you get the name  of an open text file  from AFile : Text
	   variable?

	3) The  FileSize function  doesn't work on  Text files.  How  can I
	   easily and  efficiently  get the FileSize  (in bytes) of  a Text
	   file? 

Thanks

defaria@hpcupt3.cup.hp.com (Andy DeFaria) (05/22/91)

>/ hpcupt3:comp.lang.pascal / defaria@hpcupt3.cup.hp.com (Andy DeFaria) / 10:44 am  May 21, 1991 /
>I have been having three problems lately in TP:
>
>	1) How do you perform I/O redirection using  Exec?  I remember this
>	   being discussed  and had thought  that  my home-brewed shell did
>	   this properly  then  found out it doesn't and   the  posting has
>	   since timed out.   I thought that  I needed to  Exec Command.Com
>	   and specify the ">" or "|" options  in the Options parameter but
>	   this not working.
>
>	2) How do you get the name  of an open text file  from AFile : Text
>	   variable?
>
>	3) The  FileSize function  doesn't work on  Text files.  How  can I
>	   easily and  efficiently  get the FileSize  (in bytes) of  a Text
>	   file? 

I have solved #2 and #3.  The solution for #2 is to cast the Text file to a
TextRec  and extract  the Name field.   Unfortunately this Name field is an
ASCIIZ string (smells like a C string).  So:

	FileName := ASCIIZToString (TextRec (TextFile).Name);

Where ASCIIZToString is a function that converts the ASCIIZ  string to a TP
String. 

The solution to #3 is a little bit uglier but works.  Basically, I take the
Text file, get its name (see above), assign  it to a  File of Byte and call
FileSize  on that file (Oh  and don't forget to Close  the file).   I would
like to  come up  with a better method for  this.  I don't like opening and
closing files if  I can avoid it.  Perhaps  a  FindFirst would give me  the
information but I'm not sure if FindFirst would incur more overhead than an
Open/Filesize/Close.  Does anybody know?

Oh, and I'm still waiting for an answer to #1.

ts@uwasa.fi (Timo Salmi) (05/23/91)

In article <45670017@hpcupt3.cup.hp.com> defaria@hpcupt3.cup.hp.com (Andy DeFaria) writes:
>I have been having three problems lately in TP:

>	3) The  FileSize function  doesn't work on  Text files.  How  can I
>	   easily and  efficiently  get the FileSize  (in bytes) of  a Text
>	   file? 

Among other units /ts/tspa23##.arc (##=40,50,55,60) at
garbo.uwasa.fi (and SIMTEL20) archives contains:

TSUNTE: (uses Dos)
  ALLSIZFN Allocated true size of a file in bytes
  BORDER   Change border color (CGA, VGA)
  CAPS     Turn CapsLock on, or off
  CAPSONFN Get CapsLock status
  CLB      Clear the keyboard buffer
  CLUSIZFN Cluster size on a device (cluster is the allocation unit)
  CMDLNFN  Returns the entire command line
  CURSOFF  Turn off cursor (keeps other cursor attributes)
  CURSON   Turn cursor back on as it was
  CURSOR   Change cursor size
  DATEOKFN Is a date a valid, existing date
  FEXISTFN File existence status, handles ReadOnly files correctly
  FSIZE2FN File size in bytes, alternative method
  FSIZEFN  File size in bytes as in directory (never on open file)
  LASTDMFN The number of days in a given month and year
  MONOFN   Is it a monochrome
  NUMLOCK  Turn NumLock on, or off
  NUMLONFN Get NumLock status
  SCRLOCK  Turn ScrollLock on, or off
  SCRLONFN Get ScrollLock status
  WEEKNRFN Returns the week number for a given date
  WKDAYFN  Returns modern weekday
  ZELLERFN Zeller's congruence (for comparing dates etc)

...................................................................
Prof. Timo Salmi
Moderating at garbo.uwasa.fi anonymous ftp archives 128.214.12.37
School of Business Studies, University of Vaasa, SF-65101, Finland
Internet: ts@chyde.uwasa.fi Funet: gado::salmi Bitnet: salmi@finfun

phys169@csc.canterbury.ac.nz (05/23/91)

In article <45670017@hpcupt3.cup.hp.com>, defaria@hpcupt3.cup.hp.com (Andy DeFaria) writes:
> I have been having three problems lately in TP:
> 
> 	1) How do you perform I/O redirection using  Exec?  I remember this
> 	   being discussed  and had thought  that  my home-brewed shell did
> 	   this properly  then  found out it doesn't and   the  posting has
> 	   since timed out.   I thought that  I needed to  Exec Command.Com
> 	   and specify the ">" or "|" options  in the Options parameter but
> 	   this not working.
>
The basic answer is to the the handle duplication DOS calls; copy the current
handle, open a file for redirection, and force a copy of its handle to be put
on handle 1 (for output) or 0 (for stdin), etc. When you've finished, force the
old one back again and close the temporary copy.  An example of all this is at
the end of the posting.
 
> 	2) How do you get the name  of an open text file  from AFile : Text
> 	   variable?
> 
Note that this gets what name was given by the last assign, so you can't get
the name from a handle in general this way (e.g. you can't use it to see the
name of where the stdout is going).
        {make sure you have a "uses DOS" at the start}
        st:=TextRec(AFile).Name;          {uses the pre-defined type TextRec}
        st:=copy(st,1,pred(pos(#0,st)));  {truncate (it is ASCIZ)}
        writeln('The file AFile is assigned to: ',st);

> 	3) The  FileSize function  doesn't work on  Text files.  How  can I
> 	   easily and  efficiently  get the FileSize  (in bytes) of  a Text
> 	   file? 
> 
The following inline assembler will do that, except for devices.
function TextSize(var TF ) : longint;
 inline( $5F/        {pop di}
         $07/        {pop es}
         $B8/$4202/  {mov ax,4202}
         $33/$C9/    {xor cx,cx}
         $8B/$D1/    {mov dx,cx}
         $26/$8B/$1D/{mov bx,es:[di]}
         $CD/$21/    {int 21}
         $50/        {push ax}
         $52/        {push dx}
         $B8/$4200/  {mov ax,4200}
         $33/$C9/    {xor cx,cx}
         $8B/$D1/    {mov dx,cx}
         $26/$8B/$1D/{mov bx,es:[di]}
         $CD/$21/    {int 21}
         $5A/        {pop dx}
         $58);       {pop ax}

Hope this helps,
Mark Aitchison, Physics, University of Canterbury, New Zealand, Up Under.
--snip--
program TryRedirection;
{$M 9000,0,0}

uses DOS;

const
  StandardInputHandle = 0;
  StandardOutputHandle= 1;
  StandardErrorHandle = 2;
  StandardPrinterHandle=4;
  StandardAuxHandle   = 3;

var RedirectionFile : text;
    OldHandle       : word;
    reg             : registers;

function Truncate(st : string) : string;
begin
Truncate:=copy(st,1,pred(pos(#0,st)));
end;

function Redirect(HandleToRedirect,NewFileHandle : word) : word;
begin
with reg do
     begin
     AH:=$45;
     BX:=HandleToRedirect;
     MsDos(reg);   {Duplicate a file handle}
     Redirect:=AX; {Return the temporary copy of the original handle}
     AH:=$46;
     CX:=HandleToRedirect;
     BX:=NewFileHandle;
     if BX<>CX then MsDos(reg); {MSDOS might hang if BX=CX!}
     end;
end;

procedure UnRedirect(HandleToRedirect,OldFileHandle : word);
begin
with reg do
     begin
     AH:=$46;  {force duplicate handle}
     CX:=HandleToRedirect;
     BX:=OldFileHandle;
     if BX<>CX then MsDos(reg); {MSDOS might hang if BX=CX!}
     AH:=$3E;  {close a handle}
     BX:=OldFileHandle;
     MsDos(reg);
     end;
end;

begin
writeln('This goes to the standard output file (screen)');
writeToStandardFile(StandardOutputHandle,' So does this...'^M^J);
assign(RedirectionFile,'Test.out');
rewrite(RedirectionFile);
writeln('Output will be redirected to: ',truncate(TextRec(RedirectionFile).name));
writeln(RedirectionFile,'This is the file to get the redirected output...');
OldHandle:=Redirect(StandardOutputHandle,TextRec(RedirectionFile).handle);
close(RedirectionFile);
exec('c:\COMMAND.COM','/C dir try*.*');
UnRedirect(StandardOutputHandle,OldHandle);
end.

CDCKAB%EMUVM1.BITNET@cunyvm.cuny.edu ( Karl Brendel) (05/24/91)

In article 45670018@hpcupt3.cup.hp.com, defaria@hpcupt3.cup.hp.com
 (Andy DeFaria) wrote:

[... scattered deletions throughout ...]

>2) How do you get the name  of an open text file  from AFile : Text
>   variable?
>
>3) The  FileSize function  doesn't work on  Text files.  How  can I
>   easily and  efficiently  get the FileSize  (in bytes) of  a Text
>   file?

>I have solved #2 and #3.  The solution for #2 is to cast the Text
>file to a TextRec  and extract  the Name field.   Unfortunately this

>The solution to #3 is a little bit uglier but works.  Basically, I
>take the Text file, get its name (see above), assign  it to a  File
>of Byte and call FileSize  on that file (Oh  and don't forget to
>Close  the file).   I would like to  come up  with a better method
>for  this.  I don't like opening and closing files if  I can avoid

Unfortunately, that "solution" to #3 overlooks the text currently in
the file's buffer (within the TextRec). This doesn't matter if you
aren't currently writing the file.

_My_ solution to this problem is to use routines from TurboPower
Software. ;) However, for your use I offer this:

Again casting the text file as a TextRec, extract the Handle and
BufPos values. With a registers variable, call MSdos with ah := $42,
al := 1, bx := Handle, and cx and dx := 0. The current position of
the DOS file pointer will return in dx and ax. Save it as fpos :=
longint(dx) shl 16 + ax. If you are writing the file, the true size,
including buffered output, should be fpos + BufPos.

If you are reading the file, you need to do a DOS seek to EOF. Don't
forget to save fpos, because you'll need it to restore the file
pointer. Call MSdos again with ah := $42, al := 2, bx := Handle, cx
and dx := 0. The current DOS file size will return in dx and ax as
before. That should be the true size. Now call MSdos with ah := $42,
al := 0, bx := Handle, cx := fpos shr 16, dx := fpos and $FFFF. This
will return the file pointer to the original position.

Here's code for making the actual DOS calls. Note that this code
knows nothing about text files and doesn't set TPas' InOutRes, so
you have to pay attention to the function returns to detect a file
io error.

=========================== cut here ============================

const

                        {base of DOS file seek}
  baseSOF         = 0;   {seek from start of file}
  baseCurPos      = 1;   {seek from current file pointer}
  baseEOF         = 2;   {seek from end of file}

function DosFilePos(handle : word; var fpos : longint) : integer;
{
  Call DOS to determine the absolute position of the file pointer IN
  BYTES from the beginning of the file. Remember that DOS knows
  nothing of file contents that may be buffered with Turbo Pascal:
  this file pointer is the one DOS keeps, and may differ from Turbo
  Pascal's buffered file pointer within a text file. Return the
  error code. If no error, return the absolute position of the file
  pointer IN BYTES in fpos.
}
var
  regs : Registers;
begin {DosFilePos}
  with regs do
  begin
    ah := $42;
    al := baseCurPos;
    bx := handle;
    cx := 0;
    dx := 0;
    MSdos(regs);
    if (flags and FCarry) = FCarry then {error}
      DosFilePos := ax
    else begin
      DosFilePos := 0;
      fpos := longint(dx) shl 16 + ax;
    end; {no error}
  end; {with regs}
end; {DosFilePos}

function DosFileSeek(handle : word; base : word;
                     var fpos : longint) : integer;
{
  Call DOS to do a seek of the offset fpos IN BYTES from the
  specified base (see constants above). Return the error code. If no
  error, return the new absolute position of the file pointer IN
  BYTES in fpos.
}
var
  regs : registers;
begin {DosFileSeek}
  with regs do
  begin
    ah := $42;
    al := base;
    bx := handle;
    cx := fpos shr 16;
    dx := fpos and $FFFF;
    MSdos(regs);
    if (flags and FCarry) = FCarry then {error}
      DosFileSeek := ax
    else begin
      DosFileSeek := 0;
      fpos := longint(dx) shl 16 + ax;
    end; {no error}
  end; {with regs}
end; {DosFileSeek}

function DosFileSize(handle : word; var fsize : longint) : integer;
{
  Call DOS to get the size of the file. Return the error code. If no
  error code, return the file size IN BYTES in fsize. This calls
  DosFilePos and DosFileSeek and is subject to the same caveats re
  text files and buffers.
}
var
  pos : longint;
  res : integer;
begin {DosFileSize}
  pos := 0;
  res := DosFilePos(handle,pos);               {get current offset}
  fsize := 0;
  if res = 0 then
    res := DosFileSeek(handle,baseEOF,fsize);         {seek to EOF}
  DosFileSize := res;
  res := DosFileSeek(handle,baseSOF,pos); {restore original offset}
end; {DosFileSize}

=========================== cut here ============================

(BTW--this code is relatively new and I would urge any users to
consider it Not Adequately Tested. It is in the public domain.)

Cheers--                        --Karl

+====================================================================+
| Karl Brendel                           Centers for Disease Control |
| Internet: CDCKAB@EMUVM1.BITNET         Epidemiology Program Office |
| Bitnet: CDCKAB@EMUVM1                  Atlanta GA  30093       USA |
|                        Home of Epi Info 5.0                        |
+====================================================================+