fsiface.cc
//**************************************************************************
// MODULE : Process File System Support Object Methods. *
// AUTHOR : Ron Chernich *
// PURPOSE: Each Process descriptor has a pointer to one of these. If the *
// the process ever invokes an ASSIGN file function, an instance *
// of this class is created to support all required actions. *
// HISTORY: *
// 31-AUG-94 First Simple implementation. *
// 11-SEP-94 Relief! Unit tests for all basic PLL/2 file funcs passed! *
//**************************************************************************
#include
#include "fsiface.hh"
#undef toupper // ensure the function version (not macro) is used
////////////////////////////////////////////////////////////////////////
// When created, simply ensure that all XFCB pointers are NULL and set
// the "magic" number to one sufficiently random to assure that they
// will appear unique..
//
FsIface::FsIface (UINT16 n, Knl *p) : pTx(p), uID(n)
{
for (int idx = 0; idx < MAX_FILES; idx++)
pXfcb[idx] = NULL;
uMagic = (UINT16)(Clock.GetTicks() & 0x0ff0);
}
//////////////
// on destruction, any remaining XFCB's are destroyed without
// flushing (ie automatic behavour assumes process went illegal)..
//
FsIface::~FsIface (void)
{
for (int idx = 0; idx < MAX_FILES; idx++)
if (NULL == pXfcb[idx])
break;
else {
if (pXfcb[idx]->Fcb.pBuf)
DELETE_ARRAY pXfcb[idx]->Fcb.pBuf;
delete pXfcb[idx];
}
}
////////////
// Copies passed "name" string into passed destination, doing wild card expansion of
// first encountered *splat*, padding to the passed length..
// RETURNS: TRUE .. all ok
// FALSE .. bad char, too much input, etc, etc
//
BOOL FsIface::ExpandName (char *pszTarg, char *pszSrc, UINT16 nLen)
{
if (strchr(pszSrc, ':') || strchr(pszSrc, '.') || strchr(pszSrc, ':'))
return FALSE;
if (strlen(pszSrc) > nLen)
return FALSE;
char *cp = pszSrc;
for (UINT16 idx = 0; idx < nLen; idx++)
if ('\0' == *cp)
pszTarg[idx] = ' ';
else
if (*cp != '*')
pszTarg[idx] = toupper(*cp++);
else {
pszTarg[idx] = '?';
*cp = '\0';
}
return TRUE;
}
////////////////////
// If the passed path is for a valid drive unit and the name pases ok,
// try to get an XFCB for it
//
// RETURNS: TRUE .. all Ok
// FALSE .. Bad file name or disk unit
//
UINT16 FsIface::Allocate (HANDLE& hFile, char *pszName)
{
int idx;
char *cp1, *cp2, *psz;
for (idx = 0; idx < MAX_FILES; idx++)
if (NULL == pXfcb[idx])
break;
if (idx < MAX_FILES) {
pXfcb[idx] = new XFCB;
if (NULL == pXfcb[idx])
return FS_Fail;
memset((char*)pXfcb[idx], 0, sizeof(XFCB));
pXfcb[idx]->Fcb.pBuf = new char[BLOCK_LEN];
if (NULL == pXfcb[idx]->Fcb.pBuf) {
delete pXfcb[idx];
return FS_Fail;
}
if (cp2 = strrchr(pszName, '.'))
*cp2++ = '\0';
if (NULL == (cp1 = strchr(pszName, ':')))
cp1 = pszName;
else {
*cp1++ = '\0';
if (strlen(pszName) > 1)
return FS_BadUnit;
*pszName = toupper(*pszName);
if ((*pszName < 'A') || (*pszName > 'A' + MAX_DRIVES))
return FS_BadUnit;
pXfcb[idx]->Fcb.de.nUsrNmbr = *pszName - 'A';
}
if (0 == strlen(cp1))
return FS_BadName;
psz = (char*)pXfcb[idx]->Fcb.de.fname;
if (FALSE == ExpandName(psz, cp1, PNAME_LEN))
return FS_BadName;
psz = (char*)pXfcb[idx]->Fcb.de.fextn;
if (FALSE == ExpandName(psz, cp2, ENAME_LEN))
return FS_BadName;
hFile = (HANDLE)(uMagic + idx);
pXfcb[Idx(hFile)]->mState = FSM_Assigned;
return FS_Ok;
}
return FS_NoHandles;
}
///////////////////
// This func (which will always cause a block), posts a message to open
// an existing file if the current state is "Assigned" then waits to see
// if the open works (the file may not exist, you see)..
//
UINT16 FsIface::Open (HANDLE hFile, UINT16 uStat)
{
if ((0 <= Idx(hFile)) && (Idx(hFile) < MAX_FILES) && (pXfcb[Idx(hFile)]))
switch (pXfcb[Idx(hFile)]->mState) {
case FSM_Assigned: {
PFCBREF pRef = new FCBREF;
pRef->pFcbRef = (FCB*)&pXfcb[Idx(hFile)]->Fcb;
message Msg(uID, FS_Open, 0, (void*)pRef);
pTx->PostMsg(ID_FS, &Msg);
pXfcb[Idx(hFile)]->mState = FSM_Opening;
return FS_Busy;
}
break;
case FSM_Opening:
switch (uStat) {
case FS_Ok:
pXfcb[Idx(hFile)]->mState = FSM_BufferMT;
break;
case FS_NotFound:
pXfcb[Idx(hFile)]->mState = FSM_Assigned;
break;
default:
uStat = FS_Fail;
}
return uStat;
}
return FS_Fail;
}
////////////////////
// This scenario deals adequately with a CLOSE call, flushing the file if
// there have been any write operations performed, in which case we transit
// to the intermediate "Flushing" state, return Busy and wait for the result
// to come back. When it does (or if there have been no writes, or even
// possibly we never got past "assigned"), we deallocate the XFCB for reuse.
//
// An EOF mark is inserted in the last block unless we are right on the
// point of opening a new record (in which case the BDOS can report the EOF).
//
UINT16 FsIface::Close (HANDLE hFile, UINT16 uStat)
{
if ((Idx(hFile) < MAX_FILES) && (pXfcb[Idx(hFile)]))
switch (pXfcb[Idx(hFile)]->mState) {
case FSM_BufferMT:
case FSM_DataAvailable:
if (0 == pXfcb[Idx(hFile)]->nWriteCnt) {
pXfcb[Idx(hFile)]->mState = FSM_Closing;
return Close(hFile, uStat);
}
case FSM_BuffAvailable:
pXfcb[Idx(hFile)]->mState = FSM_Flushing;
return Write(hFile, (char)FS_EOFmark);
case FSM_Writing:
if (FS_Ok == uStat) {
PFCBREF pRef = new FCBREF;
pRef->pFcbRef = (FCB*)&pXfcb[Idx(hFile)]->Fcb;
message Msg(uID, FS_Close, 0, (void*)pRef);
pTx->PostMsg(ID_FS, &Msg);
pXfcb[Idx(hFile)]->mState = FSM_Closing;
return FS_Busy;
}
pXfcb[Idx(hFile)]->mState = FSM_DiskError;
break;
case FSM_Closing:
DELETE_ARRAY pXfcb[Idx(hFile)]->Fcb.pBuf;
delete pXfcb[Idx(hFile)];
pXfcb[Idx(hFile)] = NULL;
break;
default:
uStat = FS_Fail;
}
return uStat;
}
////////////////////
// Allow deletes provided no operation other than Allocate has taken place.
// Regardless of outcome, status is set back to "Assigned" ('cause the PLL/2
// compiler always fires off a "Delete" ahead of a "Creat" to enforce the
// rules for the "ASSIGN, REWRITE" precodures)..
//
UINT16 FsIface::Delete (HANDLE hFile, UINT16 uStat)
{
if ((Idx(hFile) < MAX_FILES) && (pXfcb[Idx(hFile)]))
switch (pXfcb[Idx(hFile)]->mState) {
case FSM_Assigned: {
PFCBREF pRef = new FCBREF;
pRef->pFcbRef = (FCB*)&pXfcb[Idx(hFile)]->Fcb;
message Msg(uID, FS_Delete, 0, (void*)pRef);
pTx->PostMsg(ID_FS, &Msg);
pXfcb[Idx(hFile)]->mState = FSM_Deleting;
}
return FS_Busy;
case FSM_Deleting:
pXfcb[Idx(hFile)]->mState = FSM_Assigned;
if (FS_Ok == uStat)
return FS_Ok;
else
return FS_NotFound;
}
return FS_Fail;
}
////////////////////
// If we hit here with status of Assigned, we try to create a new file,
// (provided the FCB name has no wild chars int it, which is bad, but we
// couldn't tell if the ASSIGN would be followed by a RESET or a REWRITE)
// by sending a message to the BDOS and telling exec this process is now
// blocked (busy). When the reply comes back, we transit to "Buffer Empty"
// ready for a read or write sequence (can't tell which yet) if the creat
// worked and return the status to the caller (normally the PCI).
//
UINT16 FsIface::Creat (HANDLE hFile, UINT16 uStat)
{
if ((Idx(hFile) < MAX_FILES) && (pXfcb[Idx(hFile)]))
switch (pXfcb[Idx(hFile)]->mState) {
case FSM_Assigned: {
char *cp = (char*)pXfcb[Idx(hFile)]->Fcb.de.fname;
for (int i = 0; i < FILENAME_LEN; i++, cp++)
if ('?' == *cp)
return FS_BadName;
PFCBREF pRef = new FCBREF;
pRef->pFcbRef = (FCB*)&pXfcb[Idx(hFile)]->Fcb;
message Msg(uID, FS_Creat, 0, (void*)pRef);
pTx->PostMsg(ID_FS, &Msg);
pXfcb[Idx(hFile)]->mState = FSM_Creating;
return FS_Busy;
}
case FSM_Creating:
pXfcb[Idx(hFile)]->mState = (FS_Ok == uStat) ? FSM_BufferMT : FSM_DiskFull;
return uStat;
}
return FS_Fail;
}
////////////////////
// Provided we have a valid file handle, with an existing extended control block
// which has not been "written to", read a character, performing buffered reads
// to the BDOS. We will assume that EOF marks (usually 0x1a) are imbedded in all
// text files. NOTE how we use a little gentle recursion to simplify the code..
//
UINT16 FsIface::Read (HANDLE hFile, char& ch, UINT16 uStat)
{
if ((MAX_FILES <= Idx(hFile)) ||
(NULL == pXfcb[Idx(hFile)]) ||
(pXfcb[Idx(hFile)]->nWriteCnt))
return FS_Fail;
switch (pXfcb[Idx(hFile)]->mState) {
case FSM_BufferMT: {
PFCBREF pRef = new FCBREF;
pRef->pFcbRef = (FCB*)&pXfcb[Idx(hFile)]->Fcb;
message Msg(uID, FS_ReadSeq, 0, (void*)pRef);
pTx->PostMsg(ID_FS, &Msg);
pXfcb[Idx(hFile)]->mState = FSM_Reading;
pXfcb[Idx(hFile)]->nReadCnt++;
}
return FS_Busy;
case FSM_Reading:
pXfcb[Idx(hFile)]->mState = FSM_DataAvailable;
pXfcb[Idx(hFile)]->ndx = 0;
return Read(hFile, ch, uStat);
case FSM_DataAvailable:
if (BLOCK_LEN <= pXfcb[Idx(hFile)]->ndx) {
pXfcb[Idx(hFile)]->mState = FSM_BufferMT;
return Read(hFile, ch, uStat);
}
else {
ch = pXfcb[Idx(hFile)]->Fcb.pBuf[pXfcb[Idx(hFile)]->ndx++];
if ((BLOCK_LEN > pXfcb[Idx(hFile)]->ndx) &&
(FS_EOFmark == pXfcb[Idx(hFile)]->Fcb.pBuf[pXfcb[Idx(hFile)]->ndx]))
pXfcb[Idx(hFile)]->mState = FSM_EOF;
}
return FS_Ok;
case FSM_EOF:
return FS_Fail;
}
return uStat;
}
////////////////////
// Buffer character writes, flushing to disk as it fills. The handle must be
// valid, an extended FCB allocated and the file can NOT have been read from.
// Again, we use recursion to save on code as the State Machine steps through
// the sequence BufferMT -> BuffAvailable -> Writing -> BufferMT..
//
// Caution! BufferMT FALLS THROUGH to BuffAvailable
//
UINT16 FsIface::Write (HANDLE hFile, char ch, UINT16 uStat)
{
if ((Idx(hFile) < MAX_FILES) &&
(pXfcb[Idx(hFile)]) && (0 == pXfcb[Idx(hFile)]->nReadCnt))
switch (pXfcb[Idx(hFile)]->mState) {
case FSM_BufferMT:
pXfcb[Idx(hFile)]->nWriteCnt++;
pXfcb[Idx(hFile)]->ndx = 0;
pXfcb[Idx(hFile)]->mState = FSM_BuffAvailable;
case FSM_Flushing:
case FSM_BuffAvailable: {
if (pXfcb[Idx(hFile)]->ndx < BLOCK_LEN) {
pXfcb[Idx(hFile)]->Fcb.pBuf[pXfcb[Idx(hFile)]->ndx++] = ch;
if (FSM_Flushing != pXfcb[Idx(hFile)]->mState)
return FS_Ok;
}
PFCBREF pRef = new FCBREF;
pRef->pFcbRef = (FCB*)&pXfcb[Idx(hFile)]->Fcb;
message Msg(uID, FS_WriteSeq, 0, (void*)pRef);
pTx->PostMsg(ID_FS, &Msg);
pXfcb[Idx(hFile)]->mState = FSM_Writing;
}
return FS_Busy;
case FSM_Writing:
if (uStat != FS_Ok) {
pXfcb[Idx(hFile)]->mState = FSM_DiskFull;
return FS_Fail;
}
pXfcb[Idx(hFile)]->mState = FSM_BufferMT;
return Write(hFile, ch, uStat);
}
return FS_Fail;
}
////////////////////
// This one deals with entering strings into the deblocking buffer. It is
// similar to the signle char case except it does not have to deal with the
// "FSM_Flushing" state, but it does have to deal with an input string that
// may fill the buffer (perhaps more than once!). The XFCB has a char
// pointer to hold any overflow. A copy of this overflow is held using
// this reference and the buffer written to disk. When the caller (who
// has managed the blocked state) call us again, we test this pointer
// and recurse until is is null.
//
UINT16 FsIface::Write (HANDLE hFile, char *psz, UINT16 uStat)
{
if (psz && (Idx(hFile) < MAX_FILES) &&
(pXfcb[Idx(hFile)]) && (0 == pXfcb[Idx(hFile)]->nReadCnt))
switch (pXfcb[Idx(hFile)]->mState) {
case FSM_BufferMT:
pXfcb[Idx(hFile)]->nWriteCnt++;
pXfcb[Idx(hFile)]->ndx = 0;
pXfcb[Idx(hFile)]->mState = FSM_BuffAvailable;
case FSM_BuffAvailable: {
char *pszHold = NULL;
while (*psz && (pXfcb[Idx(hFile)]->ndx < BLOCK_LEN))
pXfcb[Idx(hFile)]->Fcb.pBuf[pXfcb[Idx(hFile)]->ndx++] = *psz++;
if (*psz) {
pszHold = new char[strlen(psz) + 1];
if (pszHold)
strcpy(pszHold, psz);
else
return FS_Fail;
}
if (pXfcb[Idx(hFile)]->pszTemp)
DELETE_ARRAY pXfcb[Idx(hFile)]->pszTemp;
pXfcb[Idx(hFile)]->pszTemp = pszHold;
if (pXfcb[Idx(hFile)]->ndx < BLOCK_LEN)
return FS_Ok;
PFCBREF pRef = new FCBREF;
pRef->pFcbRef = (FCB*)&pXfcb[Idx(hFile)]->Fcb;
message Msg(uID, FS_WriteSeq, 0, (void*)pRef);
pTx->PostMsg(ID_FS, &Msg);
pXfcb[Idx(hFile)]->mState = FSM_Writing;
}
return FS_Busy;
case FSM_Writing:
if (uStat != FS_Ok) {
pXfcb[Idx(hFile)]->mState = FSM_DiskFull;
return FS_Fail;
}
pXfcb[Idx(hFile)]->mState = FSM_BufferMT;
return (NULL == pXfcb[Idx(hFile)]->pszTemp) ?
FS_Ok : Write(hFile, pXfcb[Idx(hFile)]->pszTemp, uStat);
default:
break;
}
return FS_Fail;
}
////////////////////
// ** NOT IMPLEMENTED **
//
UINT16 FsIface::Read (HANDLE hFile, char *psz, UINT16 uStat)
{
return FS_Ok;
}
////////////////////
// ** NOT IMPLEMENTED **
//
//
UINT16 FsIface::Stat (HANDLE, UINT16)
{
return FS_Ok;
}
////////////////////
// Return the state of the the XFCB FSM associated with the passed handle
// as either hit end of file, or not.
//
UINT16 FsIface::IsEof (HANDLE hFile)
{
return ((MAX_FILES <= Idx(hFile)) || (NULL == pXfcb[Idx(hFile)])) ?
FS_Fail : (FSM_EOF == pXfcb[Idx(hFile)]->mState) ? FS_EOF : FS_Ok;
}
////////////////////
// ** NOT IMPLEMENTED **
//
UINT16 FsIface::CloseAll (UINT16)
{
return FS_Ok;
}
////////////////////
// ** NOT IMPLEMENTED **
//
UINT16 FsIface::FindFirst (HANDLE, UINT16)
{
return FS_Ok;
}
////////////////////
// ** NOT IMPLEMENTED **
//
UINT16 FsIface::FindNext (HANDLE hFile, UINT16 uStat)
{
return FS_Ok;
}
/********************************** eof **********************************/