home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
os2prgc.zip
/
file_io.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-06
|
16KB
|
469 lines
/*****************************************************************************
FILE_IO.C -- Sample code for handling FILE I/O with Dos...() API functions.
Copyright (C) 1993,94,95 by Craig Morrison, All Rights Reserved.
You may use this code in your own projects, regardless of renumeration.
All I ask is that you prominently display the above copyright notice.
Should you need assistance, I can be contacted at the following addresses:
Fidonet: Craig Morrison, 1:201/60@fidonet.org
Internet: cam@wpc.cioe.com
Post: Craig Morrison
1316 Ferry St.
Lafayette, IN 47901-1533
USA
NOTES:
You'll notice the complete lack of any references to run-time
library functions. This was done on purpose so that *I* could
control what happens when a thread gets killed. This package
contains just about everything you'll need to do port/file I/O,
string manipulation and ordinal number conversions.
The basic thrust behind this module is to allow you to have a
basic working set of functions that mock C "streams". In a nutshell
you use these functions just like the C stream functions, with one
important exception; THESE ARE NOT C STREAMS. They operate on file
handles that the Dos...() file API functions understand. What I have
done is put a translation layer in between the API functions and
you. When you open a file, you get a "handle" for the file which is
actually an offset into a lookup table of the real file handles and
a lookup table containing the buffers used during file I/O.
When a file is opened, the offset into the lookup tables is returned
with 1 added to it. This allows for easy testing of a valid return,
if 0 is returned something went wrong and the file didn't get
opened. Don't be concerned with what is returned other than to make
sure you get a non-zero value. All the functions in this module take
the above behaviour into account and adjust the offset for you. When
calling a function after opening a file, use the returned value as
is and everything will work just as planned.
Another important difference is that there is no "mode" for an open
file. BlockRead(), BlockWrite(), LineRead() and LineWrite() calls
can be intermixed at will with an open file using this file I/O
interface.
As of this writing, the buffers are only used when the LineRead()
and LineWrite() functions are called. They are used to provide
someplace in memory to do the line ending translations. LineRead()
translates "\r\n" and "\n\r" pairs to "\n". LineWrite() translates
"\n" to an "\r\n" pair for output. This behaviour should be familiar
to anyone who has used files with the C stream functions in text
mode.
DosRead() usually returns a 0 error result and 0 bytes read to
indicate an EOF condition, when using LineRead() to read lines
ERROR_HANDLE_EOF will be returned when the end of file is reached.
DosWrite() usually returns a 0 error code and 0 bytes written to
indicate a disk full condition, when using LineWrite() to write
lines ERROR_HANDLE_EOF is returned to signal disk full.
When time permits I will implement buffered I/O, but right now it
isn't that important of an issue considering caching is an integral
part of OS/2.
Several of the functions in this module work as wrappers around the
actual Dos...() API functions. The wrapper functions use many of the
same parameters that the corresponding Dos...() functions do, for
those parameters that are the same please refer to the Control
Program Reference Guide that comes with the OS/2 ToolKit or the
equivalent documentation for your compiler for their possible
values.
The real reason for all this is so that I can use DosCreateThread()
and DosKillThread() to manipulate threads and not have to worry
about the run-time library having a fit because it couldn't clean up
a thread after I was done with it. Granted, it isn't very portable
but at this point in time portability isn't an issue. I'm hedging my
bets with OS/2 and I am in this for the long haul.
*****************************************************************************/
#define INCL_BASE
#define INCL_NOPM
#define INCL_DOS
#define INCL_DOSERRORS
#include <os2.h>
#include <stdio.h>
#include "str.h"
#include "dosmem.h"
#include "file_io.h"
typedef unsigned char *PBYTE;
#define ALLOC_BUFFER_PAGE_SIZE (4096*2) /* we may not use all the */
/* buffer at once, but I */
/* wanted a safety margin */
/* to avoid traps easily. */
#define MAX_FILES 15
static HMTX ioMutexSem = NULLHANDLE;
static INT openFiles = 0;
static PBYTE fileBuffers[MAX_FILES] = {NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL};
static HFILE fileTable[MAX_FILES] = {0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0};
/*****************************************************************************
Initializes our file I/O handler by creating a mutex semaphore that is
used to ensure no changes are made to the lookup tables that could be
hazardous and setting all the lookup table values to a known state.
** CALL THIS FUNCTION BEFORE DOING ANY FILE I/O WITH THESE FUNCTIONS! **
*****************************************************************************/
BOOL InitFileIO(VOID)
{
INT i;
if (ioMutexSem)
return TRUE;
else if (!DosCreateMutexSem(NULL, &ioMutexSem, DC_SEM_SHARED, FALSE))
{
openFiles = 0;
for (i=0; i<MAX_FILES; i++)
{
fileBuffers[i] = NULL;
fileTable[i] = NULLHANDLE;
}
return TRUE;
}
else
return FALSE;
}
/*****************************************************************************
Closes all open files, frees associated memory and closes the mutex
semaphore used to manage resource contention.
This function should be called just before the last thread in your code
terminates so, if nothing else, the mutex semaphore can be closed and the
system resources for it can be free'd.
*****************************************************************************/
VOID CleanUpFileIO(VOID)
{
CloseAllFiles();
DosCloseMutexSem(ioMutexSem);
}
/*****************************************************************************
Find an empty slot in the lookup tables and return that offset+1.
*****************************************************************************/
INT FindFileOffset(VOID)
{
INT i;
DosRequestMutexSem(ioMutexSem, SEM_INDEFINITE_WAIT);
for(i=0; i<MAX_FILES; i++)
if (!fileTable[i])
break;
DosReleaseMutexSem(ioMutexSem);
return(i<MAX_FILES ? i+1 : 0);
}
/*****************************************************************************
Returns the "real" file handle associated with the passed offset.
*****************************************************************************/
HFILE GetHandleFromInt(INT fIndex)
{
if ((fIndex) && (fIndex<=MAX_FILES))
return( fileTable[fIndex-1] );
else
return NULLHANDLE;
}
/*****************************************************************************
Returns the buffer pointer associated with the passed offset.
*****************************************************************************/
PVOID GetPtrFromInt(INT fIndex)
{
if ((fIndex) && (fIndex<=MAX_FILES))
return( fileBuffers[fIndex-1] );
else
return NULL;
}
/*****************************************************************************
Open a file with the passed parameters.
The file handle and an allocated buffer are inserted into their
respective tables.
The table offset+1 is returned, 0 signifies an error.
*****************************************************************************/
INT OpenFile(PSZ pszFileName, PULONG pulAction, ULONG cbFile,
ULONG ulAttribute, ULONG fsOpenFlags, ULONG fsOpenMode,
PEAOP2 peaop2)
{
HFILE f = NULLHANDLE;
INT fIndex = 0;
APIRET rc;
if (openFiles<MAX_FILES)
{
rc = DosOpen(pszFileName, &f, pulAction, cbFile, ulAttribute,
fsOpenFlags, fsOpenMode, peaop2);
if (!rc)
{
if ((fIndex = FindFileOffset())!=0)
{
DosRequestMutexSem(ioMutexSem, SEM_INDEFINITE_WAIT);
fileBuffers[fIndex-1] = AllocMem(ALLOC_BUFFER_PAGE_SIZE,
PAG_WRITE | PAG_COMMIT);
if (fileBuffers[fIndex-1])
{
setmem(fileBuffers[fIndex-1], 0, ALLOC_BUFFER_PAGE_SIZE);
fileTable[fIndex-1] = f;
openFiles++;
}
else
{
DosClose(f);
fIndex = 0;
}
DosReleaseMutexSem(ioMutexSem);
}
else
DosClose(f);
}
}
return(fIndex);
}
/*****************************************************************************
Closes a file and frees the buffers associated with it.
*****************************************************************************/
VOID CloseFile(INT fIndex)
{
DosRequestMutexSem(ioMutexSem, SEM_INDEFINITE_WAIT);
if (fileTable[fIndex-1])
{
DosClose(fileTable[fIndex-1]);
fileTable[fIndex-1] = 0;
FreeMem(fileBuffers[fIndex-1]);
fileBuffers[fIndex-1] = NULL;
openFiles--;
}
DosReleaseMutexSem(ioMutexSem);
}
/*****************************************************************************
Closes all open files and frees their buffers.
*****************************************************************************/
VOID CloseAllFiles(VOID)
{
INT i;
HFILE f;
DosRequestMutexSem(ioMutexSem, SEM_INDEFINITE_WAIT);
for(i=1; i<=MAX_FILES; i++)
{
if ((f = GetHandleFromInt(i))!=NULLHANDLE)
{
DosClose(f);
FreeMem(GetPtrFromInt(i));
openFiles--;
}
}
DosReleaseMutexSem(ioMutexSem);
}
/*****************************************************************************
Performs a block read from a file.
*****************************************************************************/
APIRET BlockRead(INT fIndex, PVOID p, PULONG cbBuffer)
{
if (GetHandleFromInt(fIndex))
return(DosRead(GetHandleFromInt(fIndex), p, *cbBuffer, cbBuffer));
else
return ERROR_INVALID_HANDLE;
}
/*****************************************************************************
Performs a block write to a file.
*****************************************************************************/
APIRET BlockWrite(INT fIndex, PVOID p, PULONG cbBuffer)
{
if (GetHandleFromInt(fIndex))
return(DosWrite(GetHandleFromInt(fIndex), p, *cbBuffer, cbBuffer));
else
return ERROR_INVALID_HANDLE;
}
/*****************************************************************************
Reads a single line from a file, performing line ending translations.
"This is a line.\r\n"
becomes:
"This is a line.\n"
*****************************************************************************/
APIRET LineRead(INT fIndex, PSZ pszBuf)
{
PSZ pz = (PSZ)GetPtrFromInt(fIndex);
PSZ p;
HFILE f = GetHandleFromInt(fIndex);
ULONG cbRead, cbRetreat;
APIRET rc;
if (f)
{
rc = DosRead(f, pz, ALLOC_BUFFER_PAGE_SIZE, &cbRead);
if (!rc)
{
if (cbRead)
{
if (cbRead==ALLOC_BUFFER_PAGE_SIZE)
pz[cbRead-1] = 0;
else
pz[cbRead] = 0;
p = charinstr(pz, '\n');
if (!p)
p = charinstr(pz, '\r');
*p = 0;
cbRetreat = cbRead-(lenstr(pz)+1);
if (cbRetreat)
DosSetFilePtr(f, -cbRetreat, FILE_CURRENT, &cbRead);
if (lenstr(pz))
{
p--;
if (*p=='\r')
*p = '\n';
else if (*p=='\n')
*p = 0;
}
cpystr(pszBuf, pz);
return 0;
}
else
{
pszBuf[0] = 0;
return ERROR_HANDLE_EOF;
}
}
else
return(rc);
}
return ERROR_INVALID_HANDLE;
}
/*****************************************************************************
Performs the magic for LineWrite() and translates `\n' to `\r\n'.
*****************************************************************************/
PSZ TranslateEndings(PSZ pz)
{
PSZ t;
t = pz;
while ((pz = charinstr(pz, '\n'))!=NULL)
{
movemem(pz+1, pz, lenstr(pz)+1);
*pz = '\r';
pz += 2;
}
return(t);
}
/*****************************************************************************
Writes text line(s) to a file performing line ending translations.
"This is a line.\n"
becomes:
"This is a line.\r\n"
on output to the file.
You can write multiple lines at once with this function by concatenating
them together.
*****************************************************************************/
APIRET LineWrite(INT fIndex, PSZ pszBuf)
{
PSZ pz = (PSZ)GetPtrFromInt(fIndex);
HFILE f = GetHandleFromInt(fIndex);
ULONG cbWritten,
cbLen = lenstr(pszBuf);
APIRET rc;
if (f)
{
rc = 0;
while ((!rc) && (cbLen>3072))
{
cpystrn(pz, pszBuf, 1024);
pszBuf += 1024;
TranslateEndings(pz);
rc = DosWrite(f, pz, lenstr(pz), &cbWritten);
if ((!rc) && (!cbWritten))
return ERROR_HANDLE_EOF;
}
if (!rc)
{
cpystr(pz, pszBuf);
TranslateEndings(pz);
rc = DosWrite(f, pz, lenstr(pz), &cbWritten);
if ((!rc) && (!cbWritten))
return ERROR_HANDLE_EOF;
return(rc);
}
else
return(rc);
}
return ERROR_INVALID_HANDLE;
}
/*****************************************************************************
Positions the file pointer for a file.
*****************************************************************************/
APIRET FileSeek(INT fIndex, LONG *plMove, ULONG ulDirection)
{
HFILE f = GetHandleFromInt(fIndex);
if (f)
return(DosSetFilePtr(f, *plMove, ulDirection, (PULONG)plMove));
else
return ERROR_INVALID_HANDLE;
}