File I/O

Technical Notes

Overview
Creating, Opening and Closing Files
Reading and Writing Data
Manipulating Files and Devices Using a Handle
Manipulating File Pointers
Manipulating File Handles

Overview

This unit consists of handle-oriented file I/O functions which open and create files, manipulate files and devices, manipulate file pointers, and manipulate file handles.

When any file I/O function returns an error code, it does so through the global e_code variable. This convention minimizes the number of registers changed by the functions, and it allows inputs and returns to be more consistent. This convention also allows error conditions to be handled at a later time or ignored altogether. Symbolic constants are defined in ECODES.H for each error code. A list of these codes, their symbolic constants, and their meanings can be found in Appendix A. The error code symbolic constants follow an E_... naming convention.

The naming convention for GET and PUT functions with console I/O and DOS console I/O equivalents is _h...; the naming convention for all other handle-oriented file I/O functions is ..._h.

Creating, Opening and Closing Files

_close_h
Closes a file.
_create_h
Creates and opens a new file or truncates and opens an existing one.
_open_h
Opens a file, optionally creating or truncating it as required.
_openex_h
Opens an existing file.
Each of these functions (except _close_h) takes a file pathname as input and, if successful, returns a DOS file handle for the created or opened file. _close_h accepts a DOS file handle and closes the file or device associated with the handle.

Three distinct functions are provided for opening files. _open_h is a full-featured function which automatically creates nonexisting files and/or truncates existing ones. _openex_h is a significantly smaller function which opens already-existing files. _create_h creates and opens nonexisting files or truncates and opens existing ones.

_open_h supports all DOS file access modes (read only, write only and read/write), sharing modes (read sharing, write sharing, etc.), and inheritance modes. The sharing and inheritance modes are only supported by DOS 3.0 and later-they are ignored under earlier versions of DOS. _open_h can also be directed to create nonexisting files and truncate existing files before opening them. Created files are assigned normal file attributes (non-hidden, non-system and non-read-only).

_openex_h also supports all DOS access modes. This function assumes that the file exists and opens it if it does. An error condition results if the file does not exist. _openex_h should be used if a device is to be opened.

_create_h creates nonexisting files or truncates existing ones prior to opening them. Files may be assigned any file attributes (i.e., hidden, system or read-only). The newly-created files are then opened with read/write access and compatibility-mode sharing characteristics.

The following example demonstrates use of _open_h:


#include                 
...
char *tfile ="TEMPFILE.TXT";
int handle;
...
void main (void)
{
   ...
   handle = _open_h(tfile, O_RDWR+O_TRUNC+O_CREAT);
   if (handle == -1)
   {
   /* error occurred */
   }
   ...
}

Reading and Writing Data

_flush_h
Flushes all DOS buffers associated with an open file.
_hget_chr
Gets a character from a file or device.
_hget_line
Gets a line from a file or device.
_hget_str
Gets a string from a file or device.
_hput_chr
Writes a character to a file or device.
_hput_str
Writes a string to a file or device.
_read_h
Reads a specified number of bytes from a file into a buffer.
_write_h
Writes a specified number of bytes to a file or device from a buffer.
These functions read and write data to/from the file or device associated with a DOS file handle. Each of these functions assumes that the file pointer associated with the handle already points to the file position where data is to be read or written. The file pointer is always adjusted to reflect the data read or written.

_read_h and _write_h read and write a specified number of bytes to or from a buffer. If a DOS error is encountered, it is reported and the error code is returned in e_code. If a disk full condition is encountered, it is reported, but it is not treated as an error (no error code is returned in e_code). In all cases, the actual number of bytes read or written is reported. Note that _read_h and _write_h do not always read or write the exact number of bytes requested, even if no error or disk full condition is reported.

_flush_h forces DOS to flush all file data to disk. This flushes all changed DOS buffers to disk.

The _hget_... and _hput_... functions are the counterparts of the _?get... and _?put... functions in the DOS console I/O and console I/O units. _get_... and _put_... operate on STDIN and STDOUT. _cget_... and _cput_... operate on the console via BIOS calls or direct writes to video hardware. _hget_... and _hput_... operate on any file or device associated with a handle.

_hget_chr and _hput_chr read and write a single character to or from the file or device associated with the DOS file handle. _hget_str and _hput_str read and write strings. _hput_str writes an ASCIIZ string, intact, up to but not including the terminal NULL. _hget_str reads a string of characters up to a CR/LF pair, terminating the string with a NULL in place of the CR/LF. _hget_line behaves like _hget_str, except the returned ASCIIZ string includes the final CR/LF character(s).

Although the _hget_... functions may be used to get characters from STDIN, this should be done with caution. At least some DOS versions buffer input from STDIN, returning control to the program only after the return key has been pressed. However, if more characters are entered than are to be read by the _hget_... function, characters are left in the buffer and are returned immediately on a subsequent read. For this reason, the DOS console I/O _get_... and _put_... functions are recommended for reading and writing STDIN and STDOUT.

Manipulating Files and Devices Using a Handle

_getfsize_h
Gets the current size of an open file.
_getftime_h
Gets the date/time stamp of an open file.
_isatty_h
Determines if a handle is associated with a character device.
_setfsize_h
Sets the size of a file to a specified size.
_setftime_h
Sets the date/time stamp of an open file.
These functions return or modify information about the file or device associated with a given handle.

_getftime_h and _setftime_h return or set the date/time stamp which is affixed to the file when it is closed. This may not take effect until the file is closed.

_getfsize_h and _setfsize_h return or set the current size of the file associated with a DOS file handle. _setfsize_h extends or truncates the file as needed.

_isatty_h allows a given DOS file handle to be queried to see if it refers to a file or device.

Manipulating File Pointers

_eof_h
Determines if a file pointer is at end-of-file.
_lseekb_h
Moves a file pointer to a new position (relative to the beginning of the file).
_lseekbof_h
Moves a file pointer to the beginning of the file.
_lseekc_h
Moves a file pointer to a new position (relative to the current file position).
_lseeke_h
Moves a file pointer to a new position (relative to end-of-file).
_lseekeof_h
Moves a file pointer to end-of-file.
_tell_h
Returns a file pointer position for an open file.
These functions manipulate or get information about the file pointer associated with a given DOS file handle. They are intended to be used only with handles which are associated with files (not devices).

The _lseek... functions move the file pointer to a specified position relative the beginning of the file, the end of the file, or the current file pointer position. _lseekbof_h and _lseekeof_h reset the file pointer to the beginning or the end of the file, respectively. This allows a file to be opened (_open_h) and then prepared for appending (_lseekeof_h) using two back-to-back function calls.

_lseekb_h treats the supplied long int as an unsigned offset relative to the beginning of the file (allowing file sizes up to 1 byte less than 4 gigabytes). _lseekc_h and _lseeke_h each use the supplied long int as a signed offset relative to the current position or the end of file, respectively. Where an offset is returned, the offset is the pointer's absolute position relative to the beginning of the file.

Because of the way DOS controls file handles, _lseekc_h and _lseeke_h allow the file pointer to be moved either prior to the beginning of the file or beyond the end of the file without generating an error. However, an error may occur on a subsequent read/write if the pointer is positioned before the beginning of the file. If the pointer is positioned beyond the end of the file, a write will attempt to grow the file to that length or a read will generate an error. _setfsize_h should be used to expand the size of a file; simply moving the file pointer beyond the end of the file has no affect on the file size.

Manipulating File Handles

_dup_h
Creates a second DOS handle for a file or device.
_dup2_h
Redirects a DOS file handle to another file or device.
These functions manipulate file handles. _dup2_h redirects I/O from one file or device to another. For example, _dup2_h may be used to redirect output going to STDOUT into a file. _dup_h, on the other hand, gives a duplicate handle for the handle that was passed in. The following code demonstrates how to redirect STDOUT to a file and then restore the handle associated with STDOUT to its original state:

#include                 
char *rfile = "RECEIVE.TXT";
int stdout_h = -1, rfile_h = -1;

void main (void)
{
   ...
/* ---- create a duplicate handle for STDOUT */
   stdout_h = _dup_h (STDOUT);
   if (stdout_h == -1)
   {
      /* error occurred */
   }
/* ---- create the file RECEIVE.TXT */
   rfile_h = _create_h (rfile, FA_NORM);
   if (rfile_h == -1)
   {
      /* error occurred */
   }
/* ---- redirect STDOUT to RECEIVE.TXT */
   if (_dup2_h (rfile_h, STDOUT))
   {
      /* error occurred */
   }
           ...
/* ---- restore output path to STDOUT */
   if (_dup2_h (stdout_h, STDOUT))
   {
      /* error occurred */
   }
   ...
}