Jump to content

CPGuide - File Management: Difference between revisions

From EDM2
No edit summary
Line 154: Line 154:


==Using File Commands==
==Using File Commands==
Files are accessed through the file system using file handles. File handles are returned by DosOpen when the file is opened and are used for all subsequent accesses to the file. Files can be be created, opened, read from, written to, closed, copied, moved, deleted, renamed, and locked. Files can be searched for based on a metacharacter pattern template.
Each open file has a file pointer that indicates the current reading or writing location within the file. The file pointer moves automatically with each read or write operation on the file and can be moved manually by the application.
The standard file handles-standard input, standard output, and standard error-provide redirectable input and output to applications. The standard file handles can be used to read input from the keyboard and write output to the display. Alternatively, they can be redirected to read input from and write output to a file. To an application reading and writing the standard file handles, there is no difference between the two.
;Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred.
===Creating Files===
===Creating Files===
DosOpen is used to create files, which are then read from or written to. To create a file, specify FILE_CREATE as the sixth argument in the call to the function. DosOpen then creates the file, if it does not already exist. If the file already exists, the function returns the error value FILE_EXISTED.
The following code fragment shows how to use DosOpen to create the file NEWFILE.TXT:
<pre>
    #define INCL_DOSFILEMGR  /* File System values */
    #include <os2.h>
    HFILE  hf;
    ULONG  ulAction;
    APIRET  ulrc;
    ulrc = DosOpen("NEWFILE.TXT",      /* Name of file to create and open */
                  &hf,                /* Address of file handle          */
                  &ulAction,          /* Action taken                    */
                  0,                  /* Size of new file                */
                  FILE_NORMAL,        /* File attributes                */
                  FILE_CREATE,        /* Creates the file                */
                  OPEN_ACCESS_WRITEONLY |
                  OPEN_SHARE_DENYNONE,
                  (PEAOP2) NULL);      /* No extended attributes          */
</pre>
In this example, DosOpen creates the file and opens it for writing only. Note that the sharing method chosen permits other processes to open the file for any access. The new file is empty (contains no data).
When you use DosOpen to create (or replace) a file, you must specify the attributes the new file is to have. In the preceding example, this value is FILE_NORMAL, so the file is created as a normal file. Other possible file attributes include read-only and hidden, which correspond to FILE_READONLY and FILE_HIDDEN, respectively. The possible file attributes are:
File Attribute Defined Constant
Normal file FILE_NORMAL
Read-only file FILE_READONLY
Hidden file FILE_HIDDEN
System file FILE_SYSTEM
Subdirectory FILE_DIRECTORY
Archived file FILE_ARCHIVED.
The file attribute affects how other processes access the file. For example, if the file is read-only, no process can open the file for writing. There is one exception-the process that creates the read-only file can write to it immediately after creating it. After closing the file, however, the process cannot reopen it for writing.
If you are creating a new file object (a new file or a replacement for an existing one), you must specify the size of the new file in bytes. For example, if you specify 256, the file size is 256 bytes. However, these 256 bytes are undefined. If the file being opened already exists, the file size parameter is ignored. It is up to the application to write valid data to the file. No matter what size you specify, the file pointer is set to point to the beginning of the file so a subsequent call to DosWrite starts writing data at the beginning of the file.
Extended attributes can be defined by an application when a file object is created. An application can define an extended attribute for a file when the file is created during a DosOpen call.
Applications can also control access to specific regions within a file by calling DosSetFileLocks.
===Opening Files===
===Opening Files===
Before performing input or output operations on a file, you must open the file and obtain a file handle. You obtain a file handle by using DosOpen. This function opens the specified file and returns a file handle for it. DosOpen can also be used to create new files.
DosOpen establishes a connection betwee n a file object and an application. This connection is in the form of a 32-bit identifier called a file handle, which is used to refer to the file object and any information associated with it. DosOpen returns a handle that is used in other file system calls to gain access to the object. The file object can be a new file, an existing file, or a replacement for an existing file. It can also be a character device, a block device, or the client end of a named pipe. The type of object is determined by the file name you pass to DosOpen.
;Note: If the object is a named pipe, it must be in a listening state for DosOpen to be successful.
The following code fragment shows the use of DosOpen to:
*Open the existing file SAMPLE.TXT for reading
*Put the file handle into the hf variable
<pre>
    #define INCL_DOSFILEMGR  /* File System values */
    #include <os2.h>
    HFILE  hf;
    ULONG  ulAction;
    APIRET  ulrc;
    ulrc = DosOpen("SAMPLE.TXT",      /* Name of file to open      */
                  &hf,                /* Address of file handle    */
                  &ulAction,          /* Action taken              */
                  0,                  /* Size of file              */
                  FILE_NORMAL,        /* File attribute            */
                  FILE_OPEN,          /* Open the file            */
                  OPEN_ACCESS_READONLY |
                  OPEN_SHARE_DENYNONE,
                  (PEAOP2) NULL);    /* Extended attribute buffer */
</pre>
If DosOpen successfully opens the file, it copies the file handle to the hf variable and copies a value to the ulAction variable indicating what action was taken (for example, FILE_EXISTED for "existing file opened"). A file size is not needed to open an existing file, so the fourth argument is 0. The fifth argument, FILE_NORMAL, specifies the normal file attribute. The sixth argument, FILE_OPEN, directs DosOpen to open the file if it exists or return an error if it does not exist. The seventh argument directs DosOpen to open the file for reading only and enables other applications to open the file even while the current application has it open. The final argument is a pointer to a structure containing information on extended attributes. If the file has no extended attributes, this argument must be NULL.
DosOpen returns 0 if it successfully op ens the file. Applications use the file handle in subsequent functions to read data from the file or to check the status and other file characteristics. If DosOpen fails to open the file, it returns an error value.
When you open a file you must specify whether you want to read from the file, write to it, or both read and write. Also, since more than one application might attempt to open the same file, you must also specify whether you want to allow other processes to have access to the file while you have it open. A file that is shared can be shared for reading only, writing only, or reading and writing. A file that is not shared cannot be opened by another application (or more than once by the first application) until it has been closed by the first application.
An application defines its file access rights (that is, I/O it needs to perform on a file) by setting the appropriate file access mode field in the file open-mode parameter. An application accesses a file as:
*Read-only
*Write-only
*Read/write
An application defines what I/O operations other processes can perform on a file by setting the appropriate sharing mode field in the OpenMode parameter. Other processes are granted:
*Deny read/write access
*Deny write access
*Deny read access
*Deny neither read or write access (deny none)
File sharing requires cooperation between the sharing processes. For example, if process A calls DosOpen to open a file with Deny Write sharing and process B calls DosOpen to open the same file with Read/Write access, the DosOpen call made by process B fails.
You indicate whether or not you want to permit another application to access your file by combining an OPEN_ACCESS_ value and an OPEN_SHARE_ value from the following list:
;File Access and Sharing Rights
<pre>
┌──────────────────────────────┬──────────────────────────────┐
│Value                        │Meaning                      │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_ACCESS_READONLY          │Open a file for reading.      │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_ACCESS_WRITEONLY        │Open a file for writing.      │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_ACCESS_READWRITE        │Open a file for reading and  │
│                              │writing.                      │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_SHARE_DENYREADWRITE      │Open a file for exclusive use,│
│                              │denying other processes read  │
│                              │and write access.            │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_SHARE_DENYWRITE          │Deny other processes write    │
│                              │access to a file.            │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_SHARE_DENYREAD          │Deny other processes read    │
│                              │access to a file.            │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_SHARE_DENYNONE          │Open a file with no sharing  │
│                              │restrictions, granting read  │
│                              │and write access to all      │
│                              │processes.                    │
└──────────────────────────────┴──────────────────────────────┘
</pre>
In general, you can combine any access method (read, write, or read and write) with any sharing method (deny reading, deny writing, deny reading and writing, or grant any access). Some combinations have to be handled carefully, however, such as opening a file for writing without denying other processes access to it.
;Note: For named pipes, the access and sharing modes must be consistent with those specified by DosCreateNPipe.
Other characteristics of the file handle that can be set:
;File Handle Characteristics
</pre>
┌───────────────┬──────────────────────────────┐
│Flag          │Purpose                      │
├───────────────┼──────────────────────────────┤
│Inheritance    │Handle is inherited by a child│
│              │process created with          │
│              │DosExecPgm, or is private to  │
│              │the calling process.          │
├───────────────┼──────────────────────────────┤
│Write-Through  │Actual I/O for synchronous    │
│              │writes is not guaranteed as  │
│              │complete or is guaranteed as  │
│              │complete before the write    │
│              │returns.                      │
├───────────────┼──────────────────────────────┤
│Fail-Errors    │Media errors are reported by  │
│              │the system critical error    │
│              │handler, or by the            │
│              │application.                  │
├───────────────┼──────────────────────────────┤
│DASD          │The file name parameter is the│
│              │name of a file or device      │
│              │opened in the normal way, or a│
│              │drive specification for a    │
│              │fixed disk or diskette drive. │
│              │The DASD flag can be set for  │
│              │direct access to an entire    │
│              │disk or diskette volume,      │
│              │independent of the file      │
│              │system. When the DASD flag is │
│              │set, the handle returned by  │
│              │DosOpen represents the logical│
│              │volume as a single file. To  │
│              │block other processes from    │
│              │accessing the logical volume, │
│              │a DosDevIOCtl Category 8,    │
│              │Function 0 call should be made│
│              │using the handle returned by  │
│              │DosOpen. The DASD flag should │
│              │be set only by systems        │
│              │programs, not by applications.│
├───────────────┼──────────────────────────────┤
│Cache          │The file system caches or does│
│              │not cache data for I/O        │
│              │operations on the file. This  │
│              │flag is advisory only.        │
└───────────────┴──────────────────────────────┘
</pre>
See the [[DosOpen]] material in the Control Program Programming Reference for details of these characteristics.
After an object has been opened, its file handle state flags can be queried by calling [[DosQueryFHState]] and reset by calling [[DosSetFHState]]. See Determining and Setting the State of a File or Device Handle for information on determining the state of a file handle.
When a child process inherits a file handle, it also inherits the sharing and access rights of the file.
You cannot use metacharacters (global file name characters; * and ?) in file names you supply to DosOpen.
===Reading from Files===
===Reading from Files===
Once you open a file or have a file handle, you can read from and write to the file by using DosRead and DosWrite.
DosRead is called with a handle (obtained with DosOpen) for a file, pipe, or device. DosRead copies a specified number of bytes (up to the end of the file) from the file object to the buffer you specify. OS/2 returns, in a parameter, the number of bytes actually read (which might not be the same as the number of bytes requested because the end of the file might have been reached).
To read from a file, you must open it for reading or for reading and writing.
The following code fragment shows how to open the file named SAMPLE.TXT and read the first 512 bytes from it:
<pre>
    #define INCL_DOSFILEMGR  /* File System values */
    #include <os2.h>
    #define BUF_SIZE 512
    HFILE  hf;
    ULONG  ulAction;
    BYTE    abBuffer[BUF_SIZE];
    ULONG  cbRead;
    APIRET  ulrc;
    ulrc = DosOpen("SAMPLE.TXT",
                  &hf,
                  &ulAction,
                  0,
                  FILE_NORMAL,
                  FILE_OPEN,
                  OPEN_ACCESS_READONLY |
                  OPEN_SHARE_DENYNONE,
                  (PEAOP2) NULL);
    if (!ulrc) {
        DosRead(hf,
                abBuffer,
                sizeof(abBuffer),
                &cbRead);
        DosClose(hf);
    }
</pre>
If the file does not have 512 bytes, DosRead reads to the end of the file and puts the number of bytes read in the cbRead variable. If the file pointer is already positioned at the end of the file when DosRead is called, the function puts a 0 in the cbRead variable.
===Writing to Files===
===Writing to Files===
Once you open a file or have a file handle, you can read from and write to the file by using DosRead and DosWrite.
DosWrite copies bytes from a buffer you specify to a file, device, or pipe.
Calling DosWrite with a handle for a file, pipe, or device transfers the number of bytes specified from a buffer location to the object. The system returns, in a parameter, the number of bytes actually written (which in the case of a disk file might not be the same as the number requested because of insufficient disk space).
To write to a file, you must first open it for writing or for reading and writing.
The following code fragment shows how to open the file SAMPLE.TXT again and write 512 bytes to it:
<pre>
    #define INCL_DOSFILEMGR  /* File System values */
    #include <os2.h>
    #define BUF_SIZE 512
    HFILE  hf;
    ULONG  ulAction;
    BYTE    abBuffer[BUF_SIZE];
    ULONG  cbWritten;
    APIRET  ulrc;
    ulrc = DosOpen("SAMPLE.TXT",
                  &hf,
                  &ulAction,
                  0,
                  FILE_NORMAL,
                  FILE_CREATE,
                  OPEN_ACCESS_WRITEONLY |
                  OPEN_SHARE_DENYWRITE,
                  (PEAOP2) NULL);
    if (!ulrc) {
        DosWrite(hf,
                abBuffer,
                sizeof(abBuffer),
                &cbWritten);
        DosClose(hf);
    }
</pre>
DosWrite writes the contents of the buffer to the file. If it fails to write 512 bytes (for example, if the disk is full), the function puts the number of bytes written in the cbWritten variable. The data is read and written exactly as given; the function does not format the data-that is, they do not convert binary data to decimal strings, or vice versa.
The Write-Through Flag
If an application requires data to be written in a specific order, setting the Write-Through flag to 1 guarantees that actual I/O for a synchronous write is completed before the DosWrite returns. If this flag has been set with DosOpen for buffered I/O, and multiple synchronous writes are performed, the system cannot guarantee the actual order in which the data is written. For details on changing the state of the Write-Through flag, see Determining and Setting the State of a File or Device Handle.
===Moving the File Pointer===
===Moving the File Pointer===
Every disk file has a corresponding file pointer that marks the current location in the file. The current location is the byte in the file that will be read from or written to on the next call to [[DosRead]] or [[DosWrite]]. Usually, the file pointer is at the beginning of the file when you first open or create the file, and it advances as you read or write to the file. You can, however, change the position of the file pointer at any time by using [[DosSetFilePtr]].
DosSetFilePtr moves the file pointer a specified offset from a given position. You can move the pointer from the beginning of the file, from the end, or from the current position.
The following code fragment shows how to move the pointer 200 bytes from the end of the file:
<pre>
    #define INCL_DOSFILEMGR  /* File system values    */
    #include <os2.h>
    #define HF_STDOUT 1      /* Standard output handle */
    #define BUF_SIZE 255
    BYTE abBuf[BUF_SIZE];
    HFILE hf;
    ULONG ulRead,
          ulWritten,
          ulAction,
          ulNewPtr;
    DosOpen("SAMPLE.TXT",
            &hf,
            &ulAction,
            0,
            FILE_NORMAL,
            FILE_OPEN,
            OPEN_ACCESS_READONLY |
            OPEN_SHARE_DENYNONE,
            (PEAOP2) NULL);
    DosSetFilePtr(hf,
                  -200,
                  FILE_END,
                  &ulNewPtr);
    DosRead(hf,
            &abBuf,
            sizeof(abBuf),
            &ulRead);
    DosWrite(HF_STDOUT,
            abBuf,
            ulRead,
            &ulWritten);
</pre>
In this example, DosSetFilePtr moves the file pointer to the 200th byte from the end of the file. If the file is not that long, the function moves the pointer to the first byte in the file and returns the actual position (relative to the end of the file) in the ulNewPtr variable.
You can move the file pointer for disk files only. You cannot use DosSetFilePtr on devices, despite using other file functions (DosOpen, DosRead) to access a device.
If a file is read-only, write operations to the file will not be performed.
Moving the pointer from the end of the file can be used to determine the size of the file.
===Closing Files===
===Closing Files===
You can close a file by using DosClose. Since each application has a limited number of file handles that can be open at one time, it is a good practice to close a file after using it.
To do so, supply the file handle in DosClose, as shown in the following code fragment:
<pre>
    #define INCL_DOSFILEMGR  /* File system values */
    #include <os2.h>
    #define BUF_SIZE 80
    HFILE  hf;
    ULONG  ulAction;
    BYTE    abBuffer[BUF_SIZE];
    ULONG  ulRead;
    APIRET  ulrc;
    ulrc = DosOpen("SAMPLE.TXT",
                  &hf,
                  &ulAction,
                  0,
                  FILE_NORMAL,
                  FILE_OPEN,
                  OPEN_ACCESS_READONLY |
                  OPEN_SHARE_DENYNONE,
                  (PEAOP2) NULL);
    if (!ulrc) {
        DosRead(hf,
                abBuffer,
                sizeof(abBuffer),
                &ulRead);
        DosClose(hf);
    }
</pre>
If you open a file for writing, DosClose directs the system to flush the file buffer-that is, to write any existing data in OS/2's intermediate file buffer to the disk or device. The system keeps these intermediate file buffers to make file input and output more efficient. For example, it saves data from previous calls to DosWrite until a certain number of bytes are in the buffer then writes the contents of the buffer to the disk.
DosClose also closes the handle to the file (or pipe, or device). If one or more additional handles to a file have been created with DosDupHandle , the internal buffers for the file are not written to disk, and its directory entry is not updated, until DosClose has been called for all the handles.
===Creating Duplicate File or Device Handles===
===Creating Duplicate File or Device Handles===
DosDupHandle enables a process to create a duplicate handle for an open file, pipe, or device.
The value for the old-file-handle parameter is the handle of an open file, a pipe, or a device. The valid values for the new-file-handle parameter include FFFFH, 0000H (standard input), 0001H (standard output), and 0002H (standard error). Any value other than FFFFH is assumed to be the value of the new file handle.
A value of FFFFH causes the system to allocate a new file handle and send it to this location. If the value specified for the new-file-handle parameter is that of a currently open file, the file handle is closed before it is redefined.
An agreed upon value for a duplicate file handle can be established between a parent process and a child process. Avoid choosing arbitrary values for the new file handle.
The duplicate handle acquires the characteristics of the original. If you move the read/write pointer of the original file handle, for example by calling DosRead, DosWrite, or DosSetFilePtr , the pointer of the duplicate handle is also moved. If the original handle has access to regions in a file that have been locked by DosSetFileLocks , the duplicate also has access.
If inheritance was indicated when a file was opened with DosOpen, a parent process can create a duplicate handle for the file and pass it to a child process by means of shared memory. This permits the child to close the duplicate handle without affecting the file handle of the parent.
Because a parent process controls the meanings for standard I/O done by any child process it creates, the parent can use DosDupHandle to redefine unnamed pipe handles as standard I/O handles to communicate with a child. The steps involved are:
*The parent process creates two pipes and duplicates the read handle of one pipe as 0000 and the write handle of the other pipe as 0001.
*When the child process performs standard I/O, instead of reading from the keyboard and writing to the display, it reads from and writes to the pipes created by its parent.
*As the owner of the pipe, the parent process uses its read and write handles to write to the pipe defined to the child as standard input and read from the pipe defined to the child as standard output.
===Determining and Setting the State of a File or Device Handle===
===Determining and Setting the State of a File or Device Handle===
After a file has been opened, the file handle state flags set with a DosOpen can be queried and reset by calling DosQueryFHState and DosSetFHState . The handle returned by DosSetFHState is used for subsequent input and output to the file.
The following code fragment calls DosSetFHState to set the File Write-Through flag for an opened file. Writes to the file may go through the file system buffer, but the sectors are to be written before any synchronous write call returns. DosQueryFHState is called first to obtain the file handle state bits. Assume that the appropriate file handle has been placed into FileHandle already.
<pre>
    #define INCL_DOSFILEMGR        /* File system values */
    #include <os2.h>
    #include <stdio.h>
    HFILE  hfFileHandle;        /* File handle        */
    ULONG  ulFileHandleState;    /* File handle state  */
    APIRET  ulrc;                /* Return code        */
    ulrc = DosQueryFHState(hfFileHandle,
                          &FileHandleState);
    if (ulrc != 0) {
        printf("DosQueryFHState error: return code = %ld",
              ulrc);
        return;
    }
    ulFileHandleState |= OPEN_FLAGS_WRITE_THROUGH;
    ulrc = DosSetFHState(hfFileHandle,
                        ulFileHandleState);
    if (ulrc != 0) {
        printf("DosSetFHState error: return code = %ld",
              ulrc);
        return;
    }
</pre>
Here are two scenarios involving the use of this function.
*An application requires that data be written in a specific order. To guarantee the order of the data written, it must perform separate synchronous write operations. The application can call DosSetFHState to set the Write-Through flag for the file. This action does not affect any previous asynchronous writes, whose data can remain in the buffers.
*An application cannot handle a certain critical error situation. DosSetFHState can be called to reset critica l error handling as OS/2's responsibility. The I/O function that caused the critical error must be called again so the error can recur, causing control to be passed to OS/2. In the case where asynchronous I/O is being done, the precise time the results of this function will be available to the application is unpredictable.
===Determining the Handle Type===
===Determining the Handle Type===
DosQueryHType enables an application to determine whether a handle is to a file, a pipe, or a device. This function can be used when a file-oriented application needs to modify its behavior, depending on the source of its input. For example, CMD.EXE suppresses writing prompts when its input is from a disk file.
The following code fragment determines whether a given file handle refers to a file or a device. Assume that the desired file handle has been placed into FileHandle already.
<pre>
    #define INCL_DOSFILEMGR  /* File system values                */
    #include <os2.h>
    #include <stdio.h>
    HFILE  hfFileHandle;    /* File handle                        */
    ULONG  ulHandType;      /* Handle type (returned)            */
    ULONG  ulFlagWord;      /* Device driver attribute (returned) */
    APIRET  ulrc;            /* Return code                        */
    ulrc = DosQueryHType(hfFileHandle,
                        &ulHandType,
                        &ulFlagWord);
    if (ulrc != 0) {
        printf("DosQueryHType error: return code = %ld",
              ulrc);
        return;
    }
</pre>
In the preceding example, DosQueryHType returns a value that characterizes the type of file handle, and the associated device driver attribute word, if the handle type indicates that the file handle is associated with a local character device.
===Searching for Files===
===Searching for Files===
You can locate files with names that match a given pattern by using metacharacters in DosFindFirst and DosFindNext .
DosFindFirst searches the current directory and locates the first file name that matches the given pattern. DosFindNext locates the next matching file name and continues to find additional matches on each subsequent call until all matching names are found. The functions copy the file statistics on each file located to a data structure that you supply. The file information returned by a search includes file dates and times, length of data in the file, file size, file attributes, and file name.
To find all files that match the file specification, call DosFindNext repeatedly until the message ERROR_NO_MORE_FILES is returned. Then call DosFindClose to close the directory handle.
The following code fragment shows how to find all file names that have the extension ".C":
<pre>
    #define INCL_DOSFILEMGR  /* File system values */
    #include <os2.h>
    HDIR        hdHdir;
    ULONG        ulFilenames;
    FILEFINDBUF3 ffbFindBuf;
    APIRET      ulrc;
    ulFilenames = 1;
    hdHdir = HDIR_SYSTEM;
    ulrc = DosFindFirst("*.C",
                        &hdHdir,            /* Directory handle                    */
                        FILE_NORMAL,        /* File attribute to look for          */
                        &ffbFindBuf,        /* Result buffer                        */
                        sizeof(ffbFindBuf), /* Size of result buffer                */
                        &ulFilenames,      /* Number of matching names to look for */
                        FIL_STANDARD);      /* Standard level of information        */
    if (!ulrc) {
        do {
            .
            .    /* Use file name in findbuf.achName */
            .
            ulrc = DosFindNext(hdHdir,
                              &ffbFindBuf,
                              sizeof(ffbFindBuf),
                              &ulFilenames);
        } while (!ulrc);
    }
    DosFindClose(hdHdir);
</pre>
In this example, DosFindNext continues to retrieve matching file names until it returns an error value (it returns ERROR_NO_MORE_FILES when it cannot find any more matching files).
To keep track of which files have already been found, both functions use the directory handle, hdir.
This directory handle must be set to HDIR_SYSTEM or HDIR_CREATE before the call to DosFindFirst. HDIR_SYSTEM (00000001H) tells OS/2 to use the system handle for standard output, which is always available to the requesting process. HDIR_CREATE (FFFFFFFFH) tells OS/2 to allocate a new, unused handle.
The directory handle returned by DosFindFirst must be used in subsequent calls to DosFindNext ; it identifies for DosFindNext the name of the file being sought and the current position in the directory.
An attribute parameter for DosFindFirst allows hidden and system files, as well as normal files, to be included in searches.
After locating the files you need, use DosFindClose to close the directory handle. This ensures that when you search for the same files again, you will start at the beginning of the directory. After DosFindClose is called, a subsequent DosFindNext fails.
===Searching Paths for Files===
===Searching Paths for Files===
DosSearchPath searches directory paths for t he name of a file object. The file specification can include metacharacters (global file name characters).
The path string used in the search consists of directory paths separated by semicolons. The caller can supply the path string, or it can supply the name of an environment variable whose value is the path string to be searched. The caller can request that the current working directory be searched before the path string is searched.
If the caller specifies an environment variable, DosSearchPath uses DosScanEnv to find the path string. DosScanEnv searches the environment segme nt for an environment variable name; for example, DPATH. The result pointer points to the string that is the value of the environment variable. The call to DosScanEnv can be made direct ly by the application, or it can be invoked by DosSearchPath.
If the file is found, its full path name is returned, with metacharacters left in place. The results might not be meaningful if a buffer overflow occurs.
As an example, assume that a string such as the following exists in the environment:
    DPATH=C:\SYSDIR;C:\INIT
The following code fragment illustrates a method for searching directory paths to find a file.
<pre>
    #define INCL_DOSFILEMGR  /* File system values */
    #define INCL_DOSMISC
    #include <os2.h>
    #include <stdio.h>
    #define ResultBufLen 255
INT main(VOID)
{
    PSZ  pszPathRef="";
    UCHAR achResultBuffer[ResultBufLen};
    PSZ  pszFile="OS2.INI";
    DosScanEnv("DPATH",
              &pszPathRef);
    DosSearchPath(0,                  /* Path source bit=0 */
                  pszPathRef,
                  pszFile,
                  achResultBuffer,
                  ResultBufLen);
    printf("Result 1: %s\n",
          achResultBuffer);
    DosSearchPath(2,                  /* Path source bit=1 */
                  (PSZ)"DPATH",
                  pszFile,
                  achResultBuffer,
                  ResultBufLen);
    printf("Result 2: %s\n",
          achResultBuffer);
    return;
}
</pre>
[[Category:CPGuide]]
==Specifying Extended LIBPATHs==
==Specifying Extended LIBPATHs==
==Standard File Handles==
==Standard File Handles==
===Standard Input, Output, and Error File Handles===
===Standard Input, Output, and Error File Handles===
===Redirecting Standard File Handles===
===Redirecting Standard File Handles===

Revision as of 19:01, 26 March 2020

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

Control Program Programming Guide and Reference
  1. Introduction to the Control Program
  2. Control Program Functions
  3. Keyboard Functions
  4. Mouse Functions
  5. Video Functions
  6. Data Types
  7. Errors
  8. Debugging
  9. Kernel Debugger Communications Protocol
  10. Device I/O
  11. Dynamic Linking
  12. Error Management
  13. Exception Management
  14. Extended Attributes
  15. File Management
  16. File Names
  17. File Systems
  18. Generic IOCtl Commands
  19. Memory Management
  20. Message Management
  21. National Language Support
  22. Pipes
  23. Program Execution Control
  24. Queues
  25. Semaphores
  26. Timers
  27. Notices
  28. Glossary

OS/2 provides a standard set of file system functions. This means that applications can create, open, read, write, copy, and delete files and directories by using the same functions, regardless of which file system is used. When an application calls a file system function, OS/2 passes the request to the dynamic link library that supports the file system. The dynamic link library carries out most file system processing, such as validating file names. If an error occurs, the file system returns the error to OS/2, which then passes it back to the calling application.

The OS/2 file system functions identify files and directories by their names. These functions store or search for the file or directory in the current directory on the current drive unless the name explicitly specifies a different directory and drive. Occasionally, a file system has control functions in addition to the standard file system functions. The control functions are specific to the given file system. An application can call a control function by using DosFSCtl, which directs OS/2 to pass the control-function information to the corresponding Installable File System (IFS).

The following topics are related to the information in this chapter:

  • Files systems
  • Files names
  • Extended attributes
  • Pipes
  • Device I/O

About Volumes and Drives

OS/2 allows more than one file system on a single storage device. If the device can have more than one partition (or volume), each partition can be initialized as an OS/2 partition and given a valid OS/2 file system. For each volume, OS/2 determines the type of file system the first time the volume is accessed by a function or when the medium in the drive changes. After that, OS/2 manages all input and output to that volume by using the corresponding dynamic link library.

OS/2 uses the volume label and serial number to ensure that the medium in the drive does not change while there are outstanding requests for input and output. Each volume has a volume label and a 32-bit volume serial number, stored in a reserved location in logical sector 0 at the time of formatting. If the volume label and serial number do not match, OS/2 signals the critical error handler to prompt the user to insert the volume that has the specified serial number and label. OS/2 maintains the connection between the medium and the volume label and serial number until all open files on the volume are closed and all search references and cache buffer references are removed. The system redetermines the type of file system and the volume label and serial number only when the medium changes.

OS/2 enables applications to:

  • Determine and set the default drive using DosQueryCurrentDisk and DosSetDefaultDisk, respectively.
  • Determine and set drive information using DosQueryFSInfo and DosSetFSInfo.
  • Determine and set the write verification switch using DosQueryVerify and DosSetVerify. If the write verification switch is on, each time data is written to the disk, the data is verified to ensure it has been recorded correctly. These functions are provided for recording critical data.

About Directories

When an application starts, it inherits the current directory and drive from the application that starts it. An application can determine which directory and drive are current by using DosQueryCurrentDir and DosQueryCurrentDisk. An application can change the current directory and drive of the file system by using DosSetCurrentDir and DosSetDefaultDisk.

When an application creates a new file, the system adds a file entry to the specified directory. Each directory can have any number of entries, up to the physical limit of the disk. An application can create new directories and delete existing directories by using DosCreateDir and DosDeleteDir. Before a directory can be deleted, it must be empty; if there are files or directories in that directory, they must be deleted or moved. An application can delete a file by using DosDelete or move a file to another directory by using DosMove.

Creating a Subdirectory

To create a new subdirectory, an application calls DosCreateDir and specifies a directory path name. If the call is successful, a new subdirectory is created at the end of the path on the specified disk. If no path name is specified, a new subdirectory is created at the end of the current directory for the process. If any subdirectories in the path do not exist, the subdirectory is not created.

Because a subdirectory is a file object, an application also can define an extended attribute for the directory when it is created during this call. See Extended Attributes for more information on extended attributes.

Determining and Changing the Current Directory

Calling DosQueryCurrentDir returns the full path name of the current directory for the specified drive. The string does not begin with a back slash (\) and it ends with a byte containing 00H, the NULL character.

To change the current directory, call DosQueryCurrentDir with the path name of the directory you want to make the current directory.

Deleting a Directory

A subdirectory cannot be removed if it is the current subdirectory or if it contains hidden files or subdirectory entries other than the "." and ".." entries. When these requirements for removal are met, DosDeleteDir can be called with a path name to remove the subdirectory from the disk.

About Files

A file is one or more bytes of data, usually stored on a disk. While the application that creates the file determines the format of the file, the file system determines how the file is stored on the disk and what actions can be performed on it. The file system also stores information about the file in file attributes and extended attributes.

Files are accessed through the file system using file handles. File handles are returned by DosOpen when the file is opened and are used for all subsequent accesses to the file. Files can be be opened, read from, written to, closed, copied, moved, deleted, renamed, and locked. Files can be searched for based on a metacharacter template.

Each open file has a file pointer that indicates the current reading or writing location within the file. The file pointer moves automatically with each read or write operation on the file and can be moved manually by the application.

File Attributes

Each directory entry includes a set of file attributes. File attributes specify whether the directory entry is for a file, a directory, or a volume identifier. The attributes also specify if the file is read-only, hidden, archived, or a system file.

A read-only file cannot be opened for writing, nor can it be deleted. A hidden file (or directory) cannot be displayed by using an ordinary directory listing. A system file is excluded from normal directory searches. The archived attribute is for use by special purpose applications to mark a file that has been changed since the last time the file was examined.

An application can retrieve and set the file attributes by using DosQueryFileInfo and DosSetFileInfo.

File Handles

OS/2 identifies each open file by assigning it a file handle when the application opens or creates the file. The file handle is a unique 32-bit integer. The application can use the handle in functions that read from and write to the file, depending on how the file was opened. The application can continue to use the handle until the file is closed.

The default maximum number of file handles for a process is 50. An application can change this maximum by using DosSetMaxFH. When this call is made, all currently open file handles are preserved.

In the past, the maximum number of file handles was 20. If you previously had code that increased the maximum file handles from 20 to less than 50, you can now remove this code.

When an application starts, it inherits all open file handles from the process that starts it. If the system's command processor starts an application, file handles 0, 1, and 2 represent the standard input, standard output, and standard error files. The standard input file is the keyboard; the standard output and standard error files are the screen. An application can read from the standard input file and write to the standard output and standard error files immediately; it does not have to open the files first.

An application can create a duplicate file handle for an open file by using DosDupHandle. A duplicate handle is identical to the original handle. Typically, duplicate handles are used to redirect the standard input and standard output files. For example, an application can open a disk file and duplicate the disk-file handle as handle 0. Thereafter, an application reading from the standard input file (handle 0) takes data from the disk file, not from the keyboard.

When devices and pipes are accessed through the file system functions (using DosOpen, DosRead, and so on), the devices and pipes are treated as files and are identified using file handles. The standard file handles can be redirected to a device or pipe.

File Pointer

Every open file has a file pointer that specifies the next byte to be read or the location to receive the next byte that is written. When a file is first opened, the system places the file pointer at the beginning of the file. As each byte is read or written, OS/2 advances the pointer.

An application can also move the pointer by using DosSetFilePtr. When the pointer reaches the end of the file and the application attempts to read from the file, no bytes are read and no error occurs. Thus, reading 0 bytes without an error means the program has reached the end of the file.

When an application writes to a disk file, the data being written is usually collected in an internal buffer. OS/2 writes to the disk only when the amount of data equals (or is a multiple of) the sector size of the disk. If there is data in the internal buffer when the file is closed, the system automatically writes the data to the disk before closing the file. An application can also flush the buffer (that is, write the contents of the buffer to the disk) by using DosResetBuffer.

Copying Files

DosCopy is used to copy files and subdirectories. Metacharacters (global file name characters) are not allowed, so only individual files or entire subdirectories can be copied with this function. The source and target can be on different drives.

When the source specified is a subdirectory and an I/O error occurs, the file being copied from the source directory to the target directory at the time of the error will be deleted from the target directory and DosCopy will be terminated.

When a file is being copied and an I/O error occurs,

  • if the source file name is that of a file to be replaced, the file is deleted from the target path.
  • if the source file name is that of a file to be appended, the target file is resized to its original size.

DosCopy will copy the attributes of the source file, such as date of creation, and time of creation to the target file. Additionally, DosCopy will copy the extended attributes of the source file when creating a new subdirectory or a new file, or when replacing an existing file.

Moving Files

DosMove is used to move a file from one subdirectory to another on the same drive. In the process of moving the file, its name can be changed. Metacharacters (global file name characters) are not permitted.

Deleting Files

Calling DosDelete removes the directory entry associated with a file name. Metacharacters (global file name characters) are not permitted.

Files whose read-only attribute is set cannot be deleted.

Changing File Sizes

DosSetFileSize is used to extend or truncate the size of a file that is open for Read/Write or Write-Only access.

When a file is extended, for example to reserve disk space, the value of the additional bytes allocated by the system is undefined.

Locking and Unlocking File Regions

Because OS/2 permits more than one application to open a file and write to it, it is important that the applications not write over each other's work. An application can protect against this problem by temporarily locking a region in a file.

DosSetFileLocks provides a simple mechanism that temporarily prevents access by other processes to regions within a file. DosSetFileLocks specifies a range of bytes in the file that is locked by an application and that can be accessed only by the application locking the region. The application uses the same function to free the locked region.

Locking and unlocking regions in a file enables sharing processes to coordinate their efforts. A process can lock a region in a file so the process can read and update the file region. A sharing process that attempts to lock the region before the other process finishes its update and unlocks the region receives an error code. When a lock is unsuccessful because a lock is already in place, ERROR_LOCK_VIOLATION is returned.

A lock that extends beyond end-of-file is not considered an error. However, a locked region cannot overlap another locked region, nor can it encompass a locked region. Any such conflicting locks must be cleared before a region can be locked.

When a file handle is duplicated, the duplicate handle has access to any locked regions currently being accessed by the original handle. Although a child process created with DosExecPgm can inherit the file handles of its parent process, it does not inherit access to locked regions in effect for a file handle unless the file handle is duplicated and passed to it.

Note
File locks are intended to be in effect for only short periods of time.
When a file with locks is closed, the locks are released in no defined order.


Searching for Files

An application can use DosFindFirst, DosFindNext, and DosFindClose to search the current directory for all file names that match a given pattern.

The pattern must be an OS/2 file name and can include metacharacters (global file name characters). The wildcard characters are the question mark (?) and the asterisk (*). The question mark matches any single character; the asterisk matches any combination of characters. For example, the pattern "A*" matches the names "ABC", "A23", and "ABCD", but the pattern "A?C" matches only the name "ABC".

Determining the Maximum Path Length

An application that recognizes long file names might be run on systems with or without installable file systems. Additionally, the maximum length of a file name might vary from one installable file system to another. So that an application can properly handle this variable condition (and, for example, allocate large enough buffers to hold file names), the application should call DosQuerySysInfo to determine the maximum path length supported by the file system.

Make this call before calling file system functions that require full path names.

Specifying an Extended LIBPATH

The LIBPATH, which is set in CONFIG.SYS, specifies a search path which OS/2 uses when searching for dynamic link libraries (DLLs). The LIBPATH is set during system startup and cannot be changed dynamically.

OS/2 provides the capability of specifying extensions to the LIBPATH. An Extended LIBPATH is a path which is searched in conjunction with the system LIBPATH, but which can be changed dynamically, either by the user from the command line, or by an application.

There are two Extended LIBPATHs: BeginLIBPATH, which is searched before the system LIBPATH, and EndLIBPATH, which is searched after the system LIBPATH.

Extended LIBPATHs are ASCIIZ strings which are formatted in the same manner as the system LIBPATH, that is, they contains a list of subdirectory paths separated by semicolons. The maximum size for each Extended LIBPATH is 1024 characters.

Applications can use DosSetExtLIBPATH to set the Extended LIBPATHs. Likewise, they can use DosQueryExtLIBPATH to query the value of either of the Extended LIBPATHs. A flag parameter for the function specifies whether the BeginLIBPATH or the EndLIBPATH is being set or queried. The flag allows two values: BEGIN_LIBPATH (1) which will set or query BeginLIBPATH, or END_LIBPATH (2) which will set or query EndLIBPATH.

You can call DosSetExtLIBPATH as often as you want. The new Extended LIBPATH that you pass in will replace the current Extended LIBPATH.

You can specify the current Extended LIBPATH as part of the argument by using the % symbol before and after the Extended LIBPATH variable name, that is, as %BeginLIBPATH% or %EndLIBPATH%. For example, suppose you set BeginLIBPATH to "D:\MYDLLS", then later you want to add "D:\TOMSDLLS" before the existing BeginLIBPATH, and "D:\JOESDLLS" after the existing BeginLIBPATH (that is, you want to change BeginLIBPATH to "D:\TOMSDLLS;D:\MYDLLS;D:\JOESDLLS"). You could accomplish this by using "D:\TOMSDLLS;%BeginLIBPATH%;D:\JOESDLLS" as the argument for DosSetExtLIBPATH.

The string argument can be up to 1024 characters long. However, if the resulting Extended LIBPATH is longer than 1024 characters, the function will return an error.

Devices

OS/2 enables you to access peripheral devices using file system commands, and treat those devices as file streams of data. These devices include:

Character devices
COM, clock, console (keyboard and screen), screen, keyboard, printer, null, pointer, and mouse devices.
Standard I/O devices
Character devices automatically installed by OS/2 and recognized by the file system as the standard input, standard output, and standard error devices.
Pseudocharacter devices
An application can attach a device name to a file system and use the file system as a pseudocharacter device (also called a single-file device). Attaching a device name to a file system allows an application to open the device associated with the file system as if it were a character device (for example, a serial port) and read from and write to the device by using DosRead and DosWrite.
In addition, an application can use DosSetFilePtr and DosSetFileLocks with a pseudocharacter device. Also, pseudocharacter devices can be redirected.
A file system that can be attached to a pseudocharacter device is typically associated with a single disk file or with a special storage device, such as a tape drive. The file system establishes a connection with the device and transfers requests and data between OS/2 and the device. The user perceives this file as a device name for a nonexistent device.
This file is seen as a character device because the current drive and directory have no effect on the name. A pseudocharacter device name is an ASCII string with the name of an OS/2 file in the form:
 \DEV\DEV_NAME
The "\dev\" is a required part of the name, but there is no actual subdirectory named "\dev\". This just lets OS/2 know that it is a pseudocharacter device name.
Logical file
devices The hard disk or floppy disk drives, or the partitions on the hard disk or floppy disk drives.


Using File Commands

Files are accessed through the file system using file handles. File handles are returned by DosOpen when the file is opened and are used for all subsequent accesses to the file. Files can be be created, opened, read from, written to, closed, copied, moved, deleted, renamed, and locked. Files can be searched for based on a metacharacter pattern template.

Each open file has a file pointer that indicates the current reading or writing location within the file. The file pointer moves automatically with each read or write operation on the file and can be moved manually by the application.

The standard file handles-standard input, standard output, and standard error-provide redirectable input and output to applications. The standard file handles can be used to read input from the keyboard and write output to the display. Alternatively, they can be redirected to read input from and write output to a file. To an application reading and writing the standard file handles, there is no difference between the two.

Note
In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred.

Creating Files

DosOpen is used to create files, which are then read from or written to. To create a file, specify FILE_CREATE as the sixth argument in the call to the function. DosOpen then creates the file, if it does not already exist. If the file already exists, the function returns the error value FILE_EXISTED.

The following code fragment shows how to use DosOpen to create the file NEWFILE.TXT:

    #define INCL_DOSFILEMGR   /* File System values */
    #include <os2.h>

    HFILE   hf;
    ULONG   ulAction;
    APIRET  ulrc;

    ulrc = DosOpen("NEWFILE.TXT",       /* Name of file to create and open */
                   &hf,                 /* Address of file handle          */
                   &ulAction,           /* Action taken                    */
                   0,                   /* Size of new file                */
                   FILE_NORMAL,         /* File attributes                 */
                   FILE_CREATE,         /* Creates the file                */
                   OPEN_ACCESS_WRITEONLY |
                   OPEN_SHARE_DENYNONE,
                   (PEAOP2) NULL);      /* No extended attributes          */

In this example, DosOpen creates the file and opens it for writing only. Note that the sharing method chosen permits other processes to open the file for any access. The new file is empty (contains no data).

When you use DosOpen to create (or replace) a file, you must specify the attributes the new file is to have. In the preceding example, this value is FILE_NORMAL, so the file is created as a normal file. Other possible file attributes include read-only and hidden, which correspond to FILE_READONLY and FILE_HIDDEN, respectively. The possible file attributes are:

File Attribute Defined Constant 
Normal file FILE_NORMAL 
Read-only file FILE_READONLY 
Hidden file FILE_HIDDEN 
System file FILE_SYSTEM 
Subdirectory FILE_DIRECTORY 
Archived file FILE_ARCHIVED. 

The file attribute affects how other processes access the file. For example, if the file is read-only, no process can open the file for writing. There is one exception-the process that creates the read-only file can write to it immediately after creating it. After closing the file, however, the process cannot reopen it for writing.

If you are creating a new file object (a new file or a replacement for an existing one), you must specify the size of the new file in bytes. For example, if you specify 256, the file size is 256 bytes. However, these 256 bytes are undefined. If the file being opened already exists, the file size parameter is ignored. It is up to the application to write valid data to the file. No matter what size you specify, the file pointer is set to point to the beginning of the file so a subsequent call to DosWrite starts writing data at the beginning of the file.

Extended attributes can be defined by an application when a file object is created. An application can define an extended attribute for a file when the file is created during a DosOpen call.

Applications can also control access to specific regions within a file by calling DosSetFileLocks.

Opening Files

Before performing input or output operations on a file, you must open the file and obtain a file handle. You obtain a file handle by using DosOpen. This function opens the specified file and returns a file handle for it. DosOpen can also be used to create new files.

DosOpen establishes a connection betwee n a file object and an application. This connection is in the form of a 32-bit identifier called a file handle, which is used to refer to the file object and any information associated with it. DosOpen returns a handle that is used in other file system calls to gain access to the object. The file object can be a new file, an existing file, or a replacement for an existing file. It can also be a character device, a block device, or the client end of a named pipe. The type of object is determined by the file name you pass to DosOpen.

Note
If the object is a named pipe, it must be in a listening state for DosOpen to be successful.


The following code fragment shows the use of DosOpen to:

  • Open the existing file SAMPLE.TXT for reading
  • Put the file handle into the hf variable
    #define INCL_DOSFILEMGR   /* File System values */
    #include <os2.h>

    HFILE   hf;
    ULONG   ulAction;
    APIRET  ulrc;

    ulrc = DosOpen("SAMPLE.TXT",       /* Name of file to open      */
                   &hf,                /* Address of file handle    */
                   &ulAction,          /* Action taken              */
                   0,                  /* Size of file              */
                   FILE_NORMAL,        /* File attribute            */
                   FILE_OPEN,          /* Open the file             */
                   OPEN_ACCESS_READONLY |
                   OPEN_SHARE_DENYNONE,
                   (PEAOP2) NULL);     /* Extended attribute buffer */

If DosOpen successfully opens the file, it copies the file handle to the hf variable and copies a value to the ulAction variable indicating what action was taken (for example, FILE_EXISTED for "existing file opened"). A file size is not needed to open an existing file, so the fourth argument is 0. The fifth argument, FILE_NORMAL, specifies the normal file attribute. The sixth argument, FILE_OPEN, directs DosOpen to open the file if it exists or return an error if it does not exist. The seventh argument directs DosOpen to open the file for reading only and enables other applications to open the file even while the current application has it open. The final argument is a pointer to a structure containing information on extended attributes. If the file has no extended attributes, this argument must be NULL.

DosOpen returns 0 if it successfully op ens the file. Applications use the file handle in subsequent functions to read data from the file or to check the status and other file characteristics. If DosOpen fails to open the file, it returns an error value.

When you open a file you must specify whether you want to read from the file, write to it, or both read and write. Also, since more than one application might attempt to open the same file, you must also specify whether you want to allow other processes to have access to the file while you have it open. A file that is shared can be shared for reading only, writing only, or reading and writing. A file that is not shared cannot be opened by another application (or more than once by the first application) until it has been closed by the first application.

An application defines its file access rights (that is, I/O it needs to perform on a file) by setting the appropriate file access mode field in the file open-mode parameter. An application accesses a file as:

  • Read-only
  • Write-only
  • Read/write

An application defines what I/O operations other processes can perform on a file by setting the appropriate sharing mode field in the OpenMode parameter. Other processes are granted:

  • Deny read/write access
  • Deny write access
  • Deny read access
  • Deny neither read or write access (deny none)

File sharing requires cooperation between the sharing processes. For example, if process A calls DosOpen to open a file with Deny Write sharing and process B calls DosOpen to open the same file with Read/Write access, the DosOpen call made by process B fails.

You indicate whether or not you want to permit another application to access your file by combining an OPEN_ACCESS_ value and an OPEN_SHARE_ value from the following list:

File Access and Sharing Rights
┌──────────────────────────────┬──────────────────────────────┐
│Value                         │Meaning                       │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_ACCESS_READONLY          │Open a file for reading.      │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_ACCESS_WRITEONLY         │Open a file for writing.      │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_ACCESS_READWRITE         │Open a file for reading and   │
│                              │writing.                      │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_SHARE_DENYREADWRITE      │Open a file for exclusive use,│
│                              │denying other processes read  │
│                              │and write access.             │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_SHARE_DENYWRITE          │Deny other processes write    │
│                              │access to a file.             │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_SHARE_DENYREAD           │Deny other processes read     │
│                              │access to a file.             │
├──────────────────────────────┼──────────────────────────────┤
│OPEN_SHARE_DENYNONE           │Open a file with no sharing   │
│                              │restrictions, granting read   │
│                              │and write access to all       │
│                              │processes.                    │
└──────────────────────────────┴──────────────────────────────┘

In general, you can combine any access method (read, write, or read and write) with any sharing method (deny reading, deny writing, deny reading and writing, or grant any access). Some combinations have to be handled carefully, however, such as opening a file for writing without denying other processes access to it.

Note
For named pipes, the access and sharing modes must be consistent with those specified by DosCreateNPipe.


Other characteristics of the file handle that can be set:

File Handle Characteristics

┌───────────────┬──────────────────────────────┐ │Flag │Purpose │ ├───────────────┼──────────────────────────────┤ │Inheritance │Handle is inherited by a child│ │ │process created with │ │ │DosExecPgm, or is private to │ │ │the calling process. │ ├───────────────┼──────────────────────────────┤ │Write-Through │Actual I/O for synchronous │ │ │writes is not guaranteed as │ │ │complete or is guaranteed as │ │ │complete before the write │ │ │returns. │ ├───────────────┼──────────────────────────────┤ │Fail-Errors │Media errors are reported by │ │ │the system critical error │ │ │handler, or by the │ │ │application. │ ├───────────────┼──────────────────────────────┤ │DASD │The file name parameter is the│ │ │name of a file or device │ │ │opened in the normal way, or a│ │ │drive specification for a │ │ │fixed disk or diskette drive. │ │ │The DASD flag can be set for │ │ │direct access to an entire │ │ │disk or diskette volume, │ │ │independent of the file │ │ │system. When the DASD flag is │ │ │set, the handle returned by │ │ │DosOpen represents the logical│ │ │volume as a single file. To │ │ │block other processes from │ │ │accessing the logical volume, │ │ │a DosDevIOCtl Category 8, │ │ │Function 0 call should be made│ │ │using the handle returned by │ │ │DosOpen. The DASD flag should │ │ │be set only by systems │ │ │programs, not by applications.│ ├───────────────┼──────────────────────────────┤ │Cache │The file system caches or does│ │ │not cache data for I/O │ │ │operations on the file. This │ │ │flag is advisory only. │ └───────────────┴──────────────────────────────┘

See the DosOpen material in the Control Program Programming Reference for details of these characteristics.

After an object has been opened, its file handle state flags can be queried by calling DosQueryFHState and reset by calling DosSetFHState. See Determining and Setting the State of a File or Device Handle for information on determining the state of a file handle.

When a child process inherits a file handle, it also inherits the sharing and access rights of the file.

You cannot use metacharacters (global file name characters; * and ?) in file names you supply to DosOpen.

Reading from Files

Once you open a file or have a file handle, you can read from and write to the file by using DosRead and DosWrite.

DosRead is called with a handle (obtained with DosOpen) for a file, pipe, or device. DosRead copies a specified number of bytes (up to the end of the file) from the file object to the buffer you specify. OS/2 returns, in a parameter, the number of bytes actually read (which might not be the same as the number of bytes requested because the end of the file might have been reached).

To read from a file, you must open it for reading or for reading and writing.


The following code fragment shows how to open the file named SAMPLE.TXT and read the first 512 bytes from it:

    #define INCL_DOSFILEMGR   /* File System values */
    #include <os2.h>

    #define BUF_SIZE 512

    HFILE   hf;
    ULONG   ulAction;
    BYTE    abBuffer[BUF_SIZE];
    ULONG   cbRead;
    APIRET  ulrc;

    ulrc = DosOpen("SAMPLE.TXT",
                   &hf,
                   &ulAction,
                   0,
                   FILE_NORMAL,
                   FILE_OPEN,
                   OPEN_ACCESS_READONLY |
                   OPEN_SHARE_DENYNONE,
                   (PEAOP2) NULL);

    if (!ulrc) {
        DosRead(hf,
                abBuffer,
                sizeof(abBuffer),
                &cbRead);

        DosClose(hf);
    }

If the file does not have 512 bytes, DosRead reads to the end of the file and puts the number of bytes read in the cbRead variable. If the file pointer is already positioned at the end of the file when DosRead is called, the function puts a 0 in the cbRead variable.

Writing to Files

Once you open a file or have a file handle, you can read from and write to the file by using DosRead and DosWrite.

DosWrite copies bytes from a buffer you specify to a file, device, or pipe.

Calling DosWrite with a handle for a file, pipe, or device transfers the number of bytes specified from a buffer location to the object. The system returns, in a parameter, the number of bytes actually written (which in the case of a disk file might not be the same as the number requested because of insufficient disk space).

To write to a file, you must first open it for writing or for reading and writing.


The following code fragment shows how to open the file SAMPLE.TXT again and write 512 bytes to it:

    #define INCL_DOSFILEMGR   /* File System values */
    #include <os2.h>

    #define BUF_SIZE 512

    HFILE   hf;
    ULONG   ulAction;
    BYTE    abBuffer[BUF_SIZE];
    ULONG   cbWritten;
    APIRET  ulrc;

    ulrc = DosOpen("SAMPLE.TXT",
                   &hf,
                   &ulAction,
                   0,
                   FILE_NORMAL,
                   FILE_CREATE,
                   OPEN_ACCESS_WRITEONLY |
                   OPEN_SHARE_DENYWRITE,
                   (PEAOP2) NULL);

    if (!ulrc) {
        DosWrite(hf,
                 abBuffer,
                 sizeof(abBuffer),
                 &cbWritten);

        DosClose(hf);
    }

DosWrite writes the contents of the buffer to the file. If it fails to write 512 bytes (for example, if the disk is full), the function puts the number of bytes written in the cbWritten variable. The data is read and written exactly as given; the function does not format the data-that is, they do not convert binary data to decimal strings, or vice versa.

The Write-Through Flag If an application requires data to be written in a specific order, setting the Write-Through flag to 1 guarantees that actual I/O for a synchronous write is completed before the DosWrite returns. If this flag has been set with DosOpen for buffered I/O, and multiple synchronous writes are performed, the system cannot guarantee the actual order in which the data is written. For details on changing the state of the Write-Through flag, see Determining and Setting the State of a File or Device Handle.

Moving the File Pointer

Every disk file has a corresponding file pointer that marks the current location in the file. The current location is the byte in the file that will be read from or written to on the next call to DosRead or DosWrite. Usually, the file pointer is at the beginning of the file when you first open or create the file, and it advances as you read or write to the file. You can, however, change the position of the file pointer at any time by using DosSetFilePtr.

DosSetFilePtr moves the file pointer a specified offset from a given position. You can move the pointer from the beginning of the file, from the end, or from the current position.

The following code fragment shows how to move the pointer 200 bytes from the end of the file:

    #define INCL_DOSFILEMGR   /* File system values     */
    #include <os2.h>

    #define HF_STDOUT 1       /* Standard output handle */
    #define BUF_SIZE 255

    BYTE abBuf[BUF_SIZE];
    HFILE hf;
    ULONG ulRead,
          ulWritten,
          ulAction,
          ulNewPtr;

    DosOpen("SAMPLE.TXT",
            &hf,
            &ulAction,
            0,
            FILE_NORMAL,
            FILE_OPEN,
            OPEN_ACCESS_READONLY |
            OPEN_SHARE_DENYNONE,
            (PEAOP2) NULL);

    DosSetFilePtr(hf,
                  -200,
                  FILE_END,
                  &ulNewPtr);

    DosRead(hf,
            &abBuf,
            sizeof(abBuf),
            &ulRead);

    DosWrite(HF_STDOUT,
             abBuf,
             ulRead,
             &ulWritten);

In this example, DosSetFilePtr moves the file pointer to the 200th byte from the end of the file. If the file is not that long, the function moves the pointer to the first byte in the file and returns the actual position (relative to the end of the file) in the ulNewPtr variable.

You can move the file pointer for disk files only. You cannot use DosSetFilePtr on devices, despite using other file functions (DosOpen, DosRead) to access a device.

If a file is read-only, write operations to the file will not be performed.

Moving the pointer from the end of the file can be used to determine the size of the file.

Closing Files

You can close a file by using DosClose. Since each application has a limited number of file handles that can be open at one time, it is a good practice to close a file after using it.

To do so, supply the file handle in DosClose, as shown in the following code fragment:

    #define INCL_DOSFILEMGR   /* File system values */
    #include <os2.h>

    #define BUF_SIZE 80

    HFILE   hf;
    ULONG   ulAction;
    BYTE    abBuffer[BUF_SIZE];
    ULONG   ulRead;
    APIRET  ulrc;

    ulrc = DosOpen("SAMPLE.TXT",
                   &hf,
                   &ulAction,
                   0,
                   FILE_NORMAL,
                   FILE_OPEN,
                   OPEN_ACCESS_READONLY |
                   OPEN_SHARE_DENYNONE,
                   (PEAOP2) NULL);

    if (!ulrc) {
        DosRead(hf,
                abBuffer,
                sizeof(abBuffer),
                &ulRead);

        DosClose(hf);
    }

If you open a file for writing, DosClose directs the system to flush the file buffer-that is, to write any existing data in OS/2's intermediate file buffer to the disk or device. The system keeps these intermediate file buffers to make file input and output more efficient. For example, it saves data from previous calls to DosWrite until a certain number of bytes are in the buffer then writes the contents of the buffer to the disk.

DosClose also closes the handle to the file (or pipe, or device). If one or more additional handles to a file have been created with DosDupHandle , the internal buffers for the file are not written to disk, and its directory entry is not updated, until DosClose has been called for all the handles.

Creating Duplicate File or Device Handles

DosDupHandle enables a process to create a duplicate handle for an open file, pipe, or device.

The value for the old-file-handle parameter is the handle of an open file, a pipe, or a device. The valid values for the new-file-handle parameter include FFFFH, 0000H (standard input), 0001H (standard output), and 0002H (standard error). Any value other than FFFFH is assumed to be the value of the new file handle.

A value of FFFFH causes the system to allocate a new file handle and send it to this location. If the value specified for the new-file-handle parameter is that of a currently open file, the file handle is closed before it is redefined.

An agreed upon value for a duplicate file handle can be established between a parent process and a child process. Avoid choosing arbitrary values for the new file handle.

The duplicate handle acquires the characteristics of the original. If you move the read/write pointer of the original file handle, for example by calling DosRead, DosWrite, or DosSetFilePtr , the pointer of the duplicate handle is also moved. If the original handle has access to regions in a file that have been locked by DosSetFileLocks , the duplicate also has access.

If inheritance was indicated when a file was opened with DosOpen, a parent process can create a duplicate handle for the file and pass it to a child process by means of shared memory. This permits the child to close the duplicate handle without affecting the file handle of the parent.

Because a parent process controls the meanings for standard I/O done by any child process it creates, the parent can use DosDupHandle to redefine unnamed pipe handles as standard I/O handles to communicate with a child. The steps involved are:

  • The parent process creates two pipes and duplicates the read handle of one pipe as 0000 and the write handle of the other pipe as 0001.
  • When the child process performs standard I/O, instead of reading from the keyboard and writing to the display, it reads from and writes to the pipes created by its parent.
  • As the owner of the pipe, the parent process uses its read and write handles to write to the pipe defined to the child as standard input and read from the pipe defined to the child as standard output.

Determining and Setting the State of a File or Device Handle

After a file has been opened, the file handle state flags set with a DosOpen can be queried and reset by calling DosQueryFHState and DosSetFHState . The handle returned by DosSetFHState is used for subsequent input and output to the file.

The following code fragment calls DosSetFHState to set the File Write-Through flag for an opened file. Writes to the file may go through the file system buffer, but the sectors are to be written before any synchronous write call returns. DosQueryFHState is called first to obtain the file handle state bits. Assume that the appropriate file handle has been placed into FileHandle already.

    #define INCL_DOSFILEMGR        /* File system values */
    #include <os2.h>
    #include <stdio.h>

    HFILE   hfFileHandle;         /* File handle        */
    ULONG   ulFileHandleState;    /* File handle state  */
    APIRET  ulrc;                 /* Return code        */

    ulrc = DosQueryFHState(hfFileHandle,
                           &FileHandleState);

    if (ulrc != 0) {
        printf("DosQueryFHState error: return code = %ld",
               ulrc);
        return;
    }

    ulFileHandleState |= OPEN_FLAGS_WRITE_THROUGH;

    ulrc = DosSetFHState(hfFileHandle,
                         ulFileHandleState);

    if (ulrc != 0) {
        printf("DosSetFHState error: return code = %ld",
               ulrc);
        return;
    }

Here are two scenarios involving the use of this function.

  • An application requires that data be written in a specific order. To guarantee the order of the data written, it must perform separate synchronous write operations. The application can call DosSetFHState to set the Write-Through flag for the file. This action does not affect any previous asynchronous writes, whose data can remain in the buffers.
  • An application cannot handle a certain critical error situation. DosSetFHState can be called to reset critica l error handling as OS/2's responsibility. The I/O function that caused the critical error must be called again so the error can recur, causing control to be passed to OS/2. In the case where asynchronous I/O is being done, the precise time the results of this function will be available to the application is unpredictable.

Determining the Handle Type

DosQueryHType enables an application to determine whether a handle is to a file, a pipe, or a device. This function can be used when a file-oriented application needs to modify its behavior, depending on the source of its input. For example, CMD.EXE suppresses writing prompts when its input is from a disk file.

The following code fragment determines whether a given file handle refers to a file or a device. Assume that the desired file handle has been placed into FileHandle already.

    #define INCL_DOSFILEMGR   /* File system values                 */
    #include <os2.h>
    #include <stdio.h>

    HFILE   hfFileHandle;    /* File handle                        */
    ULONG   ulHandType;      /* Handle type (returned)             */
    ULONG   ulFlagWord;      /* Device driver attribute (returned) */
    APIRET  ulrc;            /* Return code                        */

    ulrc = DosQueryHType(hfFileHandle,
                         &ulHandType,
                         &ulFlagWord);

    if (ulrc != 0) {
        printf("DosQueryHType error: return code = %ld",
               ulrc);
        return;
    }

In the preceding example, DosQueryHType returns a value that characterizes the type of file handle, and the associated device driver attribute word, if the handle type indicates that the file handle is associated with a local character device.

Searching for Files

You can locate files with names that match a given pattern by using metacharacters in DosFindFirst and DosFindNext .

DosFindFirst searches the current directory and locates the first file name that matches the given pattern. DosFindNext locates the next matching file name and continues to find additional matches on each subsequent call until all matching names are found. The functions copy the file statistics on each file located to a data structure that you supply. The file information returned by a search includes file dates and times, length of data in the file, file size, file attributes, and file name.

To find all files that match the file specification, call DosFindNext repeatedly until the message ERROR_NO_MORE_FILES is returned. Then call DosFindClose to close the directory handle.

The following code fragment shows how to find all file names that have the extension ".C":

    #define INCL_DOSFILEMGR   /* File system values */
    #include <os2.h>

    HDIR         hdHdir;
    ULONG        ulFilenames;
    FILEFINDBUF3 ffbFindBuf;
    APIRET       ulrc;

    ulFilenames = 1;
    hdHdir = HDIR_SYSTEM;

    ulrc = DosFindFirst("*.C",
                        &hdHdir,            /* Directory handle                     */
                        FILE_NORMAL,        /* File attribute to look for           */
                        &ffbFindBuf,        /* Result buffer                        */
                        sizeof(ffbFindBuf), /* Size of result buffer                */
                        &ulFilenames,       /* Number of matching names to look for */
                        FIL_STANDARD);      /* Standard level of information        */

    if (!ulrc) {
        do {
            .
            .     /* Use file name in findbuf.achName */
            .
            ulrc = DosFindNext(hdHdir,
                               &ffbFindBuf,
                               sizeof(ffbFindBuf),
                               &ulFilenames);

        } while (!ulrc);
    }
    DosFindClose(hdHdir);

In this example, DosFindNext continues to retrieve matching file names until it returns an error value (it returns ERROR_NO_MORE_FILES when it cannot find any more matching files).

To keep track of which files have already been found, both functions use the directory handle, hdir.

This directory handle must be set to HDIR_SYSTEM or HDIR_CREATE before the call to DosFindFirst. HDIR_SYSTEM (00000001H) tells OS/2 to use the system handle for standard output, which is always available to the requesting process. HDIR_CREATE (FFFFFFFFH) tells OS/2 to allocate a new, unused handle.

The directory handle returned by DosFindFirst must be used in subsequent calls to DosFindNext ; it identifies for DosFindNext the name of the file being sought and the current position in the directory.

An attribute parameter for DosFindFirst allows hidden and system files, as well as normal files, to be included in searches.

After locating the files you need, use DosFindClose to close the directory handle. This ensures that when you search for the same files again, you will start at the beginning of the directory. After DosFindClose is called, a subsequent DosFindNext fails.

Searching Paths for Files

DosSearchPath searches directory paths for t he name of a file object. The file specification can include metacharacters (global file name characters).

The path string used in the search consists of directory paths separated by semicolons. The caller can supply the path string, or it can supply the name of an environment variable whose value is the path string to be searched. The caller can request that the current working directory be searched before the path string is searched.

If the caller specifies an environment variable, DosSearchPath uses DosScanEnv to find the path string. DosScanEnv searches the environment segme nt for an environment variable name; for example, DPATH. The result pointer points to the string that is the value of the environment variable. The call to DosScanEnv can be made direct ly by the application, or it can be invoked by DosSearchPath.

If the file is found, its full path name is returned, with metacharacters left in place. The results might not be meaningful if a buffer overflow occurs.

As an example, assume that a string such as the following exists in the environment:

   DPATH=C:\SYSDIR;C:\INIT

The following code fragment illustrates a method for searching directory paths to find a file.

    #define INCL_DOSFILEMGR   /* File system values */
    #define INCL_DOSMISC
    #include <os2.h>
    #include <stdio.h>

    #define ResultBufLen 255

INT main(VOID)
{
    PSZ  pszPathRef="";
    UCHAR achResultBuffer[ResultBufLen};
    PSZ   pszFile="OS2.INI";

    DosScanEnv("DPATH",
               &pszPathRef);

    DosSearchPath(0,                   /* Path source bit=0 */
                  pszPathRef,
                  pszFile,
                  achResultBuffer,
                  ResultBufLen);

    printf("Result 1: %s\n",
           achResultBuffer);

    DosSearchPath(2,                   /* Path source bit=1 */
                  (PSZ)"DPATH",
                  pszFile,
                  achResultBuffer,
                  ResultBufLen);

    printf("Result 2: %s\n",
           achResultBuffer);

    return;

}

Specifying Extended LIBPATHs

Standard File Handles

Standard Input, Output, and Error File Handles

Redirecting Standard File Handles