341 lines
8.9 KiB
C
341 lines
8.9 KiB
C
/*****************************************************************************
|
|
*
|
|
* stream.c
|
|
*
|
|
* Management of input streams.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "m4.h"
|
|
|
|
#define tsCur 0
|
|
#define tsNormal 0
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FreePstm
|
|
*
|
|
* Free the memory associated with a stream.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void STDCALL
|
|
FreePstm(PSTM pstm)
|
|
{
|
|
AssertPstm(pstm);
|
|
Assert(pstm->hf == hfNil);
|
|
if (pstm->ptchName) {
|
|
FreePv(pstm->ptchName);
|
|
}
|
|
FreePv(pstm->ptchMin);
|
|
FreePv(pstm);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Reading from the stream
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* ptchFindPtchCtchTch
|
|
*
|
|
* Locate the first occurrence of a character in a buffer.
|
|
* Returns 0 if the character is not found.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifdef UNICODE
|
|
|
|
PTCH STDCALL
|
|
ptchFindPtchCtchTch(PCTCH ptch, CTCH ctch, TCH tch)
|
|
{
|
|
for ( ; ctch; ptch++, ctch--) {
|
|
if (*ptch == tch) {
|
|
return ptch;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
#define ptchFindPtchCtchTch(ptch, ctch, tch) memchr(ptch, tch, ctch)
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* ctchDemagicPstmCtch
|
|
*
|
|
* Quote all occurences of tchMagic in the stream. This is called only
|
|
* when you're probably already in trouble, so performance is not an issue.
|
|
*
|
|
* Entry:
|
|
*
|
|
* pstm->ptchMin -> Beginning of buffer
|
|
* pstm->ptchMax -> End of buffer
|
|
* ctch = number of characters to convert
|
|
*
|
|
* Returns:
|
|
*
|
|
* number of characters converted and left in the buffer
|
|
* pstm->ptchMin -> Beginning of buffer
|
|
* pstm->ptchMax -> End of buffer
|
|
*
|
|
* NOTE! that this procedure may reallocate the buffer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/* SOMEDAY! - This causes NULs to come out as tchzero, whatever that is! */
|
|
|
|
CTCH STDCALL
|
|
ctchDemagicPstmCtch(PSTM pstm, CTCH ctch)
|
|
{
|
|
PTCH ptchIn, ptchOut, ptchMax, ptchNew;
|
|
|
|
AssertPstm(pstm);
|
|
ptchNew = ptchAllocCtch(ctch * 2); /* Worst-case output buffer */
|
|
ptchMax = pstm->ptchMin + ctch;
|
|
ptchOut = ptchNew;
|
|
ptchIn = pstm->ptchMin;
|
|
while (ptchIn < ptchMax) {
|
|
if (*ptchIn == tchMagic) {
|
|
*ptchOut++ = tchMagic;
|
|
}
|
|
*ptchOut++ = *ptchIn++;
|
|
}
|
|
FreePv(pstm->ptchMin);
|
|
pstm->ptchMin = ptchNew;
|
|
pstm->ptchMax = ptchNew + ctch * 2;
|
|
return (CTCH)(ptchOut - pstm->ptchMin);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* fFillPstm
|
|
*
|
|
* Refill a stream from its file, if possible.
|
|
*
|
|
* Each file ends with an artificial EOF token, so that we can detect
|
|
* bad things like files that end with incomplete comments or quotes,
|
|
* and so that the last word of one file does not adjoin the first word
|
|
* of the next.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOL STDCALL
|
|
fFillPstm(PSTM pstm)
|
|
{
|
|
AssertPstm(pstm);
|
|
if (pstm->hf != hfNil) {
|
|
CB cb;
|
|
CTCH ctch;
|
|
Assert(pstm->ptchMax - pstm->ptchMin >= ctchFile);
|
|
cb = cbReadHfPvCb(pstm->hf, pstm->ptchMin, cbCtch(ctchFile));
|
|
if (cb == cbErr) {
|
|
Die("error reading file");
|
|
}
|
|
ctch = ctchCb(cb);
|
|
if (cbCtch(ctch) != cb) {
|
|
Die("odd number of bytes in UNICODE file");
|
|
}
|
|
if (ctch) {
|
|
if (ptchFindPtchCtchTch(pstm->ptchMin, ctch, tchMagic)) {
|
|
ctch = ctchDemagicPstmCtch(pstm, ctch);
|
|
}
|
|
pstm->ptchCur = pstm->ptchMax - ctch;
|
|
MovePtchPtchCtch(g_pstmCur->ptchCur, g_pstmCur->ptchMin, ctch);
|
|
} else { /* EOF reached */
|
|
CloseHf(pstm->hf);
|
|
pstm->hf = hfNil;
|
|
PushPtok(&tokEof); /* Eek! Does this actually work? */
|
|
}
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* tchPeek
|
|
*
|
|
* Fetch but do not consume the next character in the stream.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
TCH STDCALL
|
|
tchPeek(void)
|
|
{
|
|
AssertPstm(g_pstmCur);
|
|
while (g_pstmCur->ptchCur >= g_pstmCur->ptchMax) { /* Rarely */
|
|
Assert(g_pstmCur->ptchCur == g_pstmCur->ptchMax);
|
|
if (!fFillPstm(g_pstmCur)) {
|
|
PSTM pstmNew = g_pstmCur->pstmNext;
|
|
Assert(pstmNew != 0);
|
|
FreePstm(g_pstmCur); /* Closes file, etc */
|
|
g_pstmCur = pstmNew;
|
|
}
|
|
}
|
|
return *g_pstmCur->ptchCur;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* tchGet
|
|
*
|
|
* Fetch and consume the next character in the stream.
|
|
*
|
|
* LATER - update line number
|
|
*
|
|
*****************************************************************************/
|
|
|
|
TCH STDCALL
|
|
tchGet(void)
|
|
{
|
|
TCH tch = tchPeek();
|
|
Assert(*g_pstmCur->ptchCur == tch);
|
|
g_pstmCur->ptchCur++;
|
|
return tch;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Pushing
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* UngetTch
|
|
*
|
|
* Ungetting a character is the same as pushing it, except that it goes
|
|
* onto the file stream rather than onto the string stream.
|
|
*
|
|
* LATER - update line number
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void STDCALL
|
|
UngetTch(TCH tch)
|
|
{
|
|
AssertPstm(g_pstmCur);
|
|
Assert(g_pstmCur->ptchCur <= g_pstmCur->ptchMax);
|
|
Assert(g_pstmCur->ptchCur > g_pstmCur->ptchMin);
|
|
g_pstmCur->ptchCur--;
|
|
Assert(*g_pstmCur->ptchCur == tch);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* pstmPushStringCtch
|
|
*
|
|
* Push a fresh string stream of the requested size.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
PSTM STDCALL
|
|
pstmPushStringCtch(CTCH ctch)
|
|
{
|
|
PSTM pstm;
|
|
|
|
Assert(ctch);
|
|
pstm = pvAllocCb(sizeof(STM));
|
|
pstm->pstmNext = g_pstmCur;
|
|
pstm->hf = hfNil;
|
|
pstm->ptchName = 0;
|
|
pstm->ptchMin = ptchAllocCtch(ctch);
|
|
pstm->ptchCur = pstm->ptchMax = pstm->ptchMin + ctch;
|
|
D(pstm->sig = sigStm);
|
|
g_pstmCur = pstm;
|
|
return pstm;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* pstmPushHfPtch
|
|
*
|
|
* Push a fresh file stream with the indicated name.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
PSTM STDCALL
|
|
pstmPushHfPtch(HFILE hf, PTCH ptch)
|
|
{
|
|
PSTM pstm = pstmPushStringCtch(ctchFile);
|
|
pstm->hf = hf;
|
|
pstm->ptchName = ptch;
|
|
return pstm;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* PushPtok
|
|
* PushZPtok
|
|
*
|
|
* Push a token buffer onto the stream, possibly allocating a new
|
|
* top-of-stream if the current top-of-stream isn't big enough to
|
|
* handle it.
|
|
*
|
|
* PushZPtok takes a dummy pdiv argument.
|
|
*
|
|
* LATER - Should also alloc new tos if current tos is a file.
|
|
* This keeps line numbers happy.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void STDCALL
|
|
PushPtok(PCTOK ptok)
|
|
{
|
|
AssertPstm(g_pstmCur);
|
|
/* Assert(tsCur == tsNormal); */ /* Make sure tokenizer is quiet */
|
|
if (ctchSPtok(ptok) > (CTCH)(g_pstmCur->ptchCur - g_pstmCur->ptchMin)) {
|
|
pstmPushStringCtch(max(ctchSPtok(ptok), ctchMinPush));
|
|
}
|
|
g_pstmCur->ptchCur -= ctchSPtok(ptok);
|
|
Assert(g_pstmCur->ptchCur >= g_pstmCur->ptchMin); /* Buffer underflow! */
|
|
CopyPtchPtchCtch(g_pstmCur->ptchCur, ptchPtok(ptok), ctchSPtok(ptok));
|
|
}
|
|
|
|
void STDCALL
|
|
PushZPtok(PDIV pdiv, PCTOK ptok)
|
|
{
|
|
PushPtok(ptok);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* PushTch
|
|
*
|
|
* Push a single character. We do this by creating a scratch token.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void STDCALL
|
|
PushTch(TCH tch)
|
|
{
|
|
TOK tok;
|
|
SetStaticPtokPtchCtch(&tok, &tch, 1);
|
|
PushPtok(&tok);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* PushQuotedPtok
|
|
*
|
|
* Push a token buffer onto the stream, quoted.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void STDCALL
|
|
PushQuotedPtok(PCTOK ptok)
|
|
{
|
|
/* Assert(tsCur == tsNormal); */ /* Make sure tokenizer is quiet */
|
|
/* SOMEDAY -- should be ptokLquo and ptokRquo once we support changing quotes */
|
|
PushTch(tchRquo);
|
|
PushPtok(ptok);
|
|
PushTch(tchLquo);
|
|
}
|