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; 
}