Пример #1
0
//---------------------------------------------------------------------------
// GetRawLine
//
// This function "reads" a line of "raw" source from the given script memory
// and places it in the given line buffer.
//
// RETURNS:     TRUE if successful, or FALSE if end-of-script reached
//---------------------------------------------------------------------------
BOOL NEAR GetRawLine (LPSTR linebuf)
{
    register    CHAR    c;
    register    INT     idx;

    // Get out quick if already at EOF
    //-----------------------------------------------------------------------
    while (!*(CUR_FILE.lpText))
        if (!PopScannerStatus ())
            return (FALSE);

    // Bump up to next line...
    //-----------------------------------------------------------------------
    CUR_FILE.nLineNo++;
    while (*(CUR_FILE.lpText) == '\r')
        {
        CUR_FILE.nLineNo++;
        CUR_FILE.lpText += 2;  // bump past "\r\n"
        }

    // Copy the line, stopping where appropriate
    //-----------------------------------------------------------------------
    idx = 0;
    while ((c = *(CUR_FILE.lpText++)) && (c != '\r'))
        {
        if (idx++ == MAXLINE)
            {
            ScanError (SCN_TOOLONG);
            return (FALSE);
            }
        *linebuf++ = c;
        }
    if (!c)
        CUR_FILE.lpText--;
    else
        CUR_FILE.lpText++;
    *linebuf = 0;

    // Keep track of the number of lines grabbed, and call the compilation
    // dialog update routine (if we're supposed to)
    //-----------------------------------------------------------------------
    if (fDoCmpDlg)
        {
        iTotalLines++;
        if (++iUpdCount == 100)
            {
            if (!UpdateCompDlg (0, NULL, CUR_FILE.nLineNo, iTotalLines))
                ScanError (SCN_USERBRK);
            iUpdCount = 0;
            }
        }

    return (TRUE);
}
Пример #2
0
//---------------------------------------------------------------------------
// LoadIncludeFile
//
// This function uses the callback loader to load in a new file, into a new
// FILEINFO structure in lpFI.  Scanning/parsing then resumes from this newly
// loaded include file.
//
// RETURNS:     Nothing
//---------------------------------------------------------------------------
VOID NEAR LoadIncludeFile ()
{
    LPSTR   tok, newfile;

    // If there's no room for another include file, tell the user so...
    //-----------------------------------------------------------------------
    if ((CUR_FILE.nDepth == MAXINC) || (nFileAvail == MAXSF))
        {
        ScanError (SCN_INCDEEP);
        return;
        }

    // Get the script file name.  We just get the next token (which had best
    // be the opening ' mark), and then null-terminate the file name at the
    // closing ' mark (which had best be there).
    //-----------------------------------------------------------------------
    NextToken();
    if (curtok[0] != '\'')
        {
        ScanError (SCN_INCERR);
        return;
        }
    newfile = szLine + idx;
    tok = _fstrchr (newfile, '\'');
    if (!tok)
        {
        ScanError (SCN_INCERR);
        return;
        }
    *tok = 0;
    iEnd = idx + _fstrlen (newfile) + 1;

    // Ask the callback loader to load the file we need and give us a pointer
    // to it
    //-----------------------------------------------------------------------
    AVAIL_FILE.lpText = CBLoader (newfile, CUR_FILE.nDepth + 1, nFileAvail,
                                  TRUE, AVAIL_FILE.szName);
    if (!AVAIL_FILE.lpText)
        {
        ScanError (SCN_INCFILE);
        return;
        }

    // The file is loaded.  Finish the initialization of this FI entry, and
    // the scanner is ready to start taking its input from the new file.
    //-----------------------------------------------------------------------
    AVAIL_FILE.nLineNo = 0;
    AVAIL_FILE.pIfStack = pIfStack;
    AVAIL_FILE.nParent = nFileIdx;
    AVAIL_FILE.nDepth = CUR_FILE.nDepth + 1;
    AVAIL_FILE.fLoaded = TRUE;
    nFileIdx = nFileAvail++;
    lpCurFI = (lpFI + nFileIdx);
}
Пример #3
0
//---------------------------------------------------------------------------
// PopScannerStatus
//
// This function "restores" scanner status such that it can resume scanning
// at the previous file.
//
// RETURNS:     TRUE if a file was successfully popped, or FALSE if not
//---------------------------------------------------------------------------
BOOL NEAR PopScannerStatus ()
{
    // Get out quick if nothing to pop (the current file has no parent)
    //-----------------------------------------------------------------------
    if (CUR_FILE.nParent == -1)
        return (FALSE);

    // Check the IFDEF stack.  If there's more (or less) there than there was
    // before we got to this file, we shouldn't be at EOF, so give a
    // ScanError.
    //-----------------------------------------------------------------------
    if (CUR_FILE.pIfStack != pIfStack)
        {
        ScanError (SCN_ENDIFEXP);
        return (FALSE);
        }

    // By setting nFileIdx to the current file's parent index, we
    // automatically point to the previous location in the previous file.
    // Then, tell the callback loader that we don't need the file anymore so
    // that it can do whatever it must to "unload" it.
    //-----------------------------------------------------------------------
    CBLoader (CUR_FILE.szName, CUR_FILE.nDepth, nFileIdx, FALSE, NULL);
    CUR_FILE.fLoaded = FALSE;
    nFileIdx = CUR_FILE.nParent;
    lpCurFI = (lpFI + nFileIdx);

    // Now (if we need to) update the compilation dialog
    //-----------------------------------------------------------------------
    if (fDoCmpDlg)
        UpdateCompDlg (0, CUR_FILE.szName, CUR_FILE.nLineNo, iTotalLines);
    return (TRUE);
}
Пример #4
0
//---------------------------------------------------------------------------
// CompleteLine
//
// This function makes sure the rest of the stuff on the line is a comment,
// if there is anything
//
// RETURNS:     Nothing (produces scan error if not a comment)
//---------------------------------------------------------------------------
VOID NEAR CompleteLine ()
{
    // Check the next token for a comment.  Give an error if it isn't...
    //-----------------------------------------------------------------------
    NextToken();
    if (curtok[0])
        if ((curtok[0] != '\'') &&
            (_stricmp (curtok, szREM)))
            ScanError (SCN_SYNTAX);
}
Пример #5
0
//---------------------------------------------------------------------------
// SymbolExpression
//
// This function invokes the symbol expression evaluator.
//
// RETURNS:     Truth value of expression
//---------------------------------------------------------------------------
BOOL NEAR SymbolExpression ()
{
    BOOL    t;

    NextToken();
    t = SymExpA();

    if ((curtok[0]) && (curtok[0] != '\''))
        ScanError (SCN_LOGIC);

    return (t);
}
Пример #6
0
//---------------------------------------------------------------------------
// NextToken
//
// This function places the next token in (szLine+idx) in curtok[].
//
// RETURNS:     Pointer to curtok[]
//---------------------------------------------------------------------------
CHAR * NEAR NextToken ()
{
    INT     i;

    // Skip past white space
    //-----------------------------------------------------------------------
    while ((szLine[idx] == ' ') ||
           (szLine[idx] == 26) ||
           (szLine[idx] == '\t'))
        idx++;

    // See if this token is a one-char token.  If nothing else on the line,
    // return now (empty token -> end of line)
    //-----------------------------------------------------------------------
    iBeg = idx;
    iEnd = idx + 1;
    if (!szLine[idx])
        {
        curtok[0] = 0;
        return (curtok);
        }

    curtok[0] = szLine[idx++];
    i = 1;
    if (strchr (szSpecials, curtok[0]))
        {
        curtok[1] = 0;
        return (curtok);
        }

    // Whatever this is, we'll copy it until we see one of our specials or
    // some whitespace...
    //-----------------------------------------------------------------------
    while ((szLine[idx] != ' ') && (szLine[idx] != '\t') &&
           (szLine[idx] != 26) &&
           (!strchr(szSpecials, szLine[idx])))
        {
        if (i < MAXSYMLEN)
            curtok[i++] = szLine[idx];
        else
            {
            ScanError (SCN_LONGSYM);
            break;
            }
        idx++;
        iEnd++;
        }

    // Null-terminate our token and return it
    //-----------------------------------------------------------------------
    curtok[i] = 0;
    return (curtok);
}
Пример #7
0
//---------------------------------------------------------------------------
// SymExpC
//
// This is the bottom level of the symbol expression evaluator.
//
// RETURNS:     Truth value of sub-expression
//---------------------------------------------------------------------------
BOOL NEAR SymExpC ()
{
    BOOL    t;

    // See if current token is "NOT"
    //-----------------------------------------------------------------------
    if (!_stricmp (curtok, "NOT"))
        {
        NextToken ();
        return (!SymExpC());
        }

    // Look for parenthesis
    //-----------------------------------------------------------------------
    if (curtok[0] == '(')
        {
        NextToken ();
        t = SymExpA();
        if (fScanErr)
            return (t);
        if (curtok[0] != ')')
            ScanError (SCN_PAREN);
        NextToken ();
        return (t);
        }

    // Maybe this idiot didn't put ANYTHING on this line.  Check for \n
    //-----------------------------------------------------------------------
    if (curtok[0] == 0)
        {
        ScanError (SCN_SYMEXP);
        return (0);
        }

    // Assume this token is a symbol.  Check to see if it's in the table
    //-----------------------------------------------------------------------
    t = IsDefined (pSymRoot, curtok);
    NextToken();
    return (t);
}
Пример #8
0
//---------------------------------------------------------------------------
// ParseCommentLine
//
// This function parses the comment line currently in szLine and returns its
// metacommand type (if it is one) or MC_COMMENT if it is not.
//
// RETURNS:     Type of metacommand/comment
//---------------------------------------------------------------------------
INT NEAR ParseCommentLine ()
{
    // See if the next token's a '$' -- if not, this is not a metacommand
    //-----------------------------------------------------------------------
    NextToken();
    if (curtok[0] != '$')
        return (MC_COMMENT);

    // We've got a metacommand - get the next token and find out what kind it
    // is -- if we don't recognize it, produce a metacommand error.
    //-----------------------------------------------------------------------
    NextToken();
    if (!_stricmp (curtok, "IFDEF"))
        return (MC_IFDEF);
    else if (!_stricmp (curtok, "IFNDEF"))
        return (MC_IFNDEF);
    else if (!_stricmp (curtok, "ELSEIFDEF"))
        return (MC_ELSEIFDEF);
    else if (!_stricmp (curtok, "ELSEIFNDEF"))
        return (MC_ELSEIFNDEF);
    else if (!_stricmp (curtok, "ELSE"))
        return (MC_ELSE);
    else if (!_stricmp (curtok, "ENDIF"))
        return (MC_ENDIF);
    else if (!_stricmp (curtok, "DEFINE"))
        return (MC_DEFINE);
    else if (!_stricmp (curtok, "UNDEF"))
        return (MC_UNDEF);
    else if (!_stricmp (curtok, "INCLUDE"))
        return (MC_INCLUDE);
    else if (!_stricmp (curtok, "INCLUDE:"))
        return (MC_INCLUDE);

    ScanError (SCN_METAERR);
    return (MC_COMMENT);
}
Пример #9
0
SCAN_CODE ScanTree::FindProc(FindData *FD)
{
  if (*CurMask==0)
    return SCAN_NEXT;
  bool FastFindFile=false;
  
  if (FindStack[Depth]==NULL) // No FindFile object for this depth yet.
  {
    bool Wildcards=IsWildcard(CurMask);

    // If we have a file name without wildcards, we can try to use
    // FastFind to optimize speed. For example, in Unix it results in
    // stat call instead of opendir/readdir/closedir.
    bool FindCode=!Wildcards && FindFile::FastFind(CurMask,FD,GetLinks);

    // Link check is important for NTFS, where links can have "Directory"
    // attribute, but we do not want to recurse to them in "get links" mode.
    bool IsDir=FindCode && FD->IsDir && (!GetLinks || !FD->IsLink);

    // SearchAll means that we'll use "*" mask for search, so we'll find
    // subdirectories and will be able to recurse into them.
    // We do not use "*" for directories at any level or for files
    // at top level in recursion mode. We always comrpess the entire directory
    // if folder wildcard is specified.
    bool SearchAll=!IsDir && (Depth>0 || Recurse==RECURSE_ALWAYS ||
                   FolderWildcards && Recurse!=RECURSE_DISABLE || 
                   Wildcards && Recurse==RECURSE_WILDCARDS || 
                   ScanEntireDisk && Recurse!=RECURSE_DISABLE);
    if (Depth==0)
      SearchAllInRoot=SearchAll;
    if (SearchAll || Wildcards)
    {
      // Create the new FindFile object for wildcard based search.
      FindStack[Depth]=new FindFile;

      wchar SearchMask[NM];
      wcsncpyz(SearchMask,CurMask,ASIZE(SearchMask));
      if (SearchAll)
        SetName(SearchMask,MASKALL,ASIZE(SearchMask));
      FindStack[Depth]->SetMask(SearchMask);
    }
    else
    {
      // Either we failed to fast find or we found a file or we found
      // a directory in RECURSE_DISABLE mode, so we do not need to scan it.
      // We can return here and do not need to process further.
      // We need to process further only if we fast found a directory.
      if (!FindCode || !IsDir || Recurse==RECURSE_DISABLE)
      {
         // Return SCAN_SUCCESS if we found a file.
        SCAN_CODE RetCode=SCAN_SUCCESS;

        if (!FindCode)
        {
          // Return SCAN_ERROR if problem is more serious than just
          // "file not found".
          RetCode=FD->Error ? SCAN_ERROR:SCAN_NEXT;

          // If we failed to find an object, but our current mask is excluded,
          // we skip this object and avoid indicating an error.
          if (Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true))
            RetCode=SCAN_NEXT;
          else
          {
            ErrHandler.OpenErrorMsg(ErrArcName,CurMask);
            // User asked to return RARX_NOFILES and not RARX_OPEN here.
            ErrHandler.SetErrorCode(RARX_NOFILES);
          }
        }

        // If we searched only for one file or directory in "fast find" 
        // (without a wildcard) mode, let's set masks to zero, 
        // so calling function will know that current mask is used 
        // and next one must be read from mask list for next call.
        // It is not necessary for directories, because even in "fast find"
        // mode, directory recursing will quit by (Depth < 0) condition,
        // which returns SCAN_DONE to calling function.
        *CurMask=0;

        return RetCode;
      }

      // We found a directory using only FindFile::FastFind function.
      FastFindFile=true;
    }
  }

  if (!FastFindFile && !FindStack[Depth]->Next(FD,GetLinks))
  {
    // We cannot find anything more in directory either because of
    // some error or just as result of all directory entries already read.

    bool Error=FD->Error;
    if (Error)
      ScanError(Error);

    wchar DirName[NM];
    *DirName=0;

    // Going to at least one directory level higher.
    delete FindStack[Depth];
    FindStack[Depth--]=NULL;
    while (Depth>=0 && FindStack[Depth]==NULL)
      Depth--;
    if (Depth < 0)
    {
      // Directories scanned both in normal and FastFindFile mode,
      // finally exit from scan here, by (Depth < 0) condition.

      if (Error)
        Errors++;
      return SCAN_DONE;
    }

    wchar *Slash=wcsrchr(CurMask,CPATHDIVIDER);
    if (Slash!=NULL)
    {
      wchar Mask[NM];
      wcsncpyz(Mask,Slash,ASIZE(Mask));
      if (Depth<SetAllMaskDepth)
        wcsncpyz(Mask+1,PointToName(OrigCurMask),ASIZE(Mask)-1);
      *Slash=0;
      wcsncpyz(DirName,CurMask,ASIZE(DirName));
      wchar *PrevSlash=wcsrchr(CurMask,CPATHDIVIDER);
      if (PrevSlash==NULL)
        wcsncpyz(CurMask,Mask+1,ASIZE(CurMask));
      else
      {
        *(PrevSlash+1)=0;
        wcsncatz(CurMask,Mask,ASIZE(CurMask));
      }
    }
    if (GetDirs==SCAN_GETDIRSTWICE &&
        FindFile::FastFind(DirName,FD,GetLinks) && FD->IsDir)
    {
      FD->Flags|=FDDF_SECONDDIR;
      return Error ? SCAN_ERROR:SCAN_SUCCESS;
    }
    return Error ? SCAN_ERROR:SCAN_NEXT;
  }

  // Link check is required for NTFS links, not for Unix.
  if (FD->IsDir && (!GetLinks || !FD->IsLink))
  {
    // If we found the directory in top (Depth==0) directory
    // and if we are not in "fast find" (directory name only as argument)
    // or in recurse (SearchAll was set when opening the top directory) mode,
    // we do not recurse into this directory. We either return it by itself
    // or skip it.
    if (!FastFindFile && Depth==0 && !SearchAllInRoot)
      return GetDirs==SCAN_GETCURDIRS ? SCAN_SUCCESS:SCAN_NEXT;

    // Let's check if directory name is excluded, so we do not waste
    // time searching in directory, which will be excluded anyway.
    if (Cmd!=NULL && (Cmd->ExclCheck(FD->Name,true,false,false) ||
        Cmd->ExclDirByAttr(FD->FileAttr)))
    {
      // If we are here in "fast find" mode, it means that entire directory
      // specified in command line is excluded. Then we need to return
      // SCAN_DONE to go to next mask and avoid the infinite loop
      // in GetNext() function. Such loop would be possible in case of
      // SCAN_NEXT code and "rar a arc dir -xdir" command.

      return FastFindFile ? SCAN_DONE:SCAN_NEXT;
    }
    
    wchar Mask[NM];

    wcsncpyz(Mask,FastFindFile ? MASKALL:PointToName(CurMask),ASIZE(Mask));
    wcsncpyz(CurMask,FD->Name,ASIZE(CurMask));

    if (wcslen(CurMask)+wcslen(Mask)+1>=NM || Depth>=MAXSCANDEPTH-1)
    {
      uiMsg(UIERROR_PATHTOOLONG,CurMask,SPATHDIVIDER,Mask);
      return SCAN_ERROR;
    }

    AddEndSlash(CurMask,ASIZE(CurMask));
    wcsncatz(CurMask,Mask,ASIZE(CurMask));

    Depth++;

    // We need to use OrigCurMask for depths less than SetAllMaskDepth
    // and "*" for depths equal or larger than SetAllMaskDepth.
    // It is important when "fast finding" directories at Depth > 0.
    // For example, if current directory is RootFolder and we compress
    // the following directories structure:
    //   RootFolder
    //     +--Folder1
    //     |  +--Folder2
    //     |  +--Folder3
    //     +--Folder4
    // with 'rar a -r arcname Folder2' command, rar could add not only
    // Folder1\Folder2 contents, but also Folder1\Folder3 if we were using
    // "*" mask at all levels. We need to use "*" mask inside of Folder2,
    // but return to "Folder2" mask when completing scanning Folder2.
    // We can rewrite SearchAll expression above to avoid fast finding
    // directories at Depth > 0, but then 'rar a -r arcname Folder2'
    // will add the empty Folder2 and do not add its contents.

    if (FastFindFile)
      SetAllMaskDepth=Depth;
  }
  if (!FastFindFile && !CmpName(CurMask,FD->Name,MATCH_NAMES))
    return SCAN_NEXT;

  return SCAN_SUCCESS;
}
Пример #10
0
void CParser::NextSym()
{
	// sets the global variable sym to the next symbol, and
	// if it is an operator
	//   sets the global variable opkind to the kind of operator
	// if it is a constant
	//   sets the global variable constkind to the kind of operator
	// if it is a reference to a cell
	//   sets the global variable cellcoord to the kind of operator

	errmsg = NULL;
	while (ch == ' ' || ch == 0x9)
		NextCh();

	switch (ch) {
		case '(':
			sym = lbracksym; NextCh();
			break;
		case ')':
			sym = rbracksym; NextCh();
			break;
		case ',':
			sym = commasym; NextCh();
			break;
		case '%' :
			sym = opsym; opkind = OPmodulus; NextCh();
			break;
		case '+' :
			sym = opsym; opkind = OPplus; NextCh();
			break;
		case '-' :
			sym = opsym; opkind = OPminus; NextCh();
			break;
		case '*' :
			sym = opsym; opkind = OPtimes; NextCh();
			break;
		case '/' :
			sym = opsym; opkind = OPdivide; NextCh();
			break;
		case '&' :
			sym = opsym; opkind = OPand; NextCh(); TermChar('&');
			break;
		case '|' :
			sym = opsym; opkind = OPor; NextCh(); TermChar('|');
			break;
		case '=' :
			sym = opsym; opkind = OPequal; NextCh(); TermChar('=');
			break;
		case '!' :
			sym = opsym;
			NextCh();
			if (ch == '=')
			{
				opkind = OPunequal;
				NextCh();
			}
			else
			{
				opkind = OPnot;
			}
			break;
		case '>':
			sym = opsym;
			NextCh();
			if (ch == '=')
			{
				opkind = OPgreaterequal;
				NextCh();
			}
			else
			{
				opkind = OPgreater;
			}
			break;
		case '<':
			sym = opsym;
			NextCh();
			if (ch == '=') {
				opkind = OPlessequal;
				NextCh();
			} else {
				opkind = OPless;
			}
			break;
		case '\"' :
		{
			int start;
			sym = constsym;
			constkind = stringtype;
			NextCh();
			start = chcount;
			while ((ch != '\"') && (ch != 0x0))
				NextCh();
			GrabRealString(start);
			TermChar('\"');	// check for eol before '\"'
			break;
		}
		case 0x0: sym = eolsym; break;
		default:
		{
			int start;
			start = chcount;
			DigRep();
			if ((start != chcount) || (ch == '.')) { // number
				sym = constsym;
				if (ch == '.') {
					constkind = floattype;
					NextCh();
					DigRep();
				}
				else constkind = inttype;
				if ((ch == 'e') || (ch == 'E')) {
					int mark;
					constkind = floattype;
					NextCh();
					if ((ch == '+') || (ch == '-')) NextCh();
					mark = chcount;
					DigRep();
					if (mark == chcount) {
						ScanError("Number expected after 'E'");
						return;
					}
				}
				GrabString(start);
			} else if (((ch >= 'a') && (ch <= 'z'))
			           || ((ch >= 'A') && (ch <= 'Z')))
			{ // reserved word?

				start = chcount;
				CharRep();
				GrabString(start);
				if (!strcasecmp(const_as_string, "SUM")) {
					sym = sumsym;
				}
				else if (!strcasecmp(const_as_string, "NOT")) {
					sym = opsym;
					opkind = OPnot;
				}
				else if (!strcasecmp(const_as_string, "AND")) {
					sym = opsym; opkind = OPand;
				}
				else if (!strcasecmp(const_as_string, "OR")) {
					sym = opsym; opkind = OPor;
				}
				else if (!strcasecmp(const_as_string, "IF"))
					sym = ifsym;
				else if (!strcasecmp(const_as_string, "WHOMADE"))
					sym = whocodedsym;
				else if (!strcasecmp(const_as_string, "FALSE")) {
					sym = constsym; constkind = booltype; boolvalue = false;
				} else if (!strcasecmp(const_as_string, "TRUE")) {
					sym = constsym; constkind = booltype; boolvalue = true;
				} else {
					sym = idsym;
					//STR_String str;
					//str.Format("'%s' makes no sense here", (const char*)funstr);
					//ScanError(str);
				}
			} else { // unknown symbol
				STR_String str;
				str.Format("Unexpected character '%c'", ch);
				NextCh();
				ScanError(str);
				return;
			}
		}
	}
}
Пример #11
0
//---------------------------------------------------------------------------
// FetchLine
//
// This function "scans" the next parse-able line into the given buffer,
// along with the file index and line number information that the parser will
// use for error reporting and OP_LINE information.
//
// RETURNS:     1 if line copied, 0 if end of script), or -1 if error.
//---------------------------------------------------------------------------
INT FetchLine (LPSTR szLineBuf, UINT FAR *nFile, UINT FAR *nLine)
{
    // This while loop executes until an actual line is copied to the buffer,
    // or until we hit the end of the script, or until an error occurs.
    //-----------------------------------------------------------------------
    while (GetRawLine(szLineBuf) && (!fScanErr))
        {
        szLine = szLineBuf;
        idx = 0;
        NextToken();
        if ((curtok[0] == '\'') || (!_stricmp (curtok, szREM)))
            {
            INT     type;

            // This line is a comment or metacommand.  Parse it and find out.
            //---------------------------------------------------------------
            type = ParseCommentLine ();
            switch (type)
                {
                case MC_DEFINE:
                    // Add the symbol to the symbol table if CUR_STATE
                    //-------------------------------------------------------
                    if (CUR_STATE)
                        {
                        NextToken();
                        if (strchr (szSpecials, *curtok))
                            ScanError (SCN_SYMEXP);
                        else if (!AddSymbol (pSymRoot, curtok))
                            ScanError (SCN_OSS);

                        // Now make sure the rest of the line is either empty
                        // or a comment character
                        //---------------------------------------------------
                        CompleteLine();
                        }
                    break;

                case MC_UNDEF:
                    // Remove the symbol from the symbol table if CUR_STATE
                    //-------------------------------------------------------
                    if (CUR_STATE)
                        {
                        NextToken();
                        if (strchr (szSpecials, *curtok))
                            ScanError (SCN_SYMEXP);
                        else
                            RemoveSymbol (pSymRoot, curtok);

                        // Now make sure the rest of the line is either empty
                        // or a comment character
                        //---------------------------------------------------
                        CompleteLine();
                        }
                    break;

                case MC_IFDEF:
                case MC_IFNDEF:
                    {
                    BOOL    truth;

                    // Evaluate the symbol expression
                    //-------------------------------------------------------
                    truth = SymbolExpression();
                    if (type == MC_IFNDEF)
                        truth = !truth;

                    // Act accordingly
                    //-------------------------------------------------------
                    if (truth)
                        truth = PushState (CUR_STATE, 0, CUR_STATE);
                    else
                        truth = PushState (0, 0, 0);
                    if (!truth)
                        ScanError (SCN_TOODEEP);
                    break;
                    }

                case MC_ELSEIFDEF:
                case MC_ELSEIFNDEF:
                    // Check to see if we've seen an ELSE or are in an IF
                    // block at all.  Given an error if appropriate
                    //-------------------------------------------------------
                    if ((CUR_ELSESEEN) || (CUR_FILE.pIfStack == pIfStack))
                        ScanError (SCN_UNXPELSE);
                    else
                        {
                        BOOL    truth;

                        // Evaluate the symbol expression
                        //---------------------------------------------------
                        truth = SymbolExpression();
                        if (type == MC_ELSEIFNDEF)
                            truth = !truth;

                        // Act as appropriate
                        //---------------------------------------------------
                        if (truth)
                            {
                            if (CUR_BLKCOPIED)
                                CUR_STATE = 0;
                            else
                                CUR_STATE = CUR_BLKCOPIED = LAST_STATE;
                            }
                        else
                            CUR_STATE = 0;
                        }
                    break;

                case MC_ELSE:
                    if ((CUR_ELSESEEN) || (CUR_FILE.pIfStack == pIfStack))
                        ScanError (SCN_UNXPELSE);
                    else
                        {
                        CUR_STATE = (CUR_STATE ^ LAST_STATE)
                                      & (!CUR_BLKCOPIED);
                        CUR_ELSESEEN = 1;

                        // Now make sure the rest of the line is either empty
                        // or a comment character
                        //---------------------------------------------------
                        //CompleteLine();
                        }
                    break;

                case MC_ENDIF:
                    if (CUR_FILE.pIfStack == pIfStack)
                        ScanError (SCN_UNXPENDIF);
                    else
                        {
                        PopState ();

                        // Now make sure the rest of the line is either empty
                        // or a comment character
                        //---------------------------------------------------
                        //CompleteLine();
                        }
                    break;

                case MC_INCLUDE:
                    if (CUR_STATE)
                        LoadIncludeFile();
                    break;
                }
            }
        else if (CUR_STATE && curtok[0])
            {
            // This was not a comment, and we're not yanking this block, so
            // fill in the other data items and return this line.
            //---------------------------------------------------------------
            *nFile = nFileIdx;
            *nLine = CUR_FILE.nLineNo;
            return (1);
            }
        }

    // The only way out of the loop above is if we're at the end of the
    // script, or an error occurred.  Either way, this is it for the scan/
    // parse process, so clean up before we return to the parser.  First,
    // if there's more than one node on the IFREC stack, we didn't get an
    // ENDIF somewhere...
    //-----------------------------------------------------------------------
    if (pIfStack->next)
        ScanError (SCN_ENDIFEXP);

    // Get rid of the DEFINE table and last of the IFDEF stack
    //-----------------------------------------------------------------------
    FreeSymbolTree (pSymRoot);
    pSymRoot = NULL;
    FreeIfStack ();

    // Make sure the callback loader gets a chance to "unload" everything.
    // The lpFI table gets freed later -- after the basic engine returns.
    //-----------------------------------------------------------------------
    UnloadAllFiles ();

    // Zero-terminate the line buffer, and return 0 if no error.
    //-----------------------------------------------------------------------
    szLineBuf[0] = 0;
    return (fScanErr ? -1 : 0);
}