{
From: GREG ESTABROOKS
Subj: FileIO.Pas
---------------------------------------------------------------------------
}

UNIT FILEIO;            { Low Level File handling routines. Jan 18/94   }
                        { Copyright (C) 1993,1994 Greg Estabrooks       }
                        { NOTE: Requires TP 6.0+ to compile.            }
INTERFACE
{***********************************************************************}
USES DOS;                       { IMPORT FSearch.                       }
CONST                           { Handles PreDefined by DOS.            }
     fStdIn     = $00;          { STD Input Device, (Keyboard).         }
     fStdOut    = $01;          { STD Output Device,(CRT).              }
     fStdErr    = $02;          { STD Error Device, (CRT).              }
     fStdCom    = $03;          { STD Comm.                             }
     fStdPrn    = $04;          { STD Printer.                          }
     oRead      = $00;          { Opens a file for read only.           }
     oWrite     = $01;          { Opens a file for writing only.        }
     oReadWrite = $02;          { Opens a file for reading and writing. }
     oDenyAll   = $10;          { Deny access to other processes.       }
     oDenyWrite = $20;          { Deny write access to other processes. }
     oDenyRead  = $30;          { Deny read access to other processes.  }
     oDenyNone  = $40;          { Allow free access to other processes. }
                                { Possible file attribs,can be combined.}
     aNormal   = $00;  aSystem = $04;  aArchive = $20;
     aReadOnly = $01;  aVolume = $08;
     aHidden   = $02;  aDir    = $10;
TYPE
    LockType = (Lock,UnLock);   { Ordinal Type for use with 'fLock'.    }
VAR
   fError  :WORD;               { Holds any error codes from routines.  }

PROCEDURE ASCIIZ( VAR fName :STRING );
                         { Routine to add a NULL to a string to make it }
                         { ASCIIZ compatible.                           }
                         { File routines automatically call this routine}
                         { usage :                                      }
                         {  ASCIIZ(fName);                              }

FUNCTION  fCreate( fName :STRING; Attr :BYTE ) :WORD;
                         { Routine to Create 'fName' with an attribute  }
                         { of 'Attr'. If the file already exists then it}
                         { will be truncated to a zero length file.     }
                         { Returns a WORD value containing the  handle. }
                         { Uses Int 21h/AH=3Ch.                         }
                         { usage :                                      }
                         {  handle := fCreate('Temp.Dat',aNormal);      }

FUNCTION  fOpen( fName :STRING; Mode :BYTE ) :WORD;
                         { Routine to open already existing file defined}
                         { in 'fName' with an opening mode of 'Mode'.   }
                         { Returns a WORD value containing the  handle. }
                         { Uses Int 21h/AH=3Dh.                         }
                         { usage :                                      }
                         {  handle := fOpen('Temp.Dat',oRead);          }

PROCEDURE fRead( fHandle :WORD; VAR Buff; NTRead:WORD; VAR ARead :WORD );
                         { Reads 'NTRead' bytes of data from 'fHandle'  }
                         { and puts it in 'Buff'. The actually amount   }
                         { of bytes read is returned in 'ARead'.        }
                         { Uses Int 21h/AH=3Fh.                         }
                         { usage :                                      }
                         {  fRead(handle,Buffer,SizeOf(Buffer),ARead);  }

PROCEDURE fWrite( fHandle :WORD; VAR Buff; NTWrite:WORD; VAR AWrite :WORD );
                         { Writes 'NTWrite' bytes of info from 'Buff'   }
                         { to 'fHandle'. The actually amount written is }
                         { returned in 'AWrite'.                        }
                         { Uses Int 21h/AH=40h.                         }
                         { usage :                                      }
                         {  fWrite(handle,Buffer,SizeOf(Buffer),AWrite);}

PROCEDURE fClose( fHandle :WORD );
                         { Routine to close file 'fHandle'. This updates}
                         { the directory time and size enteries.        }
                         { Uses Int 21h/AH=3Eh.                         }
                         { usage :                                      }
                         {  fClose(handle);                             }

PROCEDURE fReset(  fHandle :WORD );
                         { Routine to reset file position pointer to the}
                         { beginning of 'fHandle'.                      }
                         { Uses Int 21h/AH=42h.                         }
                         { usage :                                      }
                         {  fReset(handle);                             }

PROCEDURE fAppend( fHandle :WORD );
                         { Routine to move the File position pointer of }
                         { 'fHandle' to the end of the file. Any further}
                         { writing is added to the end of the file.     }
                         { Uses Int 21h/AH=42h.                         }
                         { usage :                                      }
                         {  fAppend(handle);                            }

PROCEDURE fSeek( fHandle :WORD; fOfs :LONGINT );
                         { Routine to move the file position pointer for}
                         { 'fHandle' to 'fOfs'. 'fOfs' is the actual    }
                         { byte position in the file to move to.        }
                         { Uses Int 21h/AH=42h.                         }
                         { usage :                                      }
                         {  fSeek(handle,1023);                         }

PROCEDURE fErase( fName :STRING );
                         { Routine to erase 'fName'.                    }
                         { Uses Int 21h/AH=41h.                         }
                         { usage :                                      }
                         {  fErase('Temp.Dat');                         }

FUNCTION  fPos( fHandle :WORD ) :LONGINT;
                         { Routine to return the current position within}
                         { 'fHandle'.                                   }
                         { Uses Int 21h/AH=42.                          }
                         { usage :                                      }
                         {  CurPos := fPos(handle);                     }

FUNCTION  fEof( fHandle :WORD ) :BOOLEAN;
                         { Routine to determine whether or not we're    }
                         { currently at the end of file 'fHandle'.      }
                         { usage :                                      }
                         {  IsEnd := fEof(handle);                      }

FUNCTION  fExist( fName :STRING ) :BOOLEAN;
                         { Routine to determine whether or not 'fName'  }
                         { exists.                                      }
                         { usage :                                      }
                         {  Exist := fExist('Temp.Dat');                }

FUNCTION  fGetAttr( fName :STRING ) :BYTE;
                         { Routine to return the current file attribute }
                         { of 'fName'.                                  }
                         { Uses Int 21h/AH=43h,AL=00h.                  }
                         { usage :                                      }
                         {  CurAttr := fGetAttr('Temp.Dat');            }

PROCEDURE fSetAttr( fName :STRING; NAttr :BYTE );
                         { Routine to set file attribute of 'fName' to  }
                         { 'NAttr'.                                     }
                         { Uses Int 21h/AH=43h,AL=01h.                  }
                         { usage :                                      }
                         {  fSetAttr('Temp.Dat',aArchive OR aReadOnly); }

PROCEDURE fSetVerify( On_Off :BOOLEAN );
                         { Routine to set the DOS verify flag ON or OFF.}
                         { depending on 'On_Off'.                       }
                         { TRUE = ON, FALSE = OFF.                      }
                         { Uses Int 21h/AH=2Eh.                         }
                         { usage :                                      }
                         {  fSetVerify( TRUE );                         }

FUNCTION  fGetVerify :BOOLEAN;
                         { Routine to return the current state of the   }
                         { DOS verify flag.                             }
                         { Uses Int 21h/AH=54h.                         }
                         { usage :                                      }
                         {  IsVerify := fGetVerify;                     }

FUNCTION  fSize( fHandle :WORD ) :LONGINT;
                         { Routine to determine the size in bytes of    }
                         { 'fHandle'.                                   }
                         { usage :                                      }
                         {  CurSize := fSize(handle);                   }

PROCEDURE fFlush( fHandle :WORD );
                         { Flushes any File buffers for 'fHandle'       }
                         { immediately and updates the directory entry. }
                         { Uses Int 21h/AH=68h.                         }
                         { usage :                                      }
                         {  fFlush(handle);                             }

PROCEDURE fLock( fHandle :WORD; LockInf :LockType; StartOfs,Len :LONGINT );
                         { Routine to lock/unlock parts of a open file.  }
                         { Locking or unlock is determined by 'LockInf'. }
                         { Uses Int 21h/AH=5Ch.                          }
                         { usage :                                       }
                         {  fLock(handle,Lock,1000,500);                 }
{***********************************************************************}
IMPLEMENTATION

PROCEDURE ASCIIZ( VAR fName :STRING ); ASSEMBLER;
                         { Routine to add a NULL to a string to make it }
                         { ASCIIZ compatible.                           }
                         { File routines automatically call this routine}
ASM
  Push DS                       { Push DS onto the stack.               }
  LDS DI,fname                  { Point DS:DI ---> fName.               }
  Xor BX,BX                     { Clear BX.                             }
  Mov BL,BYTE PTR DS:[DI]       { Load length of string into BL.        }
  Inc BL                        { Point to char after last one in name. }
  Mov BYTE PTR DS:[DI+BX],0     { Now make it a ASCIIZ string.          }
  Pop DS                        { Pop DS off the stack.                 }
END;{ASCIIZ}

FUNCTION  fCreate( fName :STRING; Attr :BYTE ) :WORD;
                         { Routine to Create 'fName' with an attribute  }
                         { of 'Attr'. If the file already exists then it}
                         { will be truncated to a zero length file.     }
                         { Returns a WORD value containing the  handle. }
                         { Uses Int 21h/AH=3Ch.                         }
BEGIN
  ASCIIZ(fName);                { Convert fName to an ASCIIZ string.    }
  ASM
    Push DS                     { Push DS Onto stack.                   }
    Mov fError,0                { Clear Error Flag.                     }
    Mov AX,SS                   { Load AX with SS.                      }
    Mov DS,AX                   { Now load that value into DS.          }
    Lea DX,fName                { Now load DX with the offset of DX.    }
    Inc DX                      { Move past length byte.                }
    Xor CH,CH                   { Clear High byte of CX.                }
    Mov CL,Attr                 { Load attribute to give new file.      }
    Mov AH,$3C                  { Function to create a file.            }
    Int $21                     { Call dos to create file.              }
    Jnc @Exit                   { If no error exit.                     }
    Mov fError,AX               { If there was an  error save it.       }
  @Exit:
    Mov @Result,AX              { Return proper result to user.         }
    Pop DS                      { Pop DS Off the Stack.                 }
  END;
END;{fCreate}

FUNCTION  fOpen( fName :STRING; Mode :BYTE ) :WORD;
                         { Routine to open already existing file defined}
                         { in 'fName' with an opening mode of 'Mode'.   }
                         { Returns a WORD value containing the  handle. }
                         { Uses Int 21h/AH=3Dh.                         }
BEGIN
  ASCIIZ(fName);                { Convert fName to an ASCIIZ string.    }
  ASM
    Push DS                     { Push DS onto stack.                   }
    Mov fError,0                { Clear Error Flag.                     }
    Mov AX,SS                   { Load AX with SS.                      }
    Mov DS,AX                   { Now load that value into DS.          }
    Lea DX,fName                { Now load DX with the offset of DX.    }
    Inc DX                      { Move past length byte.                }
    Mov AH,$3D                  { Function to open a file.              }
    Mov AL,Mode                 { File Opening mode.                    }
    Int $21                     { Call dos to open file.                }
    Jnc @Exit                   { If no error exit.                     }
    Mov fError,AX               { If there was an  error save it.       }
  @Exit:
    Mov @Result,AX              { Return proper result to user.         }
    Pop DS                      { Restore DS from stack.                }
  END;
END;{fOpen}

PROCEDURE fRead( fHandle :WORD; VAR Buff; NTRead:WORD; VAR ARead :WORD );
ASSEMBLER;               { Reads 'NTRead' bytes of data from 'fHandle'  }
                         { and puts it in 'Buff'. The actually amount   }
                         { of bytes read is returned in 'ARead'.        }
                         { Uses Int 21h/AH=3Fh.                         }
ASM
  Push DS                       { Push DS onto the stack.               }
  Mov fError,0                  { Clear Error flag.                     }
  Mov AH,$3F                    { Function to read from a file.         }
  Mov BX,fHandle                { load handle of file to read.          }
  Mov CX,NTRead                 { # of bytes to read.                   }
  LDS DX,Buff                   { Point DS:DX to buffer.                }
  Int $21                       { Call Dos to read file.                }
  LDS DI,ARead                  { Point to amount read.                 }
  Mov WORD PTR DS:[DI],AX       { Save amount actually read.            }
  Jnc @Exit                     { if there was no error exit.           }
  Mov fError,AX                 { If there was Save error code.         }
@Exit:
  Pop DS                        { Pop DS off the stack.                 }
END;{fRead}

PROCEDURE fWrite( fHandle :WORD; VAR Buff; NTWrite:WORD; VAR AWrite :WORD );
ASSEMBLER;               { Writes 'NTWrite' bytes of info from 'Buff'   }
                         { to 'fHandle'. The actually amount written is }
                         { returned in 'AWrite'.                        }
                         { Uses Int 21h/AH=40h.                         }
ASM
  Push DS                       { Push DS onto the stack.               }
  Mov fError,0                  { Clear Error flag.                     }
  Mov AH,$40                    { Function to write to file.            }
  Mov BX,fHandle                { Handle of file to write to.           }
  Mov CX,NTWrite                { # of bytes to read.                   }
  LDS DX,Buff                   { Point DS:DX -> Buffer.                }
  Int $21                       { Call Dos to write to file.            }
  LDS DI,AWrite                 { Point to amount write.                }
  Mov WORD PTR DS:[DI],AX       { Save amount actually written.         }
  Jnc @Exit                     { If there was no error exit.           }
  Mov fError,AX                 { if there was save error code.         }
@Exit:
  Pop DS                        { Pop DS off the stack.                 }
END;{fWrite}

PROCEDURE fClose( fHandle :WORD ); ASSEMBLER;
                         { Routine to close file 'fHandle'. This updates}
                         { the directory time and size enteries.        }
                         { Uses Int 21h/AH=3Eh.                         }
ASM
  Mov fError,0                  { Clear Error flag                      }
  Mov AH,$3E                    { Function to close file                }
  Mov BX,fHandle                { load handle of file to close          }
  Int $21                       { call Dos to close file                }
  Jnc @Exit                     { If there was no error exit            }
  Mov fError,AX                 { if there was save error code          }
@Exit:
END;{fClose}

PROCEDURE fReset( fHandle :WORD ); ASSEMBLER;
                         { Routine to reset file position pointer to the}
                         { beginning of 'fHandle'.                      }
                         { Uses Int 21h/AH=42h.                         }
ASM
  Mov fError,0                  { Clear error flag.                     }
  Mov AH,$42                    { Function to move file pointer.        }
  Mov BX,fHandle                { Handle of file.                       }
  Mov AL,0                      { Offset relative to begining.          }
  Mov CX,0                      { CX:DX = offset from begining of file  }
  Mov DX,0                      { to move to.                           }
  Int $21                       { Call dos to change file pointer.      }
  Jnc @Exit                     { If there was no error exit.           }
  Mov fError,AX                 { If there was save error code.         }
@Exit:
END;{fReset}

PROCEDURE fAppend( fHandle :WORD); ASSEMBLER;
                         { Routine to move the File position pointer of }
                         { 'fHandle' to the end of the file. Any further}
                         { writing is added to the end of the file.     }
                         { Uses Int 21h/AH=42h.                         }
ASM
  Mov fError,0                  { Clear error flag.                     }
  Mov AH,$42                    { Function to change file ptr position. }
  Mov BX,fHandle                { handle of file to change.             }
  Mov AL,$02                    { Change relative to end of file.       }
  Mov CX,0                      { CX:DX = offset from end of file       }
  Mov DX,0                      { to move to.                           }
  Int $21                       { Call dos to move file ptr.            }
  Jnc @Exit                     { If there was no error exit.           }
  Mov fError,AX                 { If there was save error code.         }
@Exit:
END;{fAppend}

PROCEDURE fSeek( fHandle :WORD; fOfs :LONGINT ); ASSEMBLER;
                         { Routine to move the file position pointer for}
                         { 'fHandle' to 'fOfs'. 'fOfs' is the actual    }
                         { byte position in the file to move to.        }
                         { Uses Int 21h/AH=42h.                         }
ASM
  Mov fError,0                  { Clear error flag.                     }
  Mov AH,$42                    { Function to change file ptr position. }
  Mov BX,fHandle                { handle of file to change.             }
  Mov AL,$00                    { Change relative to start of file.     }
  Mov CX,fOfs[2].WORD           { CX:DX = offset from start of file     }
  Mov DX,fOfs.WORD              { to move to.                           }
  Int $21                       { Call dos to move file ptr.            }
  Jnc @Exit                     { If there was no error exit.           }
  Mov fError,AX                 { If there was save error code.         }
@Exit:
END;{fSeek}

PROCEDURE fErase( fName :STRING );
                         { Routine to erase 'fName'.                    }
                         { Uses Int 21h/AH=41h.                         }
BEGIN
  ASCIIZ(fName);                { Convert fName to an ASCIIZ string.    }
  ASM
    Push DS                     { Push DS onto the stack.               }
    Mov fError,0                { Clear error flag.                     }
    Mov AH,$41                  { Function to erase a file.             }
    Mov AX,SS                   { Load AX with SS.                      }
    Mov DS,AX                   { Now load that value into DS.          }
    Lea DX,fName                { Now load DX with the offset of DX.    }
    Inc DX
    Int $21                     { Call dos to erase file.               }
    Jnc @Exit                   { If no error exit.                     }
    Mov fError,AX               { if there was error save error code.   }
  @Exit:
    Pop DS                      { Pop DS off the stack.                 }
  END;
END;{fErase}

FUNCTION  fPos( fHandle :WORD ) :LONGINT; ASSEMBLER;
                         { Routine to return the current position within}
                         { 'fHandle'.                                   }
                         { Uses Int 21h/AH=42.                          }
ASM
  Mov fError,0                  { Clear error flag.                     }
  Mov AH,$42                    { Function to move file pointer.        }
  Mov BX,fHandle                { Handle of file.                       }
  Mov AL,1                      { Offset relative to current pos.       }
  Mov CX,0                      { CX:DX = offset from current position  }
  Mov DX,0                      { to move to.                           }
  Int $21                       { Call dos to change file pointer.      }
  Jnc @Exit                     { If there was no error return result.  }
  Mov fError,AX                 { If there was save error code.         }
@Exit:                          { Int already returns DX:AX as file pos.}
END;{fPos}

FUNCTION  fEof( fHandle :WORD ) :BOOLEAN;
                         { Routine to determine whether or not we're    }
                         { currently at the end of file 'fHandle'.      }
VAR
   CurOfs :LONGINT;             { current file offset.                  }
BEGIN
  CurOfs := fPos(fHandle);      { Save Current Pos.                     }
  fAppend(fHandle);             { Move to the end of the file.          }
  fEof := (CurOfs = fPos(fHandle)); { was current pos = end pos?.       }
  fSeek(fHandle,CurOfs);        { Restore to original file position.    }
END;{fEof}

FUNCTION  fExist( fName :STRING ) :BOOLEAN;
                         { Routine to determine whether or not 'fName'  }
                         { exists.                                      }
BEGIN
  fExist := ( FSearch(fName,'') <> '');
END;{fExist}

FUNCTION  fGetAttr( fName :STRING ) :BYTE;
                         { Routine to return the current file attribute }
                         { of 'fName'.                                  }
                         { Uses Int 21h/AH=43h,AL=00h.                  }
BEGIN
  ASCIIZ(fName);                { Convert fName to an ASCIIZ string.    }
  ASM
    Push DS                     { Push DS onto the stack.               }
    Mov fError,0                { Clear error flag.                     }
    Mov AX,SS                   { Load AX with SS.                      }
    Mov DS,AX                   { Now load that value into DS.          }
    Lea DX,fName                { Now load DX with the offset of DX.    }
    Inc DX
    Mov AX,$4300                { Function to Get file Attrib.          }
    Int $21                     { Call dos to get attr.                 }
    Jnc @Success                { If no error return proper info.       }
    Mov fError,AX               { if there was error save error code.   }
  @Success:
    Mov AX,CX
    Mov @Result,AL              { Return proper result to user.         }
    Pop DS                      { Pop DS off the stack.                 }
  END;
END;{fGetAttr}

PROCEDURE fSetAttr( fName :STRING; NAttr :BYTE );
                         { Routine to set file attribute of 'fName' to  }
                         { 'NAttr'.                                     }
                         { Uses Int 21h/AH=43h,AL=01h.                  }
BEGIN
  ASCIIZ(fName);                { Convert fName to an ASCIIZ string.    }
  ASM
    Push DS                     { Push DS onto the stack.               }
    Mov fError,0                { Clear error flag.                     }
    Mov AX,SS                   { Load AX with SS.                      }
    Mov DS,AX                   { Now load that value into DS.          }
    Lea DX,fName                { Now load DX with the offset of DX.    }
    Inc DX                      { Point to first char after length byte.}
    Xor CX,CX                   { Clear CX.                             }
    Mov CL,NAttr                { Load New attribute byte.              }
    Mov AX,$4301                { Function to Set file Attrib.          }
    Int $21                     { Call dos to set attrib.               }
    Jnc @Exit                   { If no error exit.                     }
    Mov fError,AX               { if there was error save error code.   }
  @Exit:
    Pop DS                      { Pop DS off the stack.                 }
  END;
END;{fSetAttr}

PROCEDURE fSetVerify( On_Off :BOOLEAN ); ASSEMBLER;
                         { Routine to set the DOS verify flag ON or OFF.}
                         { depending on 'On_Off'.                       }
                         { TRUE = ON, FALSE = OFF.                      }
                         { Uses Int 21h/AH=2Eh.                         }
ASM
  Mov AH,$2E                    {  Interrupt Subfunction.               }
  Mov DL,0                      {  Clear DL.                            }
  Mov AL,On_Off                 {  0(FALSE) = off, 1(TRUE) = on.        }
  Int $21                       {  Call Dos.                            }
END;{fSetVerify}

FUNCTION  fGetVerify :BOOLEAN; ASSEMBLER;
                         { Routine to return the current state of the   }
                         { DOS verify flag.                             }
                         { Uses Int 21h/AH=54h.                         }
ASM
  Mov AH,$54                    {  Interrupt Subfunction                }
  Int $21                       {  Call Dos                             }
END;{fGetVerify}

FUNCTION  fSize( fHandle :WORD ) :LONGINT;
                         { Routine to determine the size in bytes of    }
                         { 'fHandle'.                                   }
VAR
   CurOfs :LONGINT;             { Holds original file pointer.          }
BEGIN
  CurOfs := fPos(fHandle);      { Save current file pointer.            }
  fAppend(fHandle);             { Move to end of file.                  }
  fSize := fPos(fHandle);       { Save current pos which equals size.   }
  fSeek(fHandle,CurOfs);        { Restore original file pos.            }
END;{fSize}

PROCEDURE fFlush( fHandle :WORD ); ASSEMBLER;
                         { Flushes any File buffers for 'fHandle'       }
                         { immediately and updates the directory entry. }
                         { Uses Int 21h/AH=68h.                         }
ASM
  Mov fError,0                  { Clear error flag.                     }
  Mov AH,$68                    { Function to Commit file to disk.      }
  Mov BX,fHandle                { Load handle of file to Commit.        }
  Int $21                       { Call dos to flush file.               }
  Jnc @Exit                     { If no error exit.                     }
  Mov fError,AX                 { if there was error save error code.   }
@Exit:
END;{fSetAttr}

PROCEDURE fLock( fHandle :WORD; LockInf :LockType; StartOfs,Len :LONGINT );
                         { Routine to lock/unlock parts of a open file.  }
ASSEMBLER;               { Locking or unlock is determined by 'LockInf'. }
                         { Uses Int 21h/AH=5Ch.                          }

ASM
  Mov fError,0                  { Clear Error Flag.                     }
  Mov AH,$5C                    { Function to lock/unlock part of a file.}
  Mov AL,LockInf                { Load whether to lock/unlock file area.}
  Mov BX,fHandle                { Handle of file to lock.               }
  Mov CX,StartOfs.WORD[0]       { Load StartOfs Into  CX:DX.            }
  Mov DX,StartOfs.WORD[2]
  Mov SI,Len.WORD[0]            { Load Len Into SI:DI.                  }
  Mov DI,Len.WORD[2]
  Int $21                       { Call dos to lock area.                }
  Jnc @Exit                     { If no error exit.                     }
  Mov fError,AX                 { If there was an  error save it.       }
@Exit:
END;{fLock}

BEGIN
END.{FileIO}
