fixed a number of memory complaints from valgrind. most of these were minor, but there was a rather disturbing memory over-read error in the SMC text parser. correcting this brought about a rewrite of its API. this change is BACKWARDS INCOMPATIBLE for C++ extensions, but it was sorely needed, and the API is now both future-extensible and much easier to work with. plugins didn't need any changes, but they will probably get the better API changes in a future release. as a special bonus, the RawLine() hook is now much less expensive since the entire stream buffer won't be shoved through it like before!

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%401662
This commit is contained in:
David Anderson 2007-10-31 05:04:07 +00:00
parent e425558f3f
commit 172f28d676
19 changed files with 516 additions and 388 deletions

View File

@ -82,22 +82,21 @@ public:
private:
bool Parse()
{
unsigned int line = 0;
SMCParseError error;
SMCStates states;
SMCError error;
m_Line = 0;
m_bFileNameLogged = false;
g_SourceMod.BuildPath(Path_SM, m_File, sizeof(m_File), "configs/admin_levels.cfg");
if ((error = textparsers->ParseFile_SMC(m_File, this, &line, NULL))
!= SMCParse_Okay)
if ((error = textparsers->ParseFile_SMC(m_File, this, &states))
!= SMCError_Okay)
{
const char *err_string = textparsers->GetSMCErrorString(error);
if (!err_string)
{
err_string = "Unknown error";
}
ParseError("Error %d (%s)", error, err_string);
ParseError(NULL, "Error %d (%s)", error, err_string);
return false;
}
@ -109,12 +108,12 @@ private:
m_IgnoreLevel = 0;
memset(g_FlagSet, 0, sizeof(g_FlagSet));
}
SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes)
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
{
if (m_IgnoreLevel)
{
m_IgnoreLevel++;
return SMCParse_Continue;
return SMCResult_Continue;
}
if (m_LevelState == LEVEL_STATE_NONE)
@ -122,35 +121,41 @@ private:
if (strcmp(name, "Levels") == 0)
{
m_LevelState = LEVEL_STATE_LEVELS;
} else {
}
else
{
m_IgnoreLevel++;
}
} else if (m_LevelState == LEVEL_STATE_LEVELS) {
if (strcmp(name, "Flags") == 0)
{
m_LevelState = LEVEL_STATE_FLAGS;
} else {
}
else
{
m_IgnoreLevel++;
}
} else {
}
else
{
m_IgnoreLevel++;
}
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
if (m_LevelState != LEVEL_STATE_FLAGS || m_IgnoreLevel)
{
return SMCParse_Continue;
return SMCResult_Continue;
}
unsigned char c = (unsigned)value[0];
if (c < (unsigned)'a' || c > (unsigned)'z')
{
ParseError("Flag \"%c\" is not a lower-case ASCII letter", c);
return SMCParse_Continue;
ParseError(states, "Flag \"%c\" is not a lower-case ASCII letter", c);
return SMCResult_Continue;
}
c -= (unsigned)'a';
@ -159,38 +164,35 @@ private:
if (!g_Admins.FindFlag(key, &g_FlagLetters[c]))
{
ParseError("Unrecognized admin level \"%s\"", key);
return SMCParse_Continue;
ParseError(states, "Unrecognized admin level \"%s\"", key);
return SMCResult_Continue;
}
g_FlagSet[c] = true;
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult ReadSMC_LeavingSection()
SMCResult ReadSMC_LeavingSection(const SMCStates *states)
{
if (m_IgnoreLevel)
{
m_IgnoreLevel--;
return SMCParse_Continue;
return SMCResult_Continue;
}
if (m_LevelState == LEVEL_STATE_FLAGS)
{
m_LevelState = LEVEL_STATE_LEVELS;
return SMCParse_Halt;
} else if (m_LevelState == LEVEL_STATE_LEVELS) {
return SMCResult_Halt;
}
else if (m_LevelState == LEVEL_STATE_LEVELS)
{
m_LevelState = LEVEL_STATE_NONE;
}
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult ReadSMC_RawLine(const char *line, unsigned int curline)
{
m_Line = curline;
return SMCParse_Continue;
}
void ParseError(const char *message, ...)
void ParseError(const SMCStates *states, const char *message, ...)
{
va_list ap;
char buffer[256];
@ -205,14 +207,13 @@ private:
m_bFileNameLogged = true;
}
g_Logger.LogError("[SM] (Line %d): %s", m_Line, buffer);
g_Logger.LogError("[SM] (Line %d): %s", states ? states->line : 0, buffer);
}
private:
bool m_bFileNameLogged;
char m_File[PLATFORM_MAX_PATH];
int m_LevelState;
int m_IgnoreLevel;
unsigned int m_Line;
} s_FlagReader;
AdminCache::AdminCache()

View File

@ -100,7 +100,7 @@ void CoreConfig::OnRootConsoleCommand(const char *cmdname, const CCommand &comma
void CoreConfig::Initialize()
{
SMCParseError err;
SMCError err;
char filePath[PLATFORM_MAX_PATH];
/* Try to get command line value of core config convar */
@ -116,7 +116,7 @@ void CoreConfig::Initialize()
g_LibSys.PathFormat(filePath, sizeof(filePath), "%s/%s", g_SourceMod.GetGamePath(), corecfg);
/* Parse config file */
if ((err=textparsers->ParseFile_SMC(filePath, this, NULL, NULL)) != SMCParse_Okay)
if ((err=textparsers->ParseFile_SMC(filePath, this, NULL)) != SMCError_Okay)
{
/* :TODO: This won't actually log or print anything :( - So fix that somehow */
const char *error = textparsers->GetSMCErrorString(err);
@ -124,7 +124,7 @@ void CoreConfig::Initialize()
}
}
SMCParseResult CoreConfig::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
SMCResult CoreConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
char error[255];
ConfigResult err = SetConfigOption(key, value, ConfigSource_File, error, sizeof(error));
@ -135,7 +135,7 @@ SMCParseResult CoreConfig::ReadSMC_KeyValue(const char *key, const char *value,
g_Logger.LogFatal("Config error (key: %s) (value: %s) %s", key, value, error);
}
return SMCParse_Continue;
return SMCResult_Continue;
}
ConfigResult CoreConfig::SetConfigOption(const char *option, const char *value, ConfigSource source, char *error, size_t maxlength)

View File

@ -48,7 +48,7 @@ public: // SMGlobalClass
void OnSourceModShutdown();
void OnSourceModLevelChange(const char *mapName);
public: // ITextListener_SMC
SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes);
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
public: // IRootConsoleCommand
void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
public:

View File

@ -75,21 +75,21 @@ void DBManager::OnSourceModAllInitialized()
void DBManager::OnSourceModLevelChange(const char *mapName)
{
SMCParseError err;
unsigned int line = 0;
SMCError err;
SMCStates states = {0, 0};
/* We lock and don't give up the lock until we're done.
* This way the thread's search won't be searching through a
* potentially empty/corrupt list, which would be very bad.
*/
m_pConfigLock->Lock();
if ((err = textparsers->ParseFile_SMC(m_Filename, this, &line, NULL)) != SMCParse_Okay)
if ((err = textparsers->ParseFile_SMC(m_Filename, this, &states)) != SMCError_Okay)
{
g_Logger.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
if (err != SMCParse_Custom)
if (err != SMCError_Custom)
{
const char *txt = textparsers->GetSMCErrorString(err);
g_Logger.LogError("[SM] Line %d: %s", line, txt);
g_Logger.LogError("[SM] Line %d: %s", states.line, txt);
}
}
m_pConfigLock->Unlock();
@ -141,12 +141,12 @@ void DBManager::ReadSMC_ParseStart()
}
ConfDbInfo s_CurInfo;
SMCParseResult DBManager::ReadSMC_NewSection(const char *name, bool opt_quotes)
SMCResult DBManager::ReadSMC_NewSection(const SMCStates *states, const char *name)
{
if (m_ParseLevel)
{
m_ParseLevel++;
return SMCParse_Continue;
return SMCResult_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_NONE)
@ -165,14 +165,14 @@ SMCParseResult DBManager::ReadSMC_NewSection(const char *name, bool opt_quotes)
m_ParseLevel++;
}
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult DBManager::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
SMCResult DBManager::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
if (m_ParseLevel)
{
return SMCParse_Continue;
return SMCResult_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_MAIN)
@ -203,7 +203,7 @@ SMCParseResult DBManager::ReadSMC_KeyValue(const char *key, const char *value, b
}
}
return SMCParse_Continue;
return SMCResult_Continue;
}
#define ASSIGN_VAR(var) \
@ -213,12 +213,12 @@ SMCParseResult DBManager::ReadSMC_KeyValue(const char *key, const char *value, b
s_CurInfo.info.var = m_StrTab.GetString(s_CurInfo.var); \
}
SMCParseResult DBManager::ReadSMC_LeavingSection()
SMCResult DBManager::ReadSMC_LeavingSection(const SMCStates *states)
{
if (m_ParseLevel)
{
m_ParseLevel--;
return SMCParse_Continue;
return SMCResult_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_DATABASE)
@ -239,10 +239,10 @@ SMCParseResult DBManager::ReadSMC_LeavingSection()
m_ParseState = DBPARSE_LEVEL_MAIN;
} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
m_ParseState = DBPARSE_LEVEL_NONE;
return SMCParse_Halt;
return SMCResult_Halt;
}
return SMCParse_Continue;
return SMCResult_Continue;
}
#undef ASSIGN_VAR

View File

@ -94,9 +94,9 @@ public: //IDBManager
HandleError ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token);
public: //ITextListener_SMC
void ReadSMC_ParseStart();
SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes);
SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes);
SMCParseResult ReadSMC_LeavingSection();
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
void ReadSMC_ParseEnd(bool halted, bool failed);
public: //IThread
void RunThread(IThreadHandle *pThread);

View File

@ -106,12 +106,12 @@ CGameConfig::~CGameConfig()
delete m_pStrings;
}
SMCParseResult CGameConfig::ReadSMC_NewSection(const char *name, bool opt_quotes)
SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
{
if (m_IgnoreLevel)
{
m_IgnoreLevel++;
return SMCParse_Continue;
return SMCResult_Continue;
}
switch (m_ParseState)
@ -245,14 +245,14 @@ SMCParseResult CGameConfig::ReadSMC_NewSection(const char *name, bool opt_quotes
}
}
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult CGameConfig::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
if (m_IgnoreLevel)
{
return SMCParse_Continue;
return SMCResult_Continue;
}
if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET)
@ -296,15 +296,15 @@ SMCParseResult CGameConfig::ReadSMC_KeyValue(const char *key, const char *value,
}
}
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult CGameConfig::ReadSMC_LeavingSection()
SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
{
if (m_IgnoreLevel)
{
m_IgnoreLevel--;
return SMCParse_Continue;
return SMCResult_Continue;
}
switch (m_ParseState)
@ -478,12 +478,12 @@ skip_find:
}
}
return SMCParse_Continue;
return SMCResult_Continue;
}
bool CGameConfig::Reparse(char *error, size_t maxlength)
{
SMCParseError err;
SMCError err;
char path[PLATFORM_MAX_PATH];
g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s.txt", m_pFile);
@ -503,10 +503,10 @@ bool CGameConfig::Reparse(char *error, size_t maxlength)
sm_trie_clear(m_pProps);
sm_trie_clear(m_pKeys);
if ((err=textparsers->ParseFile_SMC(path, this, NULL, NULL))
!= SMCParse_Okay)
if ((err=textparsers->ParseFile_SMC(path, this, NULL))
!= SMCError_Okay)
{
if (error && (err != SMCParse_Custom))
if (error && (err != SMCError_Custom))
{
const char *str = textparsers->GetSMCErrorString(err);
snprintf(error, maxlength, "%s", str);

View File

@ -55,9 +55,9 @@ public:
public:
bool Reparse(char *error, size_t maxlength);
public: //ITextListener_SMC
SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes);
SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes);
SMCParseResult ReadSMC_LeavingSection();
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
public: //IGameConfig
const char *GetKeyValue(const char *key);
bool GetOffset(const char *key, int *value);

View File

@ -107,14 +107,13 @@ bool CharStreamReader(void *stream, char *buffer, size_t maxlength, unsigned int
return true;
}
SMCParseError TextParsers::ParseString_SMC(const char *stream,
SMCError TextParsers::ParseString_SMC(const char *stream,
ITextListener_SMC *smc,
unsigned int *line,
unsigned int *col)
SMCStates *states)
{
CharStream srdr = { stream };
return ParseStream_SMC(&srdr, CharStreamReader, smc, line, col);
return ParseStream_SMC(&srdr, CharStreamReader, smc, states);
}
/**
@ -135,16 +134,21 @@ bool FileStreamReader(void *stream, char *buffer, size_t maxlength, unsigned int
return (ferror((FILE *)stream) == 0);
}
SMCParseError TextParsers::ParseFile_SMC(const char *file, ITextListener_SMC *smc, unsigned int *line, unsigned int *col)
SMCError TextParsers::ParseFile_SMC(const char *file, ITextListener_SMC *smc, SMCStates *states)
{
FILE *fp = fopen(file, "rt");
if (!fp)
{
return SMCParse_StreamOpen;
if (states != NULL)
{
states->line = 0;
states->col = 0;
}
return SMCError_StreamOpen;
}
SMCParseError result = ParseStream_SMC(fp, FileStreamReader, smc, line, col);
SMCError result = ParseStream_SMC(fp, FileStreamReader, smc, states);
fclose(fp);
@ -273,34 +277,44 @@ char *lowstring(StringInfo info[3])
return NULL;
}
SMCParseError TextParsers::ParseStream_SMC(void *stream,
SMCError TextParsers::ParseStream_SMC(void *stream,
STREAMREADER srdr,
ITextListener_SMC *smc,
unsigned int *line,
unsigned int *col)
SMCStates *pStates)
{
char *reparse_point = NULL;
char in_buf[4096];
char *parse_point = in_buf;
char *line_begin = in_buf;
unsigned int read;
unsigned int curline = 1;
unsigned int curtok = 0;
unsigned int curlevel = 0;
bool in_quote = false;
bool ignoring = false;
bool eol_comment = false;
bool ml_comment = false;
unsigned int i;
SMCParseError err = SMCParse_Okay;
SMCParseResult res;
SMCError err = SMCError_Okay;
SMCResult res;
SMCStates states;
char c;
StringInfo strings[3];
StringInfo emptystring;
states.line = 1;
states.col = 0;
smc->ReadSMC_ParseStart();
/**
* The stream reader reads in as much as it can fill the buffer with.
* It then processes the buffer. If the buffer cannot be fully processed, for example,
* a line is left hanging with no newline, then the contents of the buffer is shifted
* down, and the buffer is filled from the stream reader again.
*
* What makes this particularly annoying is that we cache pointers everywhere, so when
* the shifting process takes place, all those pointers must be shifted as well.
*/
while (srdr(stream, parse_point, sizeof(in_buf) - (parse_point - in_buf) - 1, &read))
{
if (!read)
@ -308,10 +322,10 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
break;
}
/* :TODO: do this outside of the main loop somehow
* This checks for BOM markings
/* Check for BOM markings, which is only relevant on the first line.
* Not worth it, but it could be moved out of the loop.
*/
if (curline == 1 &&
if (states.line == 1 &&
in_buf[0] == (char)0xEF &&
in_buf[1] == (char)0xBB &&
in_buf[2] == (char)0xBF)
@ -341,7 +355,7 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
strings[0].end = &parse_point[i];
if (rotate(strings) != NULL)
{
err = SMCParse_InvalidTokens;
err = SMCError_InvalidTokens;
goto failed;
}
}
@ -354,39 +368,44 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
ignoring = false;
}
/* Pass the raw line onto the listener */
if ((res=smc->ReadSMC_RawLine(line_begin, curline)) != SMCParse_Continue)
/* Pass the raw line onto the listener. We terminate the line so the receiver
* doesn't get tons of useless info. We restore the newline after.
*/
parse_point[i] = '\0';
if ((res=smc->ReadSMC_RawLine(&states, line_begin)) != SMCResult_Continue)
{
err = (res == SMCParse_HaltFail) ? SMCParse_Custom : SMCParse_Okay;
err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
goto failed;
}
parse_point[i] = '\n';
/* Now we check the sanity of our staged strings! */
if (strings[2].ptr)
{
if (!curlevel)
{
err = SMCParse_InvalidProperty1;
err = SMCError_InvalidProperty1;
goto failed;
}
/* Assume the next string is a property and pass the info on. */
if ((res=smc->ReadSMC_KeyValue(
&states,
FixupString(strings[2]),
FixupString(strings[1]),
strings[2].quoted,
strings[1].quoted)) != SMCParse_Continue)
FixupString(strings[1]))) != SMCResult_Continue)
{
err = (res == SMCParse_HaltFail) ? SMCParse_Custom : SMCParse_Okay;
err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
goto failed;
}
scrap(strings);
}
/* Change the states for the next line */
curtok = 0;
curline++;
states.col = 0;
states.line++;
line_begin = &parse_point[i+1]; //Note: safe because this gets relocated later
} else if (ignoring) {
}
else if (ignoring)
{
if (in_quote)
{
/* If i was 0, this case is impossible due to reparsing */
@ -403,10 +422,12 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
if (rotate(strings) != NULL)
{
/* If we rotated too many strings, there was too much crap on one line */
err = SMCParse_InvalidTokens;
err = SMCError_InvalidTokens;
goto failed;
}
} else if (c == '\\') {
}
else if (c == '\\')
{
strings[0].special = true;
if (i == (read - 1))
{
@ -414,7 +435,9 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
break;
}
}
} else if (ml_comment) {
}
else if (ml_comment)
{
if (c == '*')
{
/* Check if we need to get more input first */
@ -431,11 +454,13 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
assert(strings[0].ptr == NULL);
/* Advance the input stream so we don't choke on this token */
i++;
curtok++;
states.col++;
}
}
}
} else {
}
else
{
/* Check if we're whitespace or not */
if (!g_ws_chartable[(unsigned)c])
{
@ -468,7 +493,9 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
ignoring = true;
eol_comment = true;
restage = true;
} else if (parse_point[i+1] == '*') {
}
else if (parse_point[i+1] == '*')
{
/* inline comment - start ignoring */
ignoring = true;
ml_comment = true;
@ -479,93 +506,108 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
*/
restage = true;
}
} else {
}
else
{
ignoring = true;
eol_comment = true;
restage = true;
}
} else if (c == '{') {
}
else if (c == '{')
{
/* If we are staging a string, we must rotate here */
if (strings[0].ptr)
{
/* We have unacceptable tokens on this line */
if (rotate(strings) != NULL)
{
err = SMCParse_InvalidSection1;
err = SMCError_InvalidSection1;
goto failed;
}
}
/* Sections must always be alone */
if (strings[2].ptr != NULL)
{
err = SMCParse_InvalidSection1;
err = SMCError_InvalidSection1;
goto failed;
} else if (strings[1].ptr == NULL) {
err = SMCParse_InvalidSection2;
}
else if (strings[1].ptr == NULL)
{
err = SMCError_InvalidSection2;
goto failed;
}
if ((res=smc->ReadSMC_NewSection(FixupString(strings[1]), strings[1].quoted))
!= SMCParse_Continue)
if ((res=smc->ReadSMC_NewSection(&states, FixupString(strings[1])))
!= SMCResult_Continue)
{
err = (res == SMCParse_HaltFail) ? SMCParse_Custom : SMCParse_Okay;
err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
goto failed;
}
strings[1] = emptystring;
curlevel++;
} else if (c == '}') {
}
else if (c == '}')
{
/* Unlike our matching friend, this can be on the same line as something prior */
if (rotate(strings) != NULL)
{
err = SMCParse_InvalidSection3;
err = SMCError_InvalidSection3;
goto failed;
}
if (strings[2].ptr)
{
if (!curlevel)
{
err = SMCParse_InvalidProperty1;
err = SMCError_InvalidProperty1;
goto failed;
}
if ((res=smc->ReadSMC_KeyValue(
&states,
FixupString(strings[2]),
FixupString(strings[1]),
strings[2].quoted,
strings[1].quoted))
!= SMCParse_Continue)
FixupString(strings[1])))
!= SMCResult_Continue)
{
err = (res == SMCParse_HaltFail) ? SMCParse_Custom : SMCParse_Okay;
err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
goto failed;
}
} else if (strings[1].ptr) {
err = SMCParse_InvalidSection3;
}
else if (strings[1].ptr)
{
err = SMCError_InvalidSection3;
goto failed;
} else if (!curlevel) {
err = SMCParse_InvalidSection4;
}
else if (!curlevel)
{
err = SMCError_InvalidSection4;
goto failed;
}
/* Now it's safe to leave the section */
scrap(strings);
if ((res=smc->ReadSMC_LeavingSection()) != SMCParse_Continue)
if ((res=smc->ReadSMC_LeavingSection(&states)) != SMCResult_Continue)
{
err = (res == SMCParse_HaltFail) ? SMCParse_Custom : SMCParse_Okay;
err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
goto failed;
}
curlevel--;
} else if (c == '"') {
}
else if (c == '"')
{
/* If we get a quote mark, we always restage, but we need to do it beforehand */
if (strings[0].ptr)
{
strings[0].end = &parse_point[i];
if (rotate(strings) != NULL)
{
err = SMCParse_InvalidTokens;
err = SMCError_InvalidTokens;
goto failed;
}
}
strings[0].ptr = &parse_point[i];
in_quote = true;
ignoring = true;
} else if (!strings[0].ptr) {
}
else if (!strings[0].ptr)
{
/* If we have no string, we must start one */
strings[0].ptr = &parse_point[i];
}
@ -574,11 +616,13 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
strings[0].end = &parse_point[i];
if (rotate(strings) != NULL)
{
err = SMCParse_InvalidTokens;
err = SMCError_InvalidTokens;
goto failed;
}
}
} else {
}
else
{
/* If we're eating a string and get whitespace, we need to restage.
* (Note that if we are quoted, this is being ignored)
*/
@ -594,8 +638,10 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
/* There's no string, so we must move this one down and eat up another */
strings[0].end = &parse_point[i];
rotate(strings);
} else if (!strings[1].quoted) {
err = SMCParse_InvalidTokens;
}
else if (!strings[1].quoted)
{
err = SMCError_InvalidTokens;
goto failed;
}
}
@ -603,7 +649,7 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
}
/* Advance which token we're on */
curtok++;
states.col++;
}
if (line_begin != in_buf)
@ -637,8 +683,10 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
parse_point = &parse_point[read];
parse_point -= bytes;
}
} else if (read == sizeof(in_buf) - 1) {
err = SMCParse_TokenOverflow;
}
else if (read == sizeof(in_buf) - 1)
{
err = SMCError_TokenOverflow;
goto failed;
}
}
@ -646,29 +694,31 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
/* If we're done parsing and there are tokens left over... */
if (curlevel)
{
err = SMCParse_InvalidSection5;
err = SMCError_InvalidSection5;
goto failed;
} else if (strings[0].ptr || strings[1].ptr) {
err = SMCParse_InvalidTokens;
}
else if (strings[0].ptr || strings[1].ptr)
{
err = SMCError_InvalidTokens;
goto failed;
}
smc->ReadSMC_ParseEnd(false, false);
if (pStates != NULL)
{
*pStates = states;
}
return SMCParse_Okay;
return SMCError_Okay;
failed:
if (line)
if (pStates != NULL)
{
*line = curline;
*pStates = states;
}
smc->ReadSMC_ParseEnd(true, (err == SMCParse_Custom));
if (col)
{
*col = curtok;
}
smc->ReadSMC_ParseEnd(true, (err == SMCError_Custom));
return err;
}
@ -976,7 +1026,7 @@ event_failed:
return false;
}
const char *TextParsers::GetSMCErrorString(SMCParseError err)
const char *TextParsers::GetSMCErrorString(SMCError err)
{
static const char *s_errors[] =
{
@ -994,7 +1044,7 @@ const char *TextParsers::GetSMCErrorString(SMCParseError err)
"A property was declared outside of a section",
};
if (err < SMCParse_Okay || err > SMCParse_InvalidProperty1)
if (err < SMCError_Okay || err > SMCError_InvalidProperty1)
{
return NULL;
}

View File

@ -60,28 +60,26 @@ public:
unsigned int *line,
unsigned int *col);
SMCParseError ParseFile_SMC(const char *file,
SMCError ParseFile_SMC(const char *file,
ITextListener_SMC *smc_listener,
unsigned int *line,
unsigned int *col);
SMCStates *states);
unsigned int GetUTF8CharBytes(const char *stream);
const char *GetSMCErrorString(SMCParseError err);
const char *GetSMCErrorString(SMCError err);
bool IsWhitespace(const char *stream);
private:
SMCParseError ParseString_SMC(const char *stream,
SMCError ParseString_SMC(const char *stream,
ITextListener_SMC *smc,
unsigned int *line,
unsigned int *col);
SMCParseError ParseStream_SMC(void *stream,
SMCStates *states);
SMCError ParseStream_SMC(void *stream,
STREAMREADER srdr,
ITextListener_SMC *smc,
unsigned int *line,
unsigned int *col);
SMCStates *states);
};
extern TextParsers g_TextParser;
#endif //_INCLUDE_SOURCEMOD_TEXTPARSERS_H_

View File

@ -119,7 +119,7 @@ void CPhraseFile::ReparseFile()
return;
}
SMCParseError err;
SMCError err;
char path[PLATFORM_MAX_PATH];
g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "translations/%s", m_File.c_str());
@ -137,9 +137,9 @@ void CPhraseFile::ReparseFile()
}
}
unsigned int line=0, col=0;
SMCStates states;
if ((err=textparsers->ParseFile_SMC(path, this, &line, &col)) != SMCParse_Okay)
if ((err=textparsers->ParseFile_SMC(path, this, &states)) != SMCError_Okay)
{
const char *msg = textparsers->GetSMCErrorString(err);
if (!msg)
@ -148,7 +148,7 @@ void CPhraseFile::ReparseFile()
}
g_Logger.LogError("[SM] Fatal error encountered parsing translation file \"%s\"", m_File.c_str());
g_Logger.LogError("[SM] Error (line %d, column %d): %s", line, col, msg);
g_Logger.LogError("[SM] Error (line %d, column %d): %s", states.line, states.col, msg);
}
}
@ -156,19 +156,11 @@ void CPhraseFile::ReadSMC_ParseStart()
{
m_CurPhrase = -1;
m_ParseState = PPS_None;
m_CurLine = 0;
m_FileLogged = false;
m_LastPhraseString.clear();
}
SMCParseResult CPhraseFile::ReadSMC_RawLine(const char *line, unsigned int curline)
{
m_CurLine = curline;
return SMCParse_Continue;
}
SMCParseResult CPhraseFile::ReadSMC_NewSection(const char *name, bool opt_quotes)
SMCResult CPhraseFile::ReadSMC_NewSection(const SMCStates *states, const char *name)
{
bool recognized = false;
if (m_ParseState == PPS_None)
@ -178,7 +170,9 @@ SMCParseResult CPhraseFile::ReadSMC_NewSection(const char *name, bool opt_quotes
m_ParseState = PPS_Phrases;
recognized = true;
}
} else if (m_ParseState == PPS_Phrases) {
}
else if (m_ParseState == PPS_Phrases)
{
m_ParseState = PPS_InPhrase;
recognized = true;
@ -188,10 +182,12 @@ SMCParseResult CPhraseFile::ReadSMC_NewSection(const char *name, bool opt_quotes
/* Create the reverse lookup */
if (!sm_trie_insert(m_pPhraseLookup, name, reinterpret_cast<void *>(m_CurPhrase)))
{
ParseWarning("Skipping duplicate phrase \"%s\" on line %d.", name, m_CurLine);
ParseWarning("Skipping duplicate phrase \"%s\" on line %d.", name, states->line);
/* :IDEA: prevent memory waste by seeking backwards in memtable? */
m_CurPhrase = -1;
} else {
}
else
{
/* Initialize new phrase */
trans_t *pTrans;
@ -211,25 +207,27 @@ SMCParseResult CPhraseFile::ReadSMC_NewSection(const char *name, bool opt_quotes
}
m_LastPhraseString.assign(name);
}
} else if (m_ParseState == PPS_InPhrase) {
}
else if (m_ParseState == PPS_InPhrase)
{
ParseError("Phrase sections may not have sub-sections");
return SMCParse_HaltFail;
return SMCResult_HaltFail;
}
if (!recognized)
{
ParseWarning("Ignoring invalid section \"%s\" on line %d.", name, m_CurLine);
ParseWarning("Ignoring invalid section \"%s\" on line %d.", name, states->line);
}
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
SMCResult CPhraseFile::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
/* See if we are ignoring a phrase */
if (m_CurPhrase == -1)
{
return SMCParse_Continue;
return SMCResult_Continue;
}
phrase_t *pPhrase = (phrase_t *)m_pMemory->GetAddress(m_CurPhrase);
@ -238,14 +236,14 @@ SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value,
{
if (pPhrase->fmt_list != -1)
{
ParseWarning("Ignoring duplicated #format property on line %d", m_CurLine);
return SMCParse_Continue;
ParseWarning("Ignoring duplicated #format property on line %d", states->line);
return SMCResult_Continue;
}
if (pPhrase->translations > 0)
{
ParseWarning("#format property should come before translations on line %d, ignoring", m_CurLine);
return SMCParse_Continue;
ParseWarning("#format property should come before translations on line %d, ignoring", states->line);
return SMCResult_Continue;
}
/* Do an initial browsing for error checking and what not */
@ -268,37 +266,47 @@ SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value,
{
pPhrase->fmt_count++;
state = Parse_Index;
} else if (*value == ',') {
}
else if (*value == ',')
{
/* Do nothing */
} else {
}
else
{
unsigned int bytes = textparsers->GetUTF8CharBytes(value);
if (bytes != 1 || !isalpha(*value))
{
ParseWarning("Invalid token '%c' in #format property on line %d.", *value, m_CurLine);
ParseWarning("Invalid token '%c' in #format property on line %d.", *value, states->line);
}
}
} else if (state == Parse_Index) {
}
else if (state == Parse_Index)
{
if (*value == ':')
{
state = Parse_Format;
if (value - last_value >= 15)
{
ParseWarning("Too many digits in format index on line %d, phrase will be ignored.", m_CurLine);
ParseWarning("Too many digits in format index on line %d, phrase will be ignored.", states->line);
m_CurPhrase = -1;
return SMCParse_Continue;
return SMCResult_Continue;
}
} else {
}
else
{
unsigned int bytes = textparsers->GetUTF8CharBytes(value);
if (bytes != 1 || !isdigit(*value))
{
ParseWarning("Token '%c' in #format property on line %d is not a digit, phrase will be ignored.",
*value,
m_CurLine);
states->line);
m_CurPhrase = -1;
return SMCParse_Continue;
return SMCResult_Continue;
}
}
} else if (state == Parse_Format) {
}
else if (state == Parse_Format)
{
if (*value == '}')
{
state = Parse_None;
@ -311,9 +319,9 @@ SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value,
if (state != Parse_None)
{
/* Moose clam cow. */
ParseWarning("Unterminated format string on line %d, phrase will be ignored.", m_CurLine);
ParseWarning("Unterminated format string on line %d, phrase will be ignored.", states->line);
m_CurPhrase = -1;
return SMCParse_Continue;
return SMCResult_Continue;
}
value = old_value;
@ -347,41 +355,49 @@ SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value,
state = Parse_Index;
idx_ptr = NULL;
}
} else if (state == Parse_Index) {
}
else if (state == Parse_Index)
{
if (*in_ptr == ':')
{
/* Check the number! */
if (!idx_ptr)
{
ParseWarning("Format property contains unindexed format string on line %d, phrase will be ignored.", m_CurLine);
ParseWarning("Format property contains unindexed format string on line %d, phrase will be ignored.", states->line);
m_CurPhrase = -1;
return SMCParse_Continue;
return SMCResult_Continue;
}
long idx = strtol(idx_ptr, NULL, 10);
if (idx < 1 || idx > (long)pPhrase->fmt_count)
{
ParseWarning("Format property contains invalid index '%d' on line %d, phrase will be ignored.", idx, m_CurLine);
ParseWarning("Format property contains invalid index '%d' on line %d, phrase will be ignored.", idx, states->line);
m_CurPhrase = -1;
return SMCParse_Continue;
} else if (fmt_list[idx - 1] != -1) {
ParseWarning("Format property contains duplicated index '%d' on line %d, phrase will be ignored.", idx, m_CurLine);
return SMCResult_Continue;
}
else if (fmt_list[idx - 1] != -1)
{
ParseWarning("Format property contains duplicated index '%d' on line %d, phrase will be ignored.", idx, states->line);
m_CurPhrase = -1;
return SMCParse_Continue;
return SMCResult_Continue;
}
cur_idx = (unsigned int)idx;
state = Parse_Format;
out_ptr = NULL;
} else if (!idx_ptr) {
}
else if (!idx_ptr)
{
idx_ptr = in_ptr;
}
} else if (state == Parse_Format) {
}
else if (state == Parse_Format)
{
if (*in_ptr == '}')
{
if (!out_ptr)
{
ParseWarning("Format property contains empty format string on line %d, phrase will be ignored.", m_CurLine);
ParseWarning("Format property contains empty format string on line %d, phrase will be ignored.", states->line);
m_CurPhrase = -1;
return SMCParse_Continue;
return SMCResult_Continue;
}
*out_ptr = '\0';
state = Parse_None;
@ -392,7 +408,9 @@ SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value,
pPhrase->fmt_bytes += strlen(fmt_buf);
fmt_list = (int *)m_pMemory->GetAddress(pPhrase->fmt_list);
fmt_list[cur_idx - 1] = tmp_idx;
} else {
}
else
{
if (!out_ptr)
{
out_ptr = fmt_buf;
@ -402,9 +420,9 @@ SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value,
if ((unsigned)(out_ptr - fmt_buf) >= sizeof(fmt_buf) - 1)
{
ParseWarning("Format property contains format string that exceeds maximum length on line %d, phrase will be ignored.",
m_CurLine);
states->line);
m_CurPhrase = -1;
return SMCParse_Continue;
return SMCResult_Continue;
}
*out_ptr++ = *in_ptr;
}
@ -421,17 +439,19 @@ SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value,
{
ParseWarning("Format property contains no string for index %d on line %d, phrase will be ignored.",
i + 1,
m_CurLine);
states->line);
m_CurPhrase = -1;
return SMCParse_Continue;
return SMCResult_Continue;
}
}
} else {
}
else
{
size_t len = strlen(key);
if (len != 2)
{
ParseWarning("Ignoring translation to invalid language \"%s\" on line %d.", key, m_CurLine);
return SMCParse_Continue;
ParseWarning("Ignoring translation to invalid language \"%s\" on line %d.", key, states->line);
return SMCResult_Continue;
}
unsigned int lang;
@ -440,7 +460,7 @@ SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value,
/* Ignore if we don't have a language.
* :IDEA: issue a one-time alert?
*/
return SMCParse_Continue;
return SMCResult_Continue;
}
/* See how many bytes we need for this string, then allocate.
@ -515,7 +535,9 @@ SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value,
}
/* Skip past the last byte read */
in_ptr++;
} else if (*in_ptr == '{' && fmt_list != NULL) {
}
else if (*in_ptr == '{' && fmt_list != NULL)
{
/* Search for parameters if this is a formatted string */
const char *scrap_in_point = in_ptr;
const char *digit_start = ++in_ptr;
@ -570,10 +592,10 @@ cont_loop:
pPhrase->translations++;
}
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult CPhraseFile::ReadSMC_LeavingSection()
SMCResult CPhraseFile::ReadSMC_LeavingSection(const SMCStates *states)
{
if (m_ParseState == PPS_InPhrase)
{
@ -584,11 +606,13 @@ SMCParseResult CPhraseFile::ReadSMC_LeavingSection()
m_CurPhrase = -1;
m_ParseState = PPS_Phrases;
m_LastPhraseString.assign("");
} else if (m_ParseState == PPS_Phrases) {
}
else if (m_ParseState == PPS_Phrases)
{
m_ParseState = PPS_None;
}
return SMCParse_Continue;
return SMCResult_Continue;
}
void CPhraseFile::ReadSMC_ParseEnd(bool halted, bool failed)
@ -623,9 +647,8 @@ TransError CPhraseFile::GetTranslation(const char *szPhrase, unsigned int lang_i
return Trans_BadPhraseLanguage;
}
pTrans->fmt_order = (int *)m_pMemory->GetAddress(trans->fmt_order);
pTrans->fmt_count = pPhrase->fmt_count;
pTrans->fmt_order = pTrans->fmt_count > 0 ? (int *)m_pMemory->GetAddress(trans->fmt_order) : NULL;
pTrans->szPhrase = m_pStringTab->GetString(trans->stridx);
return Trans_Okay;
@ -811,9 +834,9 @@ void Translator::RebuildLanguageDatabase(const char *lang_header_file)
m_Languages.clear();
/* Start anew */
SMCParseError err;
unsigned int line=0, col=0;
if ((err=textparsers->ParseFile_SMC(lang_header_file, this, &line, &col)) != SMCParse_Okay)
SMCError err;
SMCStates states;
if ((err=textparsers->ParseFile_SMC(lang_header_file, this, &states)) != SMCError_Okay)
{
const char *str_err = textparsers->GetSMCErrorString(err);
if (!str_err)
@ -822,7 +845,7 @@ void Translator::RebuildLanguageDatabase(const char *lang_header_file)
}
g_Logger.LogError("[SM] Failed to parse language header file: \"%s\"", lang_header_file);
g_Logger.LogError("[SM] Parse error (line %d, column %d): %s", line, col, str_err);
g_Logger.LogError("[SM] Parse error (line %d, column %d): %s", states.line, states.col, str_err);
}
void *serverLang;
@ -854,7 +877,7 @@ void Translator::ReadSMC_ParseStart()
m_CustomError.clear();
}
SMCParseResult Translator::ReadSMC_NewSection(const char *name, bool opt_quotes)
SMCResult Translator::ReadSMC_NewSection(const SMCStates *states, const char *name)
{
if (!m_InLanguageSection)
{
@ -869,17 +892,17 @@ SMCParseResult Translator::ReadSMC_NewSection(const char *name, bool opt_quotes)
g_Logger.LogError("[SM] Warning: Unrecognized section \"%s\" in languages.cfg", name);
}
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult Translator::ReadSMC_LeavingSection()
SMCResult Translator::ReadSMC_LeavingSection(const SMCStates *states)
{
m_InLanguageSection = false;
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult Translator::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
SMCResult Translator::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
size_t len = strlen(key);
@ -891,7 +914,7 @@ SMCParseResult Translator::ReadSMC_KeyValue(const char *key, const char *value,
AddLanguage(key, value);
return SMCParse_Continue;
return SMCResult_Continue;
}
bool Translator::AddLanguage(const char *langcode, const char *description)

View File

@ -89,11 +89,10 @@ public:
TransError GetTranslation(const char *szPhrase, unsigned int lang_id, Translation *pTrans);
public: //ITextListener_SMC
void ReadSMC_ParseStart();
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
void ReadSMC_ParseEnd(bool halted, bool failed);
SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes);
SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes);
SMCParseResult ReadSMC_LeavingSection();
SMCParseResult ReadSMC_RawLine(const char *line, unsigned int curline);
private:
void ParseError(const char *message, ...);
void ParseWarning(const char *message, ...);
@ -108,7 +107,6 @@ private:
unsigned int m_LangCount;
String m_ParseError;
String m_LastPhraseString;
unsigned int m_CurLine;
bool m_FileLogged;
};
@ -129,9 +127,9 @@ public: // SMGlobalClass
void OnSourceModLevelChange(const char *mapName);
public: // ITextListener_SMC
void ReadSMC_ParseStart();
SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes);
SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes);
SMCParseResult ReadSMC_LeavingSection();
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
public:
void RebuildLanguageDatabase(const char *lang_header_file);
unsigned int FindOrAddPhraseFile(const char *phrase_file);

View File

@ -71,41 +71,41 @@ public:
}
}
SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes)
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
{
cell_t result = SMCParse_Continue;
cell_t result = SMCResult_Continue;
if (new_section)
{
new_section->PushCell(handle);
new_section->PushString(name);
new_section->PushCell(opt_quotes ? 1 : 0);
new_section->PushCell(1);
new_section->Execute(&result);
}
return (SMCParseResult)result;
return (SMCResult)result;
}
SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
cell_t result = SMCParse_Continue;
cell_t result = SMCResult_Continue;
if (key_value)
{
key_value->PushCell(handle);
key_value->PushString(key);
key_value->PushString(value);
key_value->PushCell(key_quotes ? 1 : 0);
key_value->PushCell(value_quotes ? 1 : 0);
key_value->PushCell(1);
key_value->PushCell(1);
key_value->Execute(&result);
}
return (SMCParseResult)result;
return (SMCResult)result;
}
SMCParseResult ReadSMC_LeavingSection()
SMCResult ReadSMC_LeavingSection(const SMCStates *states)
{
cell_t result = SMCParse_Continue;
cell_t result = SMCResult_Continue;
if (end_section)
{
@ -113,22 +113,22 @@ public:
end_section->Execute(&result);
}
return (SMCParseResult)result;
return (SMCResult)result;
}
SMCParseResult ReadSMC_RawLine(const char *line, unsigned int curline)
SMCResult ReadSMC_RawLine(const SMCStates *states, const char *line)
{
cell_t result = SMCParse_Continue;
cell_t result = SMCResult_Continue;
if (raw_line)
{
raw_line->PushCell(handle);
raw_line->PushString(line);
raw_line->PushCell(curline);
raw_line->PushCell(states->line);
raw_line->Execute(&result);
}
return (SMCParseResult)result;
return (SMCResult)result;
}
public:
IPluginFunction *parse_start;
@ -279,22 +279,22 @@ static cell_t SMC_ParseFile(IPluginContext *pContext, const cell_t *params)
char path[PLATFORM_MAX_PATH];
g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "%s", file);
unsigned int line = 0, col = 0;
SMCParseError p_err = textparsers->ParseFile_SMC(path, parse, &line, &col);
SMCStates states;
SMCError p_err = textparsers->ParseFile_SMC(path, parse, &states);
cell_t *c_line, *c_col;
pContext->LocalToPhysAddr(params[3], &c_line);
pContext->LocalToPhysAddr(params[4], &c_col);
*c_line = line;
*c_col = col;
*c_line = states.line;
*c_col = states.col;
return (cell_t)p_err;
}
static cell_t SMC_GetErrorString(IPluginContext *pContext, const cell_t *params)
{
const char *str = textparsers->GetSMCErrorString((SMCParseError)params[1]);
const char *str = textparsers->GetSMCErrorString((SMCError)params[1]);
if (!str)
{

View File

@ -82,7 +82,7 @@ void CPluginInfoDatabase::ReadSMC_ParseStart()
m_infodb = -1;
}
SMCParseResult CPluginInfoDatabase::MakeError(const char *fmt, ...)
SMCResult CPluginInfoDatabase::MakeError(const char *fmt, ...)
{
char buffer[512];
va_list ap;
@ -93,7 +93,7 @@ SMCParseResult CPluginInfoDatabase::MakeError(const char *fmt, ...)
m_errmsg = m_strtab->AddString(buffer);
return SMCParse_HaltFail;
return SMCResult_HaltFail;
}
unsigned int CPluginInfoDatabase::GetSettingsNum()
@ -149,10 +149,7 @@ void CPluginInfoDatabase::GetOptionsForPlugin(PluginSettings *settings, unsigned
*val = m_strtab->GetString(table[opt_num].val);
}
SMCParseResult CPluginInfoDatabase::ReadSMC_KeyValue(const char *key,
const char *value,
bool key_quotes,
bool value_quotes)
SMCResult CPluginInfoDatabase::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
if (cur_plugin != -1)
{
@ -164,28 +161,46 @@ SMCParseResult CPluginInfoDatabase::ReadSMC_KeyValue(const char *key,
if (strcasecmp(value, "yes") == 0)
{
plugin->pause_val = true;
} else {
}
else
{
plugin->pause_val = false;
}
} else if (strcmp(key, "lifetime") == 0) {
}
else if (strcmp(key, "lifetime") == 0)
{
if (strcasecmp(value, "private") == 0)
{
plugin->type_val = PluginType_Private;
} else if (strcasecmp(value, "mapsync") == 0) {
}
else if (strcasecmp(value, "mapsync") == 0)
{
plugin->type_val = PluginType_MapUpdated;
} else if (strcasecmp(value, "maponly") == 0) {
}
else if (strcasecmp(value, "maponly") == 0)
{
plugin->type_val = PluginType_MapOnly;
} else if (strcasecmp(value, "global") == 0) {
}
else if (strcasecmp(value, "global") == 0)
{
plugin->type_val = PluginType_Global;
} else {
}
else
{
return MakeError("Unknown value for key \"lifetime\": \"%s\"", value);
}
} else if (strcmp(key, "blockload") == 0) {
}
else if (strcmp(key, "blockload") == 0)
{
plugin->blockload_val = true;
} else {
}
else
{
return MakeError("Unknown property key: \"%s\"", key);
}
} else {
}
else
{
/* Cache every option, valid or not */
int keyidx = m_strtab->AddString(key);
int validx = m_strtab->AddString(value);
@ -199,7 +214,9 @@ SMCParseResult CPluginInfoDatabase::ReadSMC_KeyValue(const char *key,
{
//right now we don't have many
plugin->opts_size = 2;
} else {
}
else
{
plugin->opts_size *= 2;
}
int newidx = memtab->CreateMem(plugin->opts_size * sizeof(PluginOpts), (void **)&table);
@ -211,23 +228,29 @@ SMCParseResult CPluginInfoDatabase::ReadSMC_KeyValue(const char *key,
memcpy(table, oldtable, oldsize * sizeof(PluginOpts));
}
plugin->optarray = newidx;
} else {
}
else
{
table = (PluginOpts *)memtab->GetAddress(plugin->optarray);
}
PluginOpts *opt = &table[plugin->opts_num++];
opt->key = keyidx;
opt->val = validx;
}
} else if (in_plugins) {
}
else if (in_plugins)
{
return MakeError("Unknown property key: \"%s\"", key);
} else {
}
else
{
/* Ignore anything we don't know about! */
}
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult CPluginInfoDatabase::ReadSMC_LeavingSection()
SMCResult CPluginInfoDatabase::ReadSMC_LeavingSection(const SMCStates *states)
{
if (in_plugins)
{
@ -236,7 +259,9 @@ SMCParseResult CPluginInfoDatabase::ReadSMC_LeavingSection()
if (in_options)
{
in_options = false;
} else {
}
else
{
/* If the plugin is ending, add it to the table */
BaseMemTable *memtab = m_strtab->GetMemTable();
int *table;
@ -246,7 +271,9 @@ SMCParseResult CPluginInfoDatabase::ReadSMC_LeavingSection()
if (!m_infodb_size)
{
m_infodb_size = 8;
} else {
}
else
{
m_infodb_size *= 2;
}
int newidx = memtab->CreateMem(m_infodb_size, (void **)&table);
@ -256,22 +283,26 @@ SMCParseResult CPluginInfoDatabase::ReadSMC_LeavingSection()
memcpy(table, oldtable, oldsize * sizeof(int));
}
m_infodb = newidx;
} else {
}
else
{
table = (int *)memtab->GetAddress(m_infodb);
}
/* Assign to table and scrap the current plugin */
table[m_infodb_count++] = cur_plugin;
cur_plugin = -1;
}
} else {
}
else
{
in_plugins = false;
}
}
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult CPluginInfoDatabase::ReadSMC_NewSection(const char *name, bool opt_quotes)
SMCResult CPluginInfoDatabase::ReadSMC_NewSection(const SMCStates *states, const char *name)
{
if (!in_plugins)
{
@ -279,13 +310,17 @@ SMCParseResult CPluginInfoDatabase::ReadSMC_NewSection(const char *name, bool op
if (strcmp(name, "Plugins") != 0)
{
return MakeError("Unknown root section: \"%s\"", name);
} else {
}
else
{
/* Otherwise set our states */
in_plugins = true;
cur_plugin = -1;
in_options = false;
}
} else {
}
else
{
if (cur_plugin == -1)
{
/* If we get a plugin node and we don't have a current plugin, create a new one */
@ -295,15 +330,20 @@ SMCParseResult CPluginInfoDatabase::ReadSMC_NewSection(const char *name, bool op
plugin->Init();
plugin->name = i_name;
in_options = false;
} else {
}
else
{
if (!in_options && strcmp(name, "Options") == 0)
{
in_options = true;
} else {
}
else
{
return MakeError("Unknown plugin sub-section: \"%s\"", name);
}
}
}
return SMCParse_Continue;
return SMCResult_Continue;
}

View File

@ -68,9 +68,9 @@ public:
~CPluginInfoDatabase();
public: //ITextListener_SMC
void ReadSMC_ParseStart();
SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes);
SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes);
SMCParseResult ReadSMC_LeavingSection();
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
public:
/**
* Returns the number of plugin settings available.
@ -90,7 +90,7 @@ public:
*/
void GetOptionsForPlugin(PluginSettings *settings, unsigned int opt_num, const char **key, const char **val);
private:
SMCParseResult MakeError(const char *fmt, ...);
SMCResult MakeError(const char *fmt, ...);
private:
BaseStringTable *m_strtab;
int m_errmsg;

View File

@ -819,10 +819,10 @@ void CPluginManager::Shutdown()
void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
{
/* First read in the database of plugin settings */
SMCParseError err;
unsigned int line, col;
SMCError err;
SMCStates states;
m_AllPluginsLoaded = false;
if ((err=textparsers->ParseFile_SMC(config, &m_PluginInfo, &line, &col)) != SMCParse_Okay)
if ((err=textparsers->ParseFile_SMC(config, &m_PluginInfo, &states)) != SMCError_Okay)
{
g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
const char *err_msg = textparsers->GetSMCErrorString(err);

View File

@ -811,7 +811,7 @@ int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *sour
len = bytes - 1;
}
memcpy(dest, source, len);
memmove(dest, source, len);
dest[len] = '\0';
return SP_ERROR_NONE;
@ -880,7 +880,7 @@ int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const cha
needtocheck = true;
}
memcpy(dest, source, len);
memmove(dest, source, len);
if ((dest[len-1] & 1<<7) && needtocheck)
{
len -= __CheckValidChar(dest+len-1);

View File

@ -895,11 +895,11 @@ void TopMenu::TearDownClient(topmenu_player_t *player)
bool TopMenu::LoadConfiguration(const char *file, char *error, size_t maxlength)
{
SMCParseError err;
unsigned int line = 0, col = 0;
SMCError err;
SMCStates states;
if ((err = textparsers->ParseFile_SMC(file, this, &line, &col))
!= SMCParse_Okay)
if ((err = textparsers->ParseFile_SMC(file, this, &states))
!= SMCError_Okay)
{
const char *err_string = textparsers->GetSMCErrorString(err);
if (!err_string)
@ -971,7 +971,7 @@ void TopMenu::ReadSMC_ParseStart()
m_Config.cats.clear();
}
SMCParseResult TopMenu::ReadSMC_NewSection(const char *name, bool opt_quotes)
SMCResult TopMenu::ReadSMC_NewSection(const SMCStates *states, const char *name)
{
if (ignore_parse_level)
{
@ -1003,16 +1003,16 @@ SMCParseResult TopMenu::ReadSMC_NewSection(const char *name, bool opt_quotes)
}
}
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult TopMenu::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
SMCResult TopMenu::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
if (ignore_parse_level > 0
|| current_parse_state != PARSE_STATE_CATEGORY
|| cur_cat == NULL)
{
return SMCParse_Continue;
return SMCResult_Continue;
}
if (strcmp(key, "item") == 0)
@ -1020,10 +1020,10 @@ SMCParseResult TopMenu::ReadSMC_KeyValue(const char *key, const char *value, boo
cur_cat->commands.push_back(m_Config.strings.AddString(value));
}
return SMCParse_Continue;
return SMCResult_Continue;
}
SMCParseResult TopMenu::ReadSMC_LeavingSection()
SMCResult TopMenu::ReadSMC_LeavingSection(const SMCStates *states)
{
if (ignore_parse_level)
{
@ -1042,7 +1042,7 @@ SMCParseResult TopMenu::ReadSMC_LeavingSection()
}
}
return SMCParse_Continue;
return SMCResult_Continue;
}
unsigned int TopMenu::FindCategory(const char *name)

View File

@ -141,13 +141,10 @@ public: //IMenuHandler
const ItemDrawInfo &dr);
virtual void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
public: //ITextListener_SMC
virtual void ReadSMC_ParseStart();
SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes);
SMCParseResult ReadSMC_KeyValue(const char *key,
const char *value,
bool key_quotes,
bool value_quotes);
SMCParseResult ReadSMC_LeavingSection();
void ReadSMC_ParseStart();
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
private:
void SortCategoriesIfNeeded();
void SortCategoryIfNeeded(unsigned int category);

View File

@ -41,6 +41,10 @@
namespace SourceMod
{
#define SMINTERFACE_TEXTPARSERS_NAME "ITextParsers"
#define SMINTERFACE_TEXTPARSERS_VERSION 2
/**
* The INI file format is defined as:
* WHITESPACE: 0x20, \n, \t, \r
@ -85,6 +89,14 @@ namespace SourceMod
*/
class ITextListener_INI
{
public:
/**
* @brief Returns version number.
*/
virtual unsigned int GetTextParserVersion1()
{
return SMINTERFACE_TEXTPARSERS_VERSION;
}
public:
/**
* @brief Called when a new section is encountered in an INI file.
@ -183,30 +195,39 @@ namespace SourceMod
/**
* @brief Lists actions to take when an SMC parse hook is done.
*/
enum SMCParseResult
enum SMCResult
{
SMCParse_Continue, /**< Continue parsing */
SMCParse_Halt, /**< Stop parsing here */
SMCParse_HaltFail /**< Stop parsing and return SMCParseError_Custom */
SMCResult_Continue, /**< Continue parsing */
SMCResult_Halt, /**< Stop parsing here */
SMCResult_HaltFail /**< Stop parsing and return SMCError_Custom */
};
/**
* @brief Lists error codes possible from parsing an SMC file.
*/
enum SMCParseError
enum SMCError
{
SMCParse_Okay = 0, /**< No error */
SMCParse_StreamOpen, /**< Stream failed to open */
SMCParse_StreamError, /**< The stream died... somehow */
SMCParse_Custom, /**< A custom handler threw an error */
SMCParse_InvalidSection1, /**< A section was declared without quotes, and had extra tokens */
SMCParse_InvalidSection2, /**< A section was declared without any header */
SMCParse_InvalidSection3, /**< A section ending was declared with too many unknown tokens */
SMCParse_InvalidSection4, /**< A section ending has no matching beginning */
SMCParse_InvalidSection5, /**< A section beginning has no matching ending */
SMCParse_InvalidTokens, /**< There were too many unidentifiable strings on one line */
SMCParse_TokenOverflow, /**< The token buffer overflowed */
SMCParse_InvalidProperty1, /**< A property was declared outside of any section */
SMCError_Okay = 0, /**< No error */
SMCError_StreamOpen, /**< Stream failed to open */
SMCError_StreamError, /**< The stream died... somehow */
SMCError_Custom, /**< A custom handler threw an error */
SMCError_InvalidSection1, /**< A section was declared without quotes, and had extra tokens */
SMCError_InvalidSection2, /**< A section was declared without any header */
SMCError_InvalidSection3, /**< A section ending was declared with too many unknown tokens */
SMCError_InvalidSection4, /**< A section ending has no matching beginning */
SMCError_InvalidSection5, /**< A section beginning has no matching ending */
SMCError_InvalidTokens, /**< There were too many unidentifiable strings on one line */
SMCError_TokenOverflow, /**< The token buffer overflowed */
SMCError_InvalidProperty1, /**< A property was declared outside of any section */
};
/**
* @brief States for line/column
*/
struct SMCStates
{
unsigned int line; /**< Current line */
unsigned int col; /**< Current col */
};
/**
@ -214,6 +235,14 @@ namespace SourceMod
*/
class ITextListener_SMC
{
public:
/**
* @brief Returns version number.
*/
virtual unsigned int GetTextParserVersion2()
{
return SMINTERFACE_TEXTPARSERS_VERSION;
}
public:
/**
* @brief Called when starting parsing.
@ -232,73 +261,58 @@ namespace SourceMod
{
}
/**
* @brief Called when a warning occurs.
* @param error By-reference variable containing the error message of the warning.
* @param tokens Pointer to the token stream causing the error.
* @return SMCParseResult directive.
*/
virtual SMCParseResult ReadSMC_OnWarning(SMCParseError &error, const char *tokens)
{
return SMCParse_HaltFail;
}
/**
* @brief Called when entering a new section
*
* @param states Parsing states.
* @param name Name of section, with the colon omitted.
* @param opt_quotes Whether or not the option string was enclosed in quotes.
* @return SMCParseResult directive.
* @return SMCResult directive.
*/
virtual SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes)
virtual SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
{
return SMCParse_Continue;
return SMCResult_Continue;
}
/**
* @brief Called when encountering a key/value pair in a section.
*
* @param states Parsing states.
* @param key Key string.
* @param value Value string. If no quotes were specified, this will be NULL,
and key will contain the entire string.
* @param key_quotes Whether or not the key was in quotation marks.
* @param value_quotes Whether or not the value was in quotation marks.
* @return SMCParseResult directive.
* and key will contain the entire string.
* @param Number of line in file.
* @return SMCResult directive.
*/
virtual SMCParseResult ReadSMC_KeyValue(const char *key,
const char *value,
bool key_quotes,
bool value_quotes)
virtual SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
return SMCParse_Continue;
return SMCResult_Continue;
}
/**
* @brief Called when leaving the current section.
*
* @return SMCParseResult directive.
* @param Parsing states.
* @return SMCResult directive.
*/
virtual SMCParseResult ReadSMC_LeavingSection()
virtual SMCResult ReadSMC_LeavingSection(const SMCStates *states)
{
return SMCParse_Continue;
return SMCResult_Continue;
}
/**
* @brief Called after an input line has been preprocessed.
*
* @param line String containing line input.
* @param curline Number of line in file.
* @return SMCParseResult directive.
* @param states Parsing states.
* @param line Contents of the line, null terminated at the position
* of the newline character (thus, no newline will exist).
* @return SMCResult directive.
*/
virtual SMCParseResult ReadSMC_RawLine(const char *line, unsigned int curline)
virtual SMCResult ReadSMC_RawLine(const SMCStates *states, const char *line)
{
return SMCParse_Continue;
return SMCResult_Continue;
}
};
#define SMINTERFACE_TEXTPARSERS_NAME "ITextParsers"
#define SMINTERFACE_TEXTPARSERS_VERSION 1
/**
* @brief Contains various text stream parsing functions.
*/
@ -313,6 +327,14 @@ namespace SourceMod
{
return SMINTERFACE_TEXTPARSERS_VERSION;
}
virtual bool IsVersionCompatible(unsigned int version)
{
if (version < 2)
{
return false;
}
return SMInterface::IsVersionCompatible(version);
}
public:
/**
* @brief Parses an INI-format file.
@ -336,22 +358,20 @@ namespace SourceMod
*
* @param file Path to file.
* @param smc_listener Event handler for reading file.
* @param line If non-NULL, will contain last line parsed (0 if file could not be opened).
* @param col If non-NULL, will contain last column parsed (undefined if file could not be opened).
* @return An SMCParseError result code.
* @param states Optional pointer to store last known states.
* @return An SMCError result code.
*/
virtual SMCParseError ParseFile_SMC(const char *file,
virtual SMCError ParseFile_SMC(const char *file,
ITextListener_SMC *smc_listener,
unsigned int *line,
unsigned int *col) =0;
SMCStates *states) =0;
/**
* @brief Converts an SMCParseError to a string.
* @brief Converts an SMCError to a string.
*
* @param err SMCParseError.
* @param err SMCError.
* @return String error message, or NULL if none.
*/
virtual const char *GetSMCErrorString(SMCParseError err) =0;
virtual const char *GetSMCErrorString(SMCError err) =0;
public:
/**
@ -395,3 +415,4 @@ namespace SourceMod
extern SourceMod::ITextParsers *textparsers;
#endif //_INCLUDE_SOURCEMOD_TEXTPARSERS_INTERFACE_H_