source-engine/utils/tfstats/textfile.cpp
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

405 lines
11 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implementation of CTextFile. See TextFile.h for details
//
// $Workfile: $
// $Date: $
//
//------------------------------------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "TFStatsApplication.h"
#include "util.h"
#include "TextFile.h"
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::init
// Purpose: initializes a CTextFile object
// Input: filename - name of the file that this object will represent
// eliminateComments - if true, C++ style comments will not be handed
// back as tokens to the user of this object
// Output:
//------------------------------------------------------------------------------------------------------
void CTextFile::init(const char* filename,bool eliminateComments)
{
this->filename=filename;
//only support one push back
fWordPushed=false;
noComments=eliminateComments;
memset(wordBuf,0,BUF_SIZE);
theFile=fopen(filename,"rt");
//three different delim sets
//use this when reading strings
stringDelims="\"";
//use this when reading normal file stuff
normalDelims=" \t{}=\n\r;\"";
//use this when you want to discard a block
blockDelims="}";
//set to default
delims=normalDelims;
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::isDelim
// Purpose: returns true if the given char is in the current delimiter set
// Input: c - the character to test
// Output: Returns true on success, false on failure.
//------------------------------------------------------------------------------------------------------
bool CTextFile::isDelim(char c)
{
for (int i=0;delims[i] != 0; i++)
{
if (c==delims[i])
return true;
}
return false;
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::discardBlock
// Purpose: advances the file ptr to the character after the next }
//------------------------------------------------------------------------------------------------------
void CTextFile::discardBlock()
{
delims=blockDelims;
getToken();
delims=normalDelims;
discard("}");
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::getToken
// Purpose: gets and returns the next token in the file
// Output: const char*
//------------------------------------------------------------------------------------------------------
const char* CTextFile::getToken()
{
return getToken(wordBuf);
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::getToken
// Purpose: gets and returns the next token in the file, with an arbitrary buffer
// Input: outputBuf - the buffer to put the token in
// Output: const char*
//------------------------------------------------------------------------------------------------------
const char* CTextFile::getToken(char* outputBuf)
{
if (fWordPushed)
{
fWordPushed=false;
strcpy(outputBuf,wordBuf);
return wordBuf;
}
readToken:
char c=getNextNonWSChar();
if (!theFile || feof(theFile)) return NULL;
if (isDelim(c) && !(isspace(c)))
{
outputBuf[0]=c;
outputBuf[1]=0;
return outputBuf;
}
int write=0;
while (!isDelim(c))
{
if (c=='\\')
{
c=fgetc(theFile);
if (!theFile&& feof(theFile))
break;
}
//these are both in the normal delimiter set, so this case won't happen unless we're reading a string
//which is exactly the behaviour we want
if (c=='\n' || c=='\r')
{
outputBuf[write]=0;
g_pApp->fatalError("new line in string constant (or unterminated string constant):\n\"%s\"",outputBuf);
}
outputBuf[write++]=c;
c=fgetc(theFile);
if (feof(theFile))
break;
}
if (theFile && !feof(theFile))
fseek(theFile,-1,SEEK_CUR); //seek to before the delimiter
outputBuf[write]=0;
if (outputBuf[0] == '/' && outputBuf[1] == '/')
{
while (fgetc(theFile)!='\n');
goto readToken;
}
return outputBuf;
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::getNextNonWSChar
// Purpose: gets and returns the next non whitespace character
// Output: char
//------------------------------------------------------------------------------------------------------
char CTextFile::getNextNonWSChar()
{
char c;
top:
c=' ';
while (theFile && !feof(theFile) && isspace(c))
c=fgetc(theFile);
if (!theFile) return 0;
if (feof(theFile)) return 0;
if (!noComments)
return c;
//check for comments
if (c=='/')
{
char c2=fgetc(theFile);
if (c2=='/') //found comment?
{
//discard and start over
getLine();
goto top;
}
else
{
fseek(theFile,-1,SEEK_CUR);
return c;
}
}
else
{
return c;
}
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::readString
// Purpose: reads and returns the contents of the file up to the next "
// Output: const char*
//------------------------------------------------------------------------------------------------------
const char* CTextFile::readString()
{
if (fWordPushed)
{
fWordPushed=false;
return wordBuf;
}
char c=getNextNonWSChar();
if (c=='\"')
{
delims=stringDelims;
getToken(); //handles escaping stuff
delims=normalDelims;
if (wordBuf[0]=='\"' && wordBuf[1]==0)
wordBuf[0]=0;
else
discard("\"");
}
else
{
fseek(theFile,-1,SEEK_CUR);
getToken();
}
return wordBuf;
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::readString
// Purpose: reads and returns the contents of the file up to the next "
// Output: const char*
//------------------------------------------------------------------------------------------------------
const char* CTextFile::readString(char* buf)
{
if (fWordPushed)
{
fWordPushed=false;
strcpy(buf,wordBuf);
return buf;
}
char c=getNextNonWSChar();
if (eof()) return NULL;
if (c=='\"')
{
delims=stringDelims;
getToken(buf); //handles escaping stuff
delims=normalDelims;
if (buf[0]=='\"' && buf[1]==0)
buf[0]=0;
else
discard("\"");
}
else
{
fseek(theFile,-1,SEEK_CUR);
getToken(buf);
}
return buf;
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::getLine
// Purpose: reads and returns the contents of the file up to the next \r or \n
// Output: const char*
//------------------------------------------------------------------------------------------------------
const char* CTextFile::getLine()
{
fgets(wordBuf,BUF_SIZE,theFile);
return wordBuf;
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::discard
// Purpose: discards the next token, also checks to see if it is the same as the token
// the user of the object was expecting to discard.
// Input: test - the token that is expected to be discarded.
// Output: Returns true on success, false on failure.
//------------------------------------------------------------------------------------------------------
bool CTextFile::discard(char* test)
{
if (!theFile || feof(theFile))
return true;
char wordBuf2[BUF_SIZE];
getToken(wordBuf2);
int result = stricmp(wordBuf2,test);
if (result !=0)
g_pApp->fatalError("While parsing %s, expecting \"%s\", got \"%s\"",filename.c_str(),test,wordBuf2);
return true;
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::peekNext
// Purpose: peeks at the next token. gets then immediately pushes it back
// destroys any pushed back words from before.
// Output: char*
//------------------------------------------------------------------------------------------------------
char* CTextFile::peekNext()
{
fpos_t startpos;
fgetpos(theFile,&startpos);
const char* c=getToken(peekBuf);
if (!c)
return NULL;
//pushBack(c);
fseek(theFile,startpos,SEEK_SET);
return peekBuf;
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::peekNextString
// Purpose: peeks at the next string. gets then immediately pushes it back
// destroys any pushed back words from before.
// Output: char*
//------------------------------------------------------------------------------------------------------
char* CTextFile::peekNextString()
{
if (!theFile)
return NULL;
fpos_t startpos;
fgetpos(theFile,&startpos);
const char* c=readString(peekBuf);
if (!c)
return NULL;
//pushBack(c);
fseek(theFile,startpos,SEEK_SET);
return peekBuf;
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::pushBack
// Purpose: "pushes" a token back into the stream. Only can have one pushed back
// token at a time. getting a token or peeking will destroy the pushed back token
// Input: pushTok - the token to push back.
//------------------------------------------------------------------------------------------------------
void CTextFile::pushBack(const char* pushTok)
{
if (pushTok != wordBuf)
strncpy(wordBuf,pushTok,BUF_SIZE);
fWordPushed=true;
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::reset
// Purpose: resets the file ptr to the beginning of the file
//------------------------------------------------------------------------------------------------------
void CTextFile::reset()
{
fseek(theFile,0,SEEK_SET);
}
//------------------------------------------------------------------------------------------------------
// Function: CTextFile::~CTextFile
// Purpose: destructor
//------------------------------------------------------------------------------------------------------
CTextFile::~CTextFile()
{
if (theFile)
fclose(theFile);
#ifndef WIN32
chmod(filename.c_str(),PERMIT);
#endif
}
bool CTextFile::eof()
{
bool retval=false;
//test current pos first
retval = (!theFile || feof(theFile));
if (retval)
return true;
//now see if only whitespace is left
fpos_t beforecheck;
fgetpos(theFile,&beforecheck);
char c=getNextNonWSChar();
retval = (!theFile || feof(theFile));
fseek(theFile,beforecheck,SEEK_SET);
return retval;
}
int CTextFile::readInt()
{
readString();
return atoi(wordBuf);
}
unsigned long CTextFile::readULong()
{
readString();
return strtoul(wordBuf,NULL,10);
}