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