RWINC.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/

/****************************** Module Header *******************************
* Module Name: rwinc.c
*
* Does the include file reading and writing.
*
* Functions:
*
* OpenIncludeFile()
* FreeInclude()
* WriteInc()
* LoadIncludeFile()
* GetChar()
* ReadChar()
* GetLabel()
* GetValue()
* GetWord()
* FindDefine()
* GetNextInc()
* RWToOffset()
* WriteIncChar()
* WriteIncFlush()
* WriteChangedInc()
* WriteDeletedInc()
* WriteAddedInc()
* WriteSymbol()
* WriteIDInc()
*
* Comments:
*
****************************************************************************/

#include "dlgedit.h"
#include "dlgfuncs.h"
#include "dlgextrn.h"

#include <ctype.h>


/*
* Field width that the symbol is printed within. This indirectly
* determines where the id value starts, because blanks are added
* after the symbol is printed up to this width value.
*/
#define CCHSYMFIELDWIDTH 27

/*
* Return codes from the file reading functions.
*/
#define READ_OK 1
#define READ_EOF 2
#define READ_BAD 3
#define READ_WRONG 4
#define BAD_POINTER ((VOID *)0xFFFF)

/*
* Return codes from the GetNextInc function.
*/
#define GNI_DONE 0
#define GNI_NOCHANGE 1
#define GNI_CHANGED 2
#define GNI_DELETED 3
#define GNI_ADDED 4

static BYTE abBuffer[CCHFILEBUFFER]; /* Buffer for read file data. */
static TCHAR achBuffer[CCHFILEBUFFER]; /* Unicode buffer for data. */
static INT cbBuf; /* Pointer into achBuffer. */
static DWORD cchFile; /* Count of characters read. */
static DWORD cchFileMax; /* Max characters in file. */
static DWORD fposLastDefine; /* Saves location of "#define". */
static DWORD fposWordStart; /* Saves start of id value. */
static BOOL fAtNewLine; /* At start or \r or \n. */
static HANDLE hfInclude; /* The current include file. */

STATICFN BOOL LoadIncludeFile(VOID);
STATICFN LPTSTR GetChar(VOID);
STATICFN LPTSTR ReadChar(VOID);
STATICFN INT GetLabel(BOOL *pfDups);
STATICFN INT GetValue(PINT pnValue);
STATICFN INT GetWord(LPTSTR pch);
STATICFN INT FindDefine(VOID);
STATICFN INT GetNextInc(NPLABEL *pplReturn, BOOL fFirst);
STATICFN BOOL RWToOffset(HANDLE hfWrite, DWORD lOffset);
STATICFN BOOL WriteIncChar(HANDLE hfWrite, TCHAR ch);
STATICFN BOOL WriteIncFlush(HANDLE hfWrite);
STATICFN BOOL WriteChangedInc(HANDLE hfWrite, NPLABEL plInc);
STATICFN BOOL WriteDeletedInc(HANDLE hfWrite, NPLABEL plInc);
STATICFN BOOL WriteAddedInc(HANDLE hfWrite, NPLABEL plInc);
STATICFN BOOL WriteSymbol(HANDLE hfWrite, LPTSTR pszSymbol);
STATICFN BOOL WriteIDInc(HANDLE hfWrite, INT id);



/****************************************************************************
* OpenIncludeFile
*
* This function attempts to open and load the include file with name
* pointed to by pszOpenInclude. If pszOpenInclude is just a file name, and
* not a path, then the path is taken from szFullLoadFile. Otherwise
* pszOpenInclude itself is used. The full pathname is put in
* szFullIncludeFile and pszIncludeFile is set to point to just the file
* name in it.
*
* If fDoOpen is TRUE, the file is opened. If it is FALSE, it is assumed
* that hfInc contains the file handle to the opened include file and this
* handle is used. In addition, the caller is responsible for closing
* any passed in file handle if an error occurs.
*
* Any existing includes are freed, szFullIncludeFile is set to the full
* include path, pszIncludeFile is set to the filename portion of this full
* path and hfInclude will contain the file handle to the include file.
*
* Arguments:
* LPTSTR pszOpenInclude - name of the include file to open.
*
* Returns:
* If the load is successful, TRUE is returned. Otherwise,
* FALSE is returned.
*
****************************************************************************/

BOOL OpenIncludeFile(
LPTSTR pszOpenInclude)
{
TCHAR szFullIncludeFileTemp[CCHMAXPATH];
HCURSOR hcurSave;
BOOL fSuccess = FALSE;

hcurSave = SetCursor(hcurWait);

if (FileInPath(pszOpenInclude) == pszOpenInclude) {
lstrcpy(szFullIncludeFileTemp, szFullResFile);
lstrcpy(FileInPath(szFullIncludeFileTemp), pszOpenInclude);
}
else {
lstrcpy(szFullIncludeFileTemp, pszOpenInclude);
}

/*
* Close any existing include file and free memory.
*/
FreeInclude();

if ((hfInclude = CreateFile(szFullIncludeFileTemp, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, NULL)) != (HANDLE)-1) {
if (LoadIncludeFile()) {
lstrcpy(szFullIncludeFile, szFullIncludeFileTemp);
pszIncludeFile = FileInPath(szFullIncludeFile);
fSuccess = TRUE;
}

CloseHandle(hfInclude);
}

/*
* Update the status windows symbol combo box. Update other fields
* also, in case the currently selected control's symbol was affected
* by the reading of the new include file.
*/
StatusFillSymbolList(plInclude);
StatusUpdate();

ShowFileStatus(TRUE);
SetCursor(hcurSave);

return fSuccess;
}



/************************************************************************
* LoadIncludeFile
*
* This function creates or adds to plInclude with all the #define
* statements in the file with handle hfInclude.
*
* Returns:
* TRUE - Load succeeded.
* FALSE - Load failed (a read error).
*
************************************************************************/

STATICFN BOOL LoadIncludeFile(VOID)
{
INT RetCode;
BOOL fDups = FALSE;

/*
* Set char count, get file cb.
*/
cchFile = 0L;
cchFileMax = GetFileSize((HANDLE)hfInclude, NULL);
cbBuf = CCHFILEBUFFER;
fAtNewLine = TRUE;

/*
* Loop through and extract all id definitions.
*/
while ((RetCode = FindDefine()) != READ_EOF) {
if (RetCode == READ_BAD || (RetCode = GetLabel(&fDups)) == READ_BAD) {
Message(MSG_INTERNAL);
return FALSE;
}
}

/*
* Warn the user if there were duplicate symbols,
* or symbols with duplicate ids.
*/
if (fDups)
Message(MSG_IDUPIDS);

return TRUE;
}



/****************************************************************************
* FindDefine
*
* This function looks for ^#define[\s\t], that "#define" at the start
* of a line and followed by a space or a tab.
*
* Returns:
* READ_OK -> All OK & #define found.
* READ_EOF -> All OK, but EOF found before #define.
* READ_BAD = Failure on read.
*
****************************************************************************/

STATICFN INT FindDefine(VOID)
{
LPTSTR pchIn;
LPTSTR pchCmp;
BOOL fLastAtNewLine;

tryagain:

/*
* Skip blank lines looking for a newline followed by a '#'.
*/
while (TRUE) {
fLastAtNewLine = fAtNewLine;
pchIn = GetChar();

if (pchIn == NULL)
return READ_EOF;
else if (pchIn == BAD_POINTER)
return READ_BAD;
else if (fLastAtNewLine && *pchIn == CHAR_POUND)
break;
else if (IsDBCSLeadByte((BYTE)*pchIn))
pchIn = GetChar();
}

/*
* At this point a newline followed by a '#' has been found.
* Begin checking for "define". Save away the file offset,
* in case we have really found one.
*/
fposLastDefine = cchFile - 1;
pchCmp = ids(IDS_DEFINE);
do {
pchIn = GetChar();

if (pchIn == BAD_POINTER)
return READ_BAD;
else if (pchIn == NULL || *pchIn != *pchCmp++)
goto tryagain;
} while (*pchCmp);

/*
* Finally, look for the trailing space or tab after the "#define".
*/
pchIn = GetChar();
if (pchIn == BAD_POINTER)
return READ_BAD;
else if (pchIn == NULL || (*pchIn != CHAR_SPACE && *pchIn != CHAR_TAB))
goto tryagain;

return READ_OK;
}



/************************************************************************
* GetLabel
*
* This function gets the next two words from the file hfInclude and treats
* them as a label and id, respectively. It allocates another LABEL
* and string to hold this information.
*
* Arguments:
* BOOL *pfDups = Points to a BOOL that will be set to TRUE if AddLabel
* finds a duplicate symbol or id.
*
* Returns:
* READ_OK -> All OK.
* READ_BAD = Failure on read.
*
************************************************************************/

STATICFN INT GetLabel(
BOOL *pfDups)
{
INT id;
INT RetCode;
TCHAR szLabel[CCHTEXTMAX];

/*
* Get string and ID at current position
*/
switch (RetCode = GetWord(szLabel)) {
case READ_OK:
if ((RetCode = GetValue(&id)) == READ_OK) {
AddLabel(szLabel, id, fposLastDefine,
(INT)(fposWordStart - fposLastDefine),
&plInclude, &plDelInclude, NULL, pfDups);
}

break;

default:
break;
}

return RetCode;
}



/************************************************************************
* GetWord
*
* This function uses GetChar to get the next word from the include
* file. First it removes tabs and spaces, then it collects everything
* to the next white space. Finally it null terminates the word.
*
* Arguments:
* LPTSTR pch - Where to put the word.
*
* Returns:
* READ_OK - a word was found.
* READ_EOF - EOF was found.
* READ_BAD - Error on Read.
* READ_WRONG - Found other than ' ' or '\t' followed by a letter,
* number or _, +, -.
*
************************************************************************/

STATICFN INT GetWord(
LPTSTR pch)
{
TCHAR ch;
LPTSTR pchIn;

/*
* Skip spaces.
*/
while ((pchIn = GetChar()) != NULL && pchIn != BAD_POINTER &&
((ch = *pchIn) == CHAR_SPACE || ch == CHAR_TAB))
;

/*
* Errors or EOF?
*/
if (pchIn == NULL)
return READ_EOF;
else if (pchIn == BAD_POINTER)
return READ_BAD;
if (!iscsym(ch) && ch != CHAR_MINUS && ch != CHAR_PLUS)
return READ_WRONG;

/*
* Save starting location of the word in the file.
*/
fposWordStart = cchFile - 1;

/*
* Pick out the current word.
*/
do {
*pch++ = ch;
} while ((pchIn = GetChar()) != NULL && pchIn != BAD_POINTER &&
(ch = *pchIn) != CHAR_SPACE && ch != CHAR_TAB &&
ch != CHAR_NEWLINE && ch != CHAR_RETURN);

/*
* Errors or EOF?
*/
if (pchIn == NULL)
return READ_WRONG;
else if (pchIn == BAD_POINTER)
return READ_BAD;

/*
* Null terminate the word.
*/
*pch = (TCHAR)0;

return READ_OK;
}



/************************************************************************
* GetChar
*
* This function returns a pointer to the next character in the
* stream hfInclude. It calls ReadChar to do the actual work.
*
* As it is reading the stream, it will compress a comment sequence to
* a single space. This means that from a slash+asterisk to the next
* asterisk+slash and from a pair of slashes to the end of the line all
* that will be returned is a single space character.
*
* Returns:
* A pointer to next character in the stream hfInclude.
* NULL => End of file.
* BAD_POINTER => Problems reading file.
*
************************************************************************/

STATICFN LPTSTR GetChar(VOID)
{
register LPTSTR pch;

/*
* Read the next character.
*/
pch = ReadChar();
if (pch == NULL || pch == BAD_POINTER)
return pch;

/*
* Possibly starting a comment?
*/
if (*pch == CHAR_SLASH) {
/*
* Starting a traditional comment?
*/
if (*(pch + 1) == CHAR_ASTERISK) {
/*
* Read the '*'.
*/
pch = ReadChar();
if (pch == NULL || pch == BAD_POINTER)
return pch;

/*
* Read until the next asterisk+slash is found.
*/
do {
pch = ReadChar();
if (pch == NULL || pch == BAD_POINTER)
return pch;
} while (*pch != CHAR_ASTERISK || *(pch + 1) != CHAR_SLASH);

/*
* Read the final '/'.
*/
pch = ReadChar();
if (pch == NULL || pch == BAD_POINTER)
return pch;

/*
* Change it to a space.
*/
*pch = CHAR_SPACE;
}
/*
* Starting a single line comment?
*/
else if (*(pch + 1) == CHAR_SLASH) {
/*
* Read up to the end of line.
*/
do {
pch = ReadChar();
if (pch == NULL || pch == BAD_POINTER)
return pch;
} while (*(pch + 1) != CHAR_RETURN && *(pch + 1) != CHAR_NEWLINE);

/*
* Convert the last character before the newline into a space.
*/
*pch = CHAR_SPACE;
}
}

return pch;
}



/************************************************************************
* ReadChar
*
* This function returns a pointer to the next character in the
* stream hfInclude, but does it in a buffered fashion. That is, abBuffer
* is filled from hfInclude and pointers are returned to there.
* Note that after ReadChar is called, all previous pointers
* returned are meaningless.
*
* Returns:
* A pointer to next character in the stream hfInclude.
* NULL => End of file.
* BAD_POINTER => Problems reading file.
*
* Comments:
* May cause abBuffer to be filled from file with handle hfInclude.
* cbBuf is changed.
* cchFile is changed.
* Sets fAtNewLine = TRUE if char returned is '\n' or '\r', or FALSE
* otherwise. Not changed unless a character is returned.
*
************************************************************************/

STATICFN LPTSTR ReadChar(VOID)
{
register LPTSTR pch;
INT cbRead;

if (cchFile >= cchFileMax)
return NULL;

if (cbBuf >= CCHFILEBUFFER) {
if ((cbRead = _lread((HFILE)hfInclude, abBuffer, CCHFILEBUFFER)) == -1)
return BAD_POINTER;

MultiByteToWideChar(CP_ACP, 0, abBuffer, cbRead, achBuffer,
CCHFILEBUFFER);

cbBuf = 0;
}

pch = achBuffer + cbBuf;
cbBuf++;
cchFile++;

if (*pch == CHAR_DOSEOF) {
cchFile = cchFileMax;
return NULL;
}

fAtNewLine = (*pch == CHAR_RETURN || *pch == CHAR_NEWLINE) ? TRUE : FALSE;

return pch;
}



/************************************************************************
* GetValue
*
* This function reads the next word in the file hfInclude with GetWord
* and converts that word to a number.
*
* If the second character of the word is an 'x' or 'X', the word is
* assumed to be a hex number and it is converted appropriately.
*
* Arguments:
* npsValue - Where to put the value of the next word in file.
*
* Returns:
* READ_OK - success.
* READ_BAD - am error occured.
* READ_WRONG - Something other than a number was found.
*
************************************************************************/

STATICFN INT GetValue(
PINT pnValue)
{
TCHAR achValue[CCHTEXTMAX];
LPTSTR pch;
INT RetValue;

*pnValue = 0;
if ((RetValue = GetWord(achValue)) != READ_OK)
return RetValue;

/*
* Verify we have only a number.
*/
pch = achValue;
if (pch[1] == CHAR_CAP_X || pch[1] == CHAR_X) {
if (*pch != CHAR_0) {
RetValue = READ_WRONG;
}
else {
for (pch += 2; *pch; pch++) {
if (!iswxdigit(*pch)) {
RetValue = READ_WRONG;
break;
}
}

if (RetValue == READ_OK)
*pnValue = axtoi(&achValue[2]);
}
}
else {
if (!iswdigit(*pch) && *pch != CHAR_MINUS && *pch != CHAR_PLUS) {
RetValue = READ_WRONG;
}
else {
for (pch++; *pch; pch++) {
if (!iswdigit(*pch)) {
RetValue = READ_WRONG;
break;
}
}

if (RetValue == READ_OK)
*pnValue = awtoi(achValue);
}
}

return RetValue;
}



/************************************************************************
* FreeInclude
*
* This function frees the memory associated with an include file,
* sets global variables to match, and closes the currently open
* include file. It frees plInclude, plDelInclude and all the LABELs in them.
* It sets gfIncChged to FALSE, sets pszIncludeFile to NULL, and
* closes any open include file.
*
************************************************************************/

VOID FreeInclude(VOID)
{
FreeLabels(&plInclude);
FreeLabels(&plDelInclude);
gfIncChged = FALSE;
pszIncludeFile = NULL;
}



/************************************************************************
* WriteInc
*
* This function writes the labels in plInclude to an include file.
*
* Arguments:
* HANDLE hfWrite - handle to the file to write to.
*
* Returns:
* TRUE if successful, FALSE if not.
*
************************************************************************/

BOOL WriteInc(
HANDLE hfWrite)
{
INT nGNIRet;
NPLABEL plInc;
BOOL fEOF;

/*
* Is there an include file already specified? If so,
* open it. If not, we are effectively at EOF now.
*/
if (pszIncludeFile) {
if ((hfInclude = CreateFile(szFullIncludeFile, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, NULL)) == (HANDLE)-1) {
//if the include file is missing or locked...
return FALSE;
}

fEOF = FALSE;
}
else {
fEOF = TRUE;
}

cchFile = 0;
cbWritePos = 0;
cbBuf = CCHFILEBUFFER;
fAtNewLine = TRUE;

/*
* Loop through all the includes.
*/
nGNIRet = GetNextInc(&plInc, TRUE);
while (nGNIRet != GNI_DONE) {
switch (nGNIRet) {
case GNI_NOCHANGE:
break;

case GNI_CHANGED:
if (!WriteChangedInc(hfWrite, plInc))
return FALSE;

break;

case GNI_DELETED:
if (!WriteDeletedInc(hfWrite, plInc))
return FALSE;

break;

case GNI_ADDED:
/*
* The first time we reach an added label, we know that
* there are no more changed or deleted ones to handle
* so we read/write up to the end of the old include file.
* This only has to be done once.
*/
if (!fEOF) {
if (!RWToOffset(hfWrite, FPOS_MAX))
return FALSE;

fEOF = TRUE;

/*
* In the unlikely case that the read include file
* does not end with a carriage return and/or
* linefeed character, add them before beginning
* to write added labels. This ensures that the
* first label added always starts on a new line.
*/
if (!fAtNewLine) {
if (!WriteIncChar(hfWrite, CHAR_RETURN))
return FALSE;

if (!WriteIncChar(hfWrite, CHAR_NEWLINE))
return FALSE;
}
}

if (!WriteAddedInc(hfWrite, plInc))
return FALSE;

break;
}

nGNIRet = GetNextInc(&plInc, FALSE);
}

/*
* Write the rest of the file, if there is any left.
*/
if (!fEOF)
if (!RWToOffset(hfWrite, FPOS_MAX))
return FALSE;

/*
* Flush any remaining characters in the write buffer.
*/
if (!WriteIncFlush(hfWrite))
return FALSE;

/*
* If we just opened the old include file, close it.
*/
if (pszIncludeFile)
CloseHandle(hfInclude);

return TRUE;
}



/************************************************************************
* GetNextInc
*
* This routine will return the next label in the plInclude and plDelInclude
* linked lists, as well as the status of the returned label.
*
* The labels will be returned in order based upon their location in the
* lists, which is their order found in the include file if their fpos
* field is not FPOS_MAX. This routine looks at the next label in both the
* plInclude and plDelInclude lists and returns the one with the lowest
* fpos. Labels are returned from plInclude and plDelInclude in order of
* their fpos until all have been returned with a valid fpos. After this,
* all new includes are returned from plInclude.
*
* Call it with fFirst equal to TRUE to initialize it.
*
* Arguments:
* NPLABEL *pplReturn - label to return.
* BOOL fFirst - TRUE if initializing.
*
* Returns:
* GNI_DONE - No more labels exist.
* GNI_NOCHANGE - An existing label is being returned.
* GNI_CHANGED - An existing label with a changed id is being returned.
* GNI_DELETED - A deleted label is being returned.
* GNI_ADDED - An added label is being returned.
*
************************************************************************/

STATICFN INT GetNextInc(
NPLABEL *pplReturn,
BOOL fFirst)
{
static NPLABEL plCur;
static NPLABEL plDelCur;

/*
* Initialize if this is the first time.
*/
if (fFirst) {
plCur = plInclude;
plDelCur = plDelInclude;
}

/*
* Are we out of valid includes?
*/
if (!plCur) {
/*
* If there are deleted ones left, return the next one.
* Otherwise we are done.
*/
if (plDelCur) {
*pplReturn = plDelCur;
plDelCur = plDelCur->npNext;
return GNI_DELETED;
}
else {
return GNI_DONE;
}
}
/*
* Have we reached the added includes (fpos == FPOS_MAX)?
*/
else if (plCur->fpos == FPOS_MAX) {
/*
* If there are deleted ones remaining, return them first.
* Otherwise, return the next added one.
*/
if (plDelCur) {
*pplReturn = plDelCur;
plDelCur = plDelCur->npNext;
return GNI_DELETED;
}
else {
*pplReturn = plCur;
plCur = plCur->npNext;
return GNI_ADDED;
}
}
else {
/*
* Return either the next label or the next deleted label,
* based on whether there are any deleted labels and who
* has the lowest file position (fpos).
*/
if (plDelCur && plDelCur->fpos < plCur->fpos) {
*pplReturn = plDelCur;
plDelCur = plDelCur->npNext;
return GNI_DELETED;
}
else {
*pplReturn = plCur;
plCur = plCur->npNext;
/*
* Return either GNI_CHANGE or GNI_NOCHANGE based on
* whether the original id value has been changed.
*/
return ((*pplReturn)->id == (*pplReturn)->idOrig) ?
GNI_NOCHANGE : GNI_CHANGED;
}
}
}



/************************************************************************
* RWToOffset
*
* This routine reads from the current include file and writes to the
* hfWrite file up to the lOffset position in the file. If lOffset is
* set to FPOS_MAX, reads/writes are performed up to the end of the
* read file.
*
* Arguments:
* HANDLE hfWrite - handle to the file to write to.
* DWORD lOffset - where to write up to.
*
* Returns:
* TRUE if successful, FALSE if not.
*
* Comments:
* This routine relies on cchFile and cchFileMax to be properly updated
* by the reading and writing routines.
*
************************************************************************/

STATICFN BOOL RWToOffset(
HANDLE hfWrite,
DWORD lOffset)
{
LPTSTR pchIn;
DWORD cbWrite;

if (lOffset == FPOS_MAX)
lOffset = cchFileMax;

for (cbWrite = lOffset - cchFile; cbWrite; cbWrite--) {
/*
* NULL can be returned if there is an EOF character found in
* the file. This is not an error, and we will stop reading
* and writing at this point.
*/
if ((pchIn = ReadChar()) == NULL)
return TRUE;

/*
* If BAD_POINTER is returned, there was an error reading the
* include file.
*/
if (pchIn == BAD_POINTER)
return FALSE;

/*
* Write out the character.
*/
if (!WriteIncChar(hfWrite, *pchIn))
return FALSE;
}

return TRUE;
}



/************************************************************************
* WriteIncChar
*
* This routine writes a character (ch) to the hfWrite file, doing it in a
* buffered fashion. Because it is buffered, before closing the file
* any remaining characters in the buffer must be "flushed" to disk.
*
* Arguments:
* HANDLE hfWrite - handle to the file to write to.
* TCHAR ch - character to write.
*
* Returns:
* TRUE if successful, FALSE if not.
*
* Comments:
* The globals gachWriteBuffer and cbWritePos are updated by this routine.
*
************************************************************************/

STATICFN BOOL WriteIncChar(
HANDLE hfWrite,
TCHAR ch)
{
INT cbWritten;

gachWriteBuffer[cbWritePos++] = ch;

/*
* Is the buffer full?
*/
if (cbWritePos == CCHFILEBUFFER) {
CHAR abWriteBuffer[CCHFILEBUFFER];
BOOL fDefCharUsed;

WideCharToMultiByte(CP_ACP, 0, gachWriteBuffer, CCHFILEBUFFER,
abWriteBuffer, CCHFILEBUFFER, NULL, &fDefCharUsed);

cbWritten = (INT)_lwrite((HFILE)hfWrite, abWriteBuffer, cbWritePos);
if (cbWritten != cbWritePos)
return FALSE;

cbWritePos = 0;
}

return TRUE;
}



/************************************************************************
* WriteIncFlush
*
* This routine flushes the write buffer. This must be done before
* the file is closed or data can be lost.
*
* Arguments:
* HANDLE hfWrite - handle to the file to write to.
*
* Returns:
* TRUE if successful, FALSE if not.
*
* Comments:
* The global cbWritePos is updated by this routine.
*
************************************************************************/

STATICFN BOOL WriteIncFlush(
HANDLE hfWrite)
{
INT cbWritten;

/*
* Are any bytes remaining in the buffer?
*/
if (cbWritePos) {
CHAR abWriteBuffer[CCHFILEBUFFER];
BOOL fDefCharUsed;


WideCharToMultiByte(CP_ACP, 0, gachWriteBuffer, cbWritePos,
abWriteBuffer, CCHFILEBUFFER, NULL, &fDefCharUsed);

cbWritten = (INT)_lwrite((HFILE)hfWrite, abWriteBuffer, cbWritePos);
if (cbWritten != cbWritePos)
return FALSE;

cbWritePos = 0;
}

return TRUE;
}



/************************************************************************
* WriteChangedInc
*
* This routine writes out a label that has had its id changed since the
* include file was last read.
*
* Arguments:
* HANDLE hfWrite - File to write to.
* NPLABEL plInc - Label to write.
*
* Returns:
* TRUE if successful, FALSE if not.
*
* History:
* 03/13/90 Byron Dazey - Created.
************************************************************************/

STATICFN BOOL WriteChangedInc(
HANDLE hfWrite,
NPLABEL plInc)
{
TCHAR ch;
LPTSTR pchIn;

if (!RWToOffset(hfWrite, plInc->fpos + plInc->nValueOffset))
return FALSE;

/*
* Consume the old id value (up to the next space, tab,
* beginning of a comment, newline or return).
*/
while ((pchIn = ReadChar()) != NULL && pchIn != BAD_POINTER &&
(ch = *pchIn) != CHAR_SPACE && ch != CHAR_TAB &&
ch != CHAR_SLASH && ch != CHAR_NEWLINE && ch != CHAR_RETURN)
;

/*
* It is an error if ReadChar returns BAD_POINTER. Note that it
* is NOT an error if it reaches EOF (and returns NULL).
*/
if (pchIn == BAD_POINTER)
return FALSE;

/*
* Write the new one.
*/
if (!WriteIDInc(hfWrite, plInc->id))
return FALSE;

/*
* Remember to write the last character read after the old value.
*/
if (pchIn != NULL)
if (!WriteIncChar(hfWrite, *pchIn))
return FALSE;

return TRUE;
}



/************************************************************************
* WriteDeletedInc
*
* This routine deletes a label in the include file, closing up the
* space. The entire line will be deleted, unless a comment is found
* after the id value. If so, the comment and following characters will
* be left.
*
* Arguments:
* HANDLE hfWrite - File to write to.
* NPLABEL plInc - Label to delete.
*
* Returns:
* TRUE if successful, FALSE if not.
*
* History:
* 03/13/90 Byron Dazey - Created.
************************************************************************/

STATICFN BOOL WriteDeletedInc(
HANDLE hfWrite,
NPLABEL plInc)
{
register INT i;
TCHAR ch;
LPTSTR pchIn;

/*
* Read and write up to the #define to be deleted.
*/
if (!RWToOffset(hfWrite, plInc->fpos))
return FALSE;

/*
* Consume up to the id value.
*/
for (i = plInc->nValueOffset; i; i--)
if ((pchIn = ReadChar()) == NULL || pchIn == BAD_POINTER)
return FALSE;

/*
* Consume the id value and following characters up to the end of
* the line or the beginning of a comment.
*/
while ((pchIn = ReadChar()) != NULL && pchIn != BAD_POINTER &&
(ch = *pchIn) != CHAR_NEWLINE && ch != CHAR_RETURN &&
ch != CHAR_SLASH)
;

if (pchIn == BAD_POINTER)
return FALSE;

/*
* We are done if we have reached EOF.
*/
if (pchIn == NULL)
return TRUE;

/*
* If the beginning of a comment was found, be sure to write the
* character back out and leave the rest of the comment.
*/
if (ch == CHAR_SLASH) {
if (!WriteIncChar(hfWrite, ch))
return FALSE;
}
else {
/*
* At this point either a newline or a return was found
* and we are going to consume it. We also want to check
* for a return following the newline, or a newline
* following the return and consume it also.
*/
if ((ch == CHAR_NEWLINE && *(pchIn + 1) == CHAR_RETURN) ||
(ch == CHAR_RETURN && *(pchIn + 1) == CHAR_NEWLINE))
if (ReadChar() == BAD_POINTER)
return FALSE;
}

return TRUE;
}



/************************************************************************
* WriteAddedInc
*
* Adds a label to the new include file.
*
* Arguments:
* HANDLE hfWrite - File to write to.
* NPLABEL plInc - Label to add.
*
* Returns:
* TRUE if successful, FALSE if not.
*
* History:
* 03/13/90 Byron Dazey - Created.
************************************************************************/

STATICFN BOOL WriteAddedInc(
HANDLE hfWrite,
NPLABEL plInc)
{
register LPTSTR psz;

/*
* Write the "#define " string.
*/
psz = ids(IDS_POUNDDEFINE);
while (*psz)
if (!WriteIncChar(hfWrite, *psz++))
return FALSE;

/*
* Write the symbol, followed by a space.
*/
if (!WriteSymbol(hfWrite, plInc->pszLabel))
return FALSE;
if (!WriteIncChar(hfWrite, CHAR_SPACE))
return FALSE;

/*
* Write the id, followed by a carriage return and newline.
*/
if (!WriteIDInc(hfWrite, plInc->id))
return FALSE;
if (!WriteIncChar(hfWrite, CHAR_RETURN))
return FALSE;
if (!WriteIncChar(hfWrite, CHAR_NEWLINE))
return FALSE;

return TRUE;
}



/************************************************************************
* WriteSymbol
*
* Writes out a "#define DID_xxx " string to hfWrite. If the symbol
* is smaller than CCHSYMFIELDWIDTH, it will be padded with spaces out
* to this width.
*
* Arguments:
* HANDLE hfWrite - handle to the file to write to.
* LPTSTR pszSymbol - symbol to write.
*
* Returns:
* TRUE if successful, FALSE if not.
*
************************************************************************/

STATICFN BOOL WriteSymbol(
HANDLE hfWrite,
LPTSTR pszSymbol)
{
register INT cch;

/*
* Write the symbol.
*/
cch = 0;
while (*pszSymbol) {
if (!WriteIncChar(hfWrite, *pszSymbol++))
return FALSE;

cch++;
}

/*
* Pad the field with blanks out to CCHSYMFIELDWIDTH, if necessary.
*/
if (cch < CCHSYMFIELDWIDTH) {
cch = CCHSYMFIELDWIDTH - cch;
while (cch--)
if (!WriteIncChar(hfWrite, CHAR_SPACE))
return FALSE;
}

return TRUE;
}



/************************************************************************
* WriteIDInc
*
* Writes out an id value to the hfWrite file. The format will be in
* either hex or decimal, depending on the current mode.
*
* Arguments:
* HANDLE hfWrite - File to write to.
* INT id - ID to write.
*
* Returns:
* TRUE if successful, FALSE if not.
*
************************************************************************/

STATICFN BOOL WriteIDInc(
HANDLE hfWrite,
INT id)
{
register LPTSTR psz;
TCHAR szValue[CCHIDMAX + 1];

Myitoa(id, szValue);

psz = szValue;
while (*psz)
if (!WriteIncChar(hfWrite, *psz++))
return FALSE;

return TRUE;
}