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

View File

@ -100,7 +100,7 @@ void CoreConfig::OnRootConsoleCommand(const char *cmdname, const CCommand &comma
void CoreConfig::Initialize() void CoreConfig::Initialize()
{ {
SMCParseError err; SMCError err;
char filePath[PLATFORM_MAX_PATH]; char filePath[PLATFORM_MAX_PATH];
/* Try to get command line value of core config convar */ /* 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); g_LibSys.PathFormat(filePath, sizeof(filePath), "%s/%s", g_SourceMod.GetGamePath(), corecfg);
/* Parse config file */ /* 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 */ /* :TODO: This won't actually log or print anything :( - So fix that somehow */
const char *error = textparsers->GetSMCErrorString(err); 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]; char error[255];
ConfigResult err = SetConfigOption(key, value, ConfigSource_File, error, sizeof(error)); 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); 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) 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 OnSourceModShutdown();
void OnSourceModLevelChange(const char *mapName); void OnSourceModLevelChange(const char *mapName);
public: // ITextListener_SMC 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 public: // IRootConsoleCommand
void OnRootConsoleCommand(const char *cmdname, const CCommand &command); void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
public: public:

View File

@ -75,21 +75,21 @@ void DBManager::OnSourceModAllInitialized()
void DBManager::OnSourceModLevelChange(const char *mapName) void DBManager::OnSourceModLevelChange(const char *mapName)
{ {
SMCParseError err; SMCError err;
unsigned int line = 0; SMCStates states = {0, 0};
/* We lock and don't give up the lock until we're done. /* 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 * This way the thread's search won't be searching through a
* potentially empty/corrupt list, which would be very bad. * potentially empty/corrupt list, which would be very bad.
*/ */
m_pConfigLock->Lock(); 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); 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); 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(); m_pConfigLock->Unlock();
@ -141,12 +141,12 @@ void DBManager::ReadSMC_ParseStart()
} }
ConfDbInfo s_CurInfo; 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) if (m_ParseLevel)
{ {
m_ParseLevel++; m_ParseLevel++;
return SMCParse_Continue; return SMCResult_Continue;
} }
if (m_ParseState == DBPARSE_LEVEL_NONE) if (m_ParseState == DBPARSE_LEVEL_NONE)
@ -165,14 +165,14 @@ SMCParseResult DBManager::ReadSMC_NewSection(const char *name, bool opt_quotes)
m_ParseLevel++; 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) if (m_ParseLevel)
{ {
return SMCParse_Continue; return SMCResult_Continue;
} }
if (m_ParseState == DBPARSE_LEVEL_MAIN) 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) \ #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); \ s_CurInfo.info.var = m_StrTab.GetString(s_CurInfo.var); \
} }
SMCParseResult DBManager::ReadSMC_LeavingSection() SMCResult DBManager::ReadSMC_LeavingSection(const SMCStates *states)
{ {
if (m_ParseLevel) if (m_ParseLevel)
{ {
m_ParseLevel--; m_ParseLevel--;
return SMCParse_Continue; return SMCResult_Continue;
} }
if (m_ParseState == DBPARSE_LEVEL_DATABASE) if (m_ParseState == DBPARSE_LEVEL_DATABASE)
@ -239,10 +239,10 @@ SMCParseResult DBManager::ReadSMC_LeavingSection()
m_ParseState = DBPARSE_LEVEL_MAIN; m_ParseState = DBPARSE_LEVEL_MAIN;
} else if (m_ParseState == DBPARSE_LEVEL_MAIN) { } else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
m_ParseState = DBPARSE_LEVEL_NONE; m_ParseState = DBPARSE_LEVEL_NONE;
return SMCParse_Halt; return SMCResult_Halt;
} }
return SMCParse_Continue; return SMCResult_Continue;
} }
#undef ASSIGN_VAR #undef ASSIGN_VAR

View File

@ -94,9 +94,9 @@ public: //IDBManager
HandleError ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token); HandleError ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token);
public: //ITextListener_SMC public: //ITextListener_SMC
void ReadSMC_ParseStart(); void ReadSMC_ParseStart();
SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes); SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
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);
SMCParseResult ReadSMC_LeavingSection(); SMCResult ReadSMC_LeavingSection(const SMCStates *states);
void ReadSMC_ParseEnd(bool halted, bool failed); void ReadSMC_ParseEnd(bool halted, bool failed);
public: //IThread public: //IThread
void RunThread(IThreadHandle *pThread); void RunThread(IThreadHandle *pThread);

View File

@ -106,12 +106,12 @@ CGameConfig::~CGameConfig()
delete m_pStrings; 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) if (m_IgnoreLevel)
{ {
m_IgnoreLevel++; m_IgnoreLevel++;
return SMCParse_Continue; return SMCResult_Continue;
} }
switch (m_ParseState) 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) if (m_IgnoreLevel)
{ {
return SMCParse_Continue; return SMCResult_Continue;
} }
if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET) 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) if (m_IgnoreLevel)
{ {
m_IgnoreLevel--; m_IgnoreLevel--;
return SMCParse_Continue; return SMCResult_Continue;
} }
switch (m_ParseState) switch (m_ParseState)
@ -478,12 +478,12 @@ skip_find:
} }
} }
return SMCParse_Continue; return SMCResult_Continue;
} }
bool CGameConfig::Reparse(char *error, size_t maxlength) bool CGameConfig::Reparse(char *error, size_t maxlength)
{ {
SMCParseError err; SMCError err;
char path[PLATFORM_MAX_PATH]; char path[PLATFORM_MAX_PATH];
g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s.txt", m_pFile); 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_pProps);
sm_trie_clear(m_pKeys); sm_trie_clear(m_pKeys);
if ((err=textparsers->ParseFile_SMC(path, this, NULL, NULL)) if ((err=textparsers->ParseFile_SMC(path, this, NULL))
!= SMCParse_Okay) != SMCError_Okay)
{ {
if (error && (err != SMCParse_Custom)) if (error && (err != SMCError_Custom))
{ {
const char *str = textparsers->GetSMCErrorString(err); const char *str = textparsers->GetSMCErrorString(err);
snprintf(error, maxlength, "%s", str); snprintf(error, maxlength, "%s", str);

View File

@ -55,9 +55,9 @@ public:
public: public:
bool Reparse(char *error, size_t maxlength); bool Reparse(char *error, size_t maxlength);
public: //ITextListener_SMC public: //ITextListener_SMC
SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes); SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
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);
SMCParseResult ReadSMC_LeavingSection(); SMCResult ReadSMC_LeavingSection(const SMCStates *states);
public: //IGameConfig public: //IGameConfig
const char *GetKeyValue(const char *key); const char *GetKeyValue(const char *key);
bool GetOffset(const char *key, int *value); 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; return true;
} }
SMCParseError TextParsers::ParseString_SMC(const char *stream, SMCError TextParsers::ParseString_SMC(const char *stream,
ITextListener_SMC *smc, ITextListener_SMC *smc,
unsigned int *line, SMCStates *states)
unsigned int *col)
{ {
CharStream srdr = { stream }; 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); 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"); FILE *fp = fopen(file, "rt");
if (!fp) 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); fclose(fp);
@ -273,34 +277,44 @@ char *lowstring(StringInfo info[3])
return NULL; return NULL;
} }
SMCParseError TextParsers::ParseStream_SMC(void *stream, SMCError TextParsers::ParseStream_SMC(void *stream,
STREAMREADER srdr, STREAMREADER srdr,
ITextListener_SMC *smc, ITextListener_SMC *smc,
unsigned int *line, SMCStates *pStates)
unsigned int *col)
{ {
char *reparse_point = NULL; char *reparse_point = NULL;
char in_buf[4096]; char in_buf[4096];
char *parse_point = in_buf; char *parse_point = in_buf;
char *line_begin = in_buf; char *line_begin = in_buf;
unsigned int read; unsigned int read;
unsigned int curline = 1;
unsigned int curtok = 0;
unsigned int curlevel = 0; unsigned int curlevel = 0;
bool in_quote = false; bool in_quote = false;
bool ignoring = false; bool ignoring = false;
bool eol_comment = false; bool eol_comment = false;
bool ml_comment = false; bool ml_comment = false;
unsigned int i; unsigned int i;
SMCParseError err = SMCParse_Okay; SMCError err = SMCError_Okay;
SMCParseResult res; SMCResult res;
SMCStates states;
char c; char c;
StringInfo strings[3]; StringInfo strings[3];
StringInfo emptystring; StringInfo emptystring;
states.line = 1;
states.col = 0;
smc->ReadSMC_ParseStart(); 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)) while (srdr(stream, parse_point, sizeof(in_buf) - (parse_point - in_buf) - 1, &read))
{ {
if (!read) if (!read)
@ -308,10 +322,10 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
break; break;
} }
/* :TODO: do this outside of the main loop somehow /* Check for BOM markings, which is only relevant on the first line.
* This checks for BOM markings * 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[0] == (char)0xEF &&
in_buf[1] == (char)0xBB && in_buf[1] == (char)0xBB &&
in_buf[2] == (char)0xBF) in_buf[2] == (char)0xBF)
@ -341,7 +355,7 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
strings[0].end = &parse_point[i]; strings[0].end = &parse_point[i];
if (rotate(strings) != NULL) if (rotate(strings) != NULL)
{ {
err = SMCParse_InvalidTokens; err = SMCError_InvalidTokens;
goto failed; goto failed;
} }
} }
@ -354,39 +368,44 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
ignoring = false; ignoring = false;
} }
/* Pass the raw line onto the listener */ /* Pass the raw line onto the listener. We terminate the line so the receiver
if ((res=smc->ReadSMC_RawLine(line_begin, curline)) != SMCParse_Continue) * 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; goto failed;
} }
parse_point[i] = '\n';
/* Now we check the sanity of our staged strings! */ /* Now we check the sanity of our staged strings! */
if (strings[2].ptr) if (strings[2].ptr)
{ {
if (!curlevel) if (!curlevel)
{ {
err = SMCParse_InvalidProperty1; err = SMCError_InvalidProperty1;
goto failed; goto failed;
} }
/* Assume the next string is a property and pass the info on. */ /* Assume the next string is a property and pass the info on. */
if ((res=smc->ReadSMC_KeyValue( if ((res=smc->ReadSMC_KeyValue(
&states,
FixupString(strings[2]), FixupString(strings[2]),
FixupString(strings[1]), FixupString(strings[1]))) != SMCResult_Continue)
strings[2].quoted,
strings[1].quoted)) != SMCParse_Continue)
{ {
err = (res == SMCParse_HaltFail) ? SMCParse_Custom : SMCParse_Okay; err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
goto failed; goto failed;
} }
scrap(strings); scrap(strings);
} }
/* Change the states for the next line */ /* Change the states for the next line */
curtok = 0; states.col = 0;
curline++; states.line++;
line_begin = &parse_point[i+1]; //Note: safe because this gets relocated later line_begin = &parse_point[i+1]; //Note: safe because this gets relocated later
} else if (ignoring) { }
else if (ignoring)
{
if (in_quote) if (in_quote)
{ {
/* If i was 0, this case is impossible due to reparsing */ /* 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 (rotate(strings) != NULL)
{ {
/* If we rotated too many strings, there was too much crap on one line */ /* If we rotated too many strings, there was too much crap on one line */
err = SMCParse_InvalidTokens; err = SMCError_InvalidTokens;
goto failed; goto failed;
} }
} else if (c == '\\') { }
else if (c == '\\')
{
strings[0].special = true; strings[0].special = true;
if (i == (read - 1)) if (i == (read - 1))
{ {
@ -414,7 +435,9 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
break; break;
} }
} }
} else if (ml_comment) { }
else if (ml_comment)
{
if (c == '*') if (c == '*')
{ {
/* Check if we need to get more input first */ /* Check if we need to get more input first */
@ -431,11 +454,13 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
assert(strings[0].ptr == NULL); assert(strings[0].ptr == NULL);
/* Advance the input stream so we don't choke on this token */ /* Advance the input stream so we don't choke on this token */
i++; i++;
curtok++; states.col++;
} }
} }
} }
} else { }
else
{
/* Check if we're whitespace or not */ /* Check if we're whitespace or not */
if (!g_ws_chartable[(unsigned)c]) if (!g_ws_chartable[(unsigned)c])
{ {
@ -468,7 +493,9 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
ignoring = true; ignoring = true;
eol_comment = true; eol_comment = true;
restage = true; restage = true;
} else if (parse_point[i+1] == '*') { }
else if (parse_point[i+1] == '*')
{
/* inline comment - start ignoring */ /* inline comment - start ignoring */
ignoring = true; ignoring = true;
ml_comment = true; ml_comment = true;
@ -479,93 +506,108 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
*/ */
restage = true; restage = true;
} }
} else { }
else
{
ignoring = true; ignoring = true;
eol_comment = true; eol_comment = true;
restage = true; restage = true;
} }
} else if (c == '{') { }
else if (c == '{')
{
/* If we are staging a string, we must rotate here */ /* If we are staging a string, we must rotate here */
if (strings[0].ptr) if (strings[0].ptr)
{ {
/* We have unacceptable tokens on this line */ /* We have unacceptable tokens on this line */
if (rotate(strings) != NULL) if (rotate(strings) != NULL)
{ {
err = SMCParse_InvalidSection1; err = SMCError_InvalidSection1;
goto failed; goto failed;
} }
} }
/* Sections must always be alone */ /* Sections must always be alone */
if (strings[2].ptr != NULL) if (strings[2].ptr != NULL)
{ {
err = SMCParse_InvalidSection1; err = SMCError_InvalidSection1;
goto failed; goto failed;
} else if (strings[1].ptr == NULL) { }
err = SMCParse_InvalidSection2; else if (strings[1].ptr == NULL)
{
err = SMCError_InvalidSection2;
goto failed; goto failed;
} }
if ((res=smc->ReadSMC_NewSection(FixupString(strings[1]), strings[1].quoted)) if ((res=smc->ReadSMC_NewSection(&states, FixupString(strings[1])))
!= SMCParse_Continue) != SMCResult_Continue)
{ {
err = (res == SMCParse_HaltFail) ? SMCParse_Custom : SMCParse_Okay; err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
goto failed; goto failed;
} }
strings[1] = emptystring; strings[1] = emptystring;
curlevel++; curlevel++;
} else if (c == '}') { }
else if (c == '}')
{
/* Unlike our matching friend, this can be on the same line as something prior */ /* Unlike our matching friend, this can be on the same line as something prior */
if (rotate(strings) != NULL) if (rotate(strings) != NULL)
{ {
err = SMCParse_InvalidSection3; err = SMCError_InvalidSection3;
goto failed; goto failed;
} }
if (strings[2].ptr) if (strings[2].ptr)
{ {
if (!curlevel) if (!curlevel)
{ {
err = SMCParse_InvalidProperty1; err = SMCError_InvalidProperty1;
goto failed; goto failed;
} }
if ((res=smc->ReadSMC_KeyValue( if ((res=smc->ReadSMC_KeyValue(
&states,
FixupString(strings[2]), FixupString(strings[2]),
FixupString(strings[1]), FixupString(strings[1])))
strings[2].quoted, != SMCResult_Continue)
strings[1].quoted))
!= SMCParse_Continue)
{ {
err = (res == SMCParse_HaltFail) ? SMCParse_Custom : SMCParse_Okay; err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
goto failed; goto failed;
} }
} else if (strings[1].ptr) { }
err = SMCParse_InvalidSection3; else if (strings[1].ptr)
{
err = SMCError_InvalidSection3;
goto failed; goto failed;
} else if (!curlevel) { }
err = SMCParse_InvalidSection4; else if (!curlevel)
{
err = SMCError_InvalidSection4;
goto failed; goto failed;
} }
/* Now it's safe to leave the section */ /* Now it's safe to leave the section */
scrap(strings); 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; goto failed;
} }
curlevel--; curlevel--;
} else if (c == '"') { }
else if (c == '"')
{
/* If we get a quote mark, we always restage, but we need to do it beforehand */ /* If we get a quote mark, we always restage, but we need to do it beforehand */
if (strings[0].ptr) if (strings[0].ptr)
{ {
strings[0].end = &parse_point[i]; strings[0].end = &parse_point[i];
if (rotate(strings) != NULL) if (rotate(strings) != NULL)
{ {
err = SMCParse_InvalidTokens; err = SMCError_InvalidTokens;
goto failed; goto failed;
} }
} }
strings[0].ptr = &parse_point[i]; strings[0].ptr = &parse_point[i];
in_quote = true; in_quote = true;
ignoring = true; ignoring = true;
} else if (!strings[0].ptr) { }
else if (!strings[0].ptr)
{
/* If we have no string, we must start one */ /* If we have no string, we must start one */
strings[0].ptr = &parse_point[i]; strings[0].ptr = &parse_point[i];
} }
@ -574,11 +616,13 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
strings[0].end = &parse_point[i]; strings[0].end = &parse_point[i];
if (rotate(strings) != NULL) if (rotate(strings) != NULL)
{ {
err = SMCParse_InvalidTokens; err = SMCError_InvalidTokens;
goto failed; goto failed;
} }
} }
} else { }
else
{
/* If we're eating a string and get whitespace, we need to restage. /* If we're eating a string and get whitespace, we need to restage.
* (Note that if we are quoted, this is being ignored) * (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 */ /* There's no string, so we must move this one down and eat up another */
strings[0].end = &parse_point[i]; strings[0].end = &parse_point[i];
rotate(strings); rotate(strings);
} else if (!strings[1].quoted) { }
err = SMCParse_InvalidTokens; else if (!strings[1].quoted)
{
err = SMCError_InvalidTokens;
goto failed; goto failed;
} }
} }
@ -603,7 +649,7 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
} }
/* Advance which token we're on */ /* Advance which token we're on */
curtok++; states.col++;
} }
if (line_begin != in_buf) if (line_begin != in_buf)
@ -637,8 +683,10 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
parse_point = &parse_point[read]; parse_point = &parse_point[read];
parse_point -= bytes; 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; goto failed;
} }
} }
@ -646,29 +694,31 @@ SMCParseError TextParsers::ParseStream_SMC(void *stream,
/* If we're done parsing and there are tokens left over... */ /* If we're done parsing and there are tokens left over... */
if (curlevel) if (curlevel)
{ {
err = SMCParse_InvalidSection5; err = SMCError_InvalidSection5;
goto failed; 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; goto failed;
} }
smc->ReadSMC_ParseEnd(false, false); smc->ReadSMC_ParseEnd(false, false);
if (pStates != NULL)
{
*pStates = states;
}
return SMCParse_Okay; return SMCError_Okay;
failed: failed:
if (line) if (pStates != NULL)
{ {
*line = curline; *pStates = states;
} }
smc->ReadSMC_ParseEnd(true, (err == SMCParse_Custom)); smc->ReadSMC_ParseEnd(true, (err == SMCError_Custom));
if (col)
{
*col = curtok;
}
return err; return err;
} }
@ -976,7 +1026,7 @@ event_failed:
return false; return false;
} }
const char *TextParsers::GetSMCErrorString(SMCParseError err) const char *TextParsers::GetSMCErrorString(SMCError err)
{ {
static const char *s_errors[] = static const char *s_errors[] =
{ {
@ -994,7 +1044,7 @@ const char *TextParsers::GetSMCErrorString(SMCParseError err)
"A property was declared outside of a section", "A property was declared outside of a section",
}; };
if (err < SMCParse_Okay || err > SMCParse_InvalidProperty1) if (err < SMCError_Okay || err > SMCError_InvalidProperty1)
{ {
return NULL; return NULL;
} }

View File

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

View File

@ -119,7 +119,7 @@ void CPhraseFile::ReparseFile()
return; return;
} }
SMCParseError err; SMCError err;
char path[PLATFORM_MAX_PATH]; char path[PLATFORM_MAX_PATH];
g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "translations/%s", m_File.c_str()); 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); const char *msg = textparsers->GetSMCErrorString(err);
if (!msg) 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] 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_CurPhrase = -1;
m_ParseState = PPS_None; m_ParseState = PPS_None;
m_CurLine = 0;
m_FileLogged = false; m_FileLogged = false;
m_LastPhraseString.clear(); m_LastPhraseString.clear();
} }
SMCParseResult CPhraseFile::ReadSMC_RawLine(const char *line, unsigned int curline) SMCResult CPhraseFile::ReadSMC_NewSection(const SMCStates *states, const char *name)
{
m_CurLine = curline;
return SMCParse_Continue;
}
SMCParseResult CPhraseFile::ReadSMC_NewSection(const char *name, bool opt_quotes)
{ {
bool recognized = false; bool recognized = false;
if (m_ParseState == PPS_None) if (m_ParseState == PPS_None)
@ -178,7 +170,9 @@ SMCParseResult CPhraseFile::ReadSMC_NewSection(const char *name, bool opt_quotes
m_ParseState = PPS_Phrases; m_ParseState = PPS_Phrases;
recognized = true; recognized = true;
} }
} else if (m_ParseState == PPS_Phrases) { }
else if (m_ParseState == PPS_Phrases)
{
m_ParseState = PPS_InPhrase; m_ParseState = PPS_InPhrase;
recognized = true; recognized = true;
@ -188,10 +182,12 @@ SMCParseResult CPhraseFile::ReadSMC_NewSection(const char *name, bool opt_quotes
/* Create the reverse lookup */ /* Create the reverse lookup */
if (!sm_trie_insert(m_pPhraseLookup, name, reinterpret_cast<void *>(m_CurPhrase))) 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? */ /* :IDEA: prevent memory waste by seeking backwards in memtable? */
m_CurPhrase = -1; m_CurPhrase = -1;
} else { }
else
{
/* Initialize new phrase */ /* Initialize new phrase */
trans_t *pTrans; trans_t *pTrans;
@ -211,25 +207,27 @@ SMCParseResult CPhraseFile::ReadSMC_NewSection(const char *name, bool opt_quotes
} }
m_LastPhraseString.assign(name); m_LastPhraseString.assign(name);
} }
} else if (m_ParseState == PPS_InPhrase) { }
else if (m_ParseState == PPS_InPhrase)
{
ParseError("Phrase sections may not have sub-sections"); ParseError("Phrase sections may not have sub-sections");
return SMCParse_HaltFail; return SMCResult_HaltFail;
} }
if (!recognized) 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 */ /* See if we are ignoring a phrase */
if (m_CurPhrase == -1) if (m_CurPhrase == -1)
{ {
return SMCParse_Continue; return SMCResult_Continue;
} }
phrase_t *pPhrase = (phrase_t *)m_pMemory->GetAddress(m_CurPhrase); 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) if (pPhrase->fmt_list != -1)
{ {
ParseWarning("Ignoring duplicated #format property on line %d", m_CurLine); ParseWarning("Ignoring duplicated #format property on line %d", states->line);
return SMCParse_Continue; return SMCResult_Continue;
} }
if (pPhrase->translations > 0) if (pPhrase->translations > 0)
{ {
ParseWarning("#format property should come before translations on line %d, ignoring", m_CurLine); ParseWarning("#format property should come before translations on line %d, ignoring", states->line);
return SMCParse_Continue; return SMCResult_Continue;
} }
/* Do an initial browsing for error checking and what not */ /* 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++; pPhrase->fmt_count++;
state = Parse_Index; state = Parse_Index;
} else if (*value == ',') { }
else if (*value == ',')
{
/* Do nothing */ /* Do nothing */
} else { }
else
{
unsigned int bytes = textparsers->GetUTF8CharBytes(value); unsigned int bytes = textparsers->GetUTF8CharBytes(value);
if (bytes != 1 || !isalpha(*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 == ':') if (*value == ':')
{ {
state = Parse_Format; state = Parse_Format;
if (value - last_value >= 15) 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; m_CurPhrase = -1;
return SMCParse_Continue; return SMCResult_Continue;
} }
} else { }
else
{
unsigned int bytes = textparsers->GetUTF8CharBytes(value); unsigned int bytes = textparsers->GetUTF8CharBytes(value);
if (bytes != 1 || !isdigit(*value)) if (bytes != 1 || !isdigit(*value))
{ {
ParseWarning("Token '%c' in #format property on line %d is not a digit, phrase will be ignored.", ParseWarning("Token '%c' in #format property on line %d is not a digit, phrase will be ignored.",
*value, *value,
m_CurLine); states->line);
m_CurPhrase = -1; m_CurPhrase = -1;
return SMCParse_Continue; return SMCResult_Continue;
} }
} }
} else if (state == Parse_Format) { }
else if (state == Parse_Format)
{
if (*value == '}') if (*value == '}')
{ {
state = Parse_None; state = Parse_None;
@ -311,9 +319,9 @@ SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value,
if (state != Parse_None) if (state != Parse_None)
{ {
/* Moose clam cow. */ /* 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; m_CurPhrase = -1;
return SMCParse_Continue; return SMCResult_Continue;
} }
value = old_value; value = old_value;
@ -347,41 +355,49 @@ SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value,
state = Parse_Index; state = Parse_Index;
idx_ptr = NULL; idx_ptr = NULL;
} }
} else if (state == Parse_Index) { }
else if (state == Parse_Index)
{
if (*in_ptr == ':') if (*in_ptr == ':')
{ {
/* Check the number! */ /* Check the number! */
if (!idx_ptr) 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; m_CurPhrase = -1;
return SMCParse_Continue; return SMCResult_Continue;
} }
long idx = strtol(idx_ptr, NULL, 10); long idx = strtol(idx_ptr, NULL, 10);
if (idx < 1 || idx > (long)pPhrase->fmt_count) 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; m_CurPhrase = -1;
return SMCParse_Continue; 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, m_CurLine); 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; m_CurPhrase = -1;
return SMCParse_Continue; return SMCResult_Continue;
} }
cur_idx = (unsigned int)idx; cur_idx = (unsigned int)idx;
state = Parse_Format; state = Parse_Format;
out_ptr = NULL; out_ptr = NULL;
} else if (!idx_ptr) { }
else if (!idx_ptr)
{
idx_ptr = in_ptr; idx_ptr = in_ptr;
} }
} else if (state == Parse_Format) { }
else if (state == Parse_Format)
{
if (*in_ptr == '}') if (*in_ptr == '}')
{ {
if (!out_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; m_CurPhrase = -1;
return SMCParse_Continue; return SMCResult_Continue;
} }
*out_ptr = '\0'; *out_ptr = '\0';
state = Parse_None; state = Parse_None;
@ -392,7 +408,9 @@ SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value,
pPhrase->fmt_bytes += strlen(fmt_buf); pPhrase->fmt_bytes += strlen(fmt_buf);
fmt_list = (int *)m_pMemory->GetAddress(pPhrase->fmt_list); fmt_list = (int *)m_pMemory->GetAddress(pPhrase->fmt_list);
fmt_list[cur_idx - 1] = tmp_idx; fmt_list[cur_idx - 1] = tmp_idx;
} else { }
else
{
if (!out_ptr) if (!out_ptr)
{ {
out_ptr = fmt_buf; 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) 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.", ParseWarning("Format property contains format string that exceeds maximum length on line %d, phrase will be ignored.",
m_CurLine); states->line);
m_CurPhrase = -1; m_CurPhrase = -1;
return SMCParse_Continue; return SMCResult_Continue;
} }
*out_ptr++ = *in_ptr; *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.", ParseWarning("Format property contains no string for index %d on line %d, phrase will be ignored.",
i + 1, i + 1,
m_CurLine); states->line);
m_CurPhrase = -1; m_CurPhrase = -1;
return SMCParse_Continue; return SMCResult_Continue;
} }
} }
} else { }
else
{
size_t len = strlen(key); size_t len = strlen(key);
if (len != 2) if (len != 2)
{ {
ParseWarning("Ignoring translation to invalid language \"%s\" on line %d.", key, m_CurLine); ParseWarning("Ignoring translation to invalid language \"%s\" on line %d.", key, states->line);
return SMCParse_Continue; return SMCResult_Continue;
} }
unsigned int lang; 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. /* Ignore if we don't have a language.
* :IDEA: issue a one-time alert? * :IDEA: issue a one-time alert?
*/ */
return SMCParse_Continue; return SMCResult_Continue;
} }
/* See how many bytes we need for this string, then allocate. /* 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 */ /* Skip past the last byte read */
in_ptr++; 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 */ /* Search for parameters if this is a formatted string */
const char *scrap_in_point = in_ptr; const char *scrap_in_point = in_ptr;
const char *digit_start = ++in_ptr; const char *digit_start = ++in_ptr;
@ -570,10 +592,10 @@ cont_loop:
pPhrase->translations++; pPhrase->translations++;
} }
return SMCParse_Continue; return SMCResult_Continue;
} }
SMCParseResult CPhraseFile::ReadSMC_LeavingSection() SMCResult CPhraseFile::ReadSMC_LeavingSection(const SMCStates *states)
{ {
if (m_ParseState == PPS_InPhrase) if (m_ParseState == PPS_InPhrase)
{ {
@ -584,11 +606,13 @@ SMCParseResult CPhraseFile::ReadSMC_LeavingSection()
m_CurPhrase = -1; m_CurPhrase = -1;
m_ParseState = PPS_Phrases; m_ParseState = PPS_Phrases;
m_LastPhraseString.assign(""); m_LastPhraseString.assign("");
} else if (m_ParseState == PPS_Phrases) { }
else if (m_ParseState == PPS_Phrases)
{
m_ParseState = PPS_None; m_ParseState = PPS_None;
} }
return SMCParse_Continue; return SMCResult_Continue;
} }
void CPhraseFile::ReadSMC_ParseEnd(bool halted, bool failed) 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; return Trans_BadPhraseLanguage;
} }
pTrans->fmt_order = (int *)m_pMemory->GetAddress(trans->fmt_order);
pTrans->fmt_count = pPhrase->fmt_count; 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); pTrans->szPhrase = m_pStringTab->GetString(trans->stridx);
return Trans_Okay; return Trans_Okay;
@ -811,9 +834,9 @@ void Translator::RebuildLanguageDatabase(const char *lang_header_file)
m_Languages.clear(); m_Languages.clear();
/* Start anew */ /* Start anew */
SMCParseError err; SMCError err;
unsigned int line=0, col=0; SMCStates states;
if ((err=textparsers->ParseFile_SMC(lang_header_file, this, &line, &col)) != SMCParse_Okay) if ((err=textparsers->ParseFile_SMC(lang_header_file, this, &states)) != SMCError_Okay)
{ {
const char *str_err = textparsers->GetSMCErrorString(err); const char *str_err = textparsers->GetSMCErrorString(err);
if (!str_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] 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; void *serverLang;
@ -854,7 +877,7 @@ void Translator::ReadSMC_ParseStart()
m_CustomError.clear(); 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) 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); 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; 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); size_t len = strlen(key);
@ -891,7 +914,7 @@ SMCParseResult Translator::ReadSMC_KeyValue(const char *key, const char *value,
AddLanguage(key, value); AddLanguage(key, value);
return SMCParse_Continue; return SMCResult_Continue;
} }
bool Translator::AddLanguage(const char *langcode, const char *description) 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); TransError GetTranslation(const char *szPhrase, unsigned int lang_id, Translation *pTrans);
public: //ITextListener_SMC public: //ITextListener_SMC
void ReadSMC_ParseStart(); 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); 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: private:
void ParseError(const char *message, ...); void ParseError(const char *message, ...);
void ParseWarning(const char *message, ...); void ParseWarning(const char *message, ...);
@ -108,7 +107,6 @@ private:
unsigned int m_LangCount; unsigned int m_LangCount;
String m_ParseError; String m_ParseError;
String m_LastPhraseString; String m_LastPhraseString;
unsigned int m_CurLine;
bool m_FileLogged; bool m_FileLogged;
}; };
@ -129,9 +127,9 @@ public: // SMGlobalClass
void OnSourceModLevelChange(const char *mapName); void OnSourceModLevelChange(const char *mapName);
public: // ITextListener_SMC public: // ITextListener_SMC
void ReadSMC_ParseStart(); void ReadSMC_ParseStart();
SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes); SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
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);
SMCParseResult ReadSMC_LeavingSection(); SMCResult ReadSMC_LeavingSection(const SMCStates *states);
public: public:
void RebuildLanguageDatabase(const char *lang_header_file); void RebuildLanguageDatabase(const char *lang_header_file);
unsigned int FindOrAddPhraseFile(const char *phrase_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) if (new_section)
{ {
new_section->PushCell(handle); new_section->PushCell(handle);
new_section->PushString(name); new_section->PushString(name);
new_section->PushCell(opt_quotes ? 1 : 0); new_section->PushCell(1);
new_section->Execute(&result); 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) if (key_value)
{ {
key_value->PushCell(handle); key_value->PushCell(handle);
key_value->PushString(key); key_value->PushString(key);
key_value->PushString(value); key_value->PushString(value);
key_value->PushCell(key_quotes ? 1 : 0); key_value->PushCell(1);
key_value->PushCell(value_quotes ? 1 : 0); key_value->PushCell(1);
key_value->Execute(&result); 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) if (end_section)
{ {
@ -113,22 +113,22 @@ public:
end_section->Execute(&result); 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) if (raw_line)
{ {
raw_line->PushCell(handle); raw_line->PushCell(handle);
raw_line->PushString(line); raw_line->PushString(line);
raw_line->PushCell(curline); raw_line->PushCell(states->line);
raw_line->Execute(&result); raw_line->Execute(&result);
} }
return (SMCParseResult)result; return (SMCResult)result;
} }
public: public:
IPluginFunction *parse_start; IPluginFunction *parse_start;
@ -279,22 +279,22 @@ static cell_t SMC_ParseFile(IPluginContext *pContext, const cell_t *params)
char path[PLATFORM_MAX_PATH]; char path[PLATFORM_MAX_PATH];
g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "%s", file); g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "%s", file);
unsigned int line = 0, col = 0; SMCStates states;
SMCParseError p_err = textparsers->ParseFile_SMC(path, parse, &line, &col); SMCError p_err = textparsers->ParseFile_SMC(path, parse, &states);
cell_t *c_line, *c_col; cell_t *c_line, *c_col;
pContext->LocalToPhysAddr(params[3], &c_line); pContext->LocalToPhysAddr(params[3], &c_line);
pContext->LocalToPhysAddr(params[4], &c_col); pContext->LocalToPhysAddr(params[4], &c_col);
*c_line = line; *c_line = states.line;
*c_col = col; *c_col = states.col;
return (cell_t)p_err; return (cell_t)p_err;
} }
static cell_t SMC_GetErrorString(IPluginContext *pContext, const cell_t *params) 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) if (!str)
{ {

View File

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

View File

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

View File

@ -819,10 +819,10 @@ void CPluginManager::Shutdown()
void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir) void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
{ {
/* First read in the database of plugin settings */ /* First read in the database of plugin settings */
SMCParseError err; SMCError err;
unsigned int line, col; SMCStates states;
m_AllPluginsLoaded = false; 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); g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
const char *err_msg = textparsers->GetSMCErrorString(err); 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; len = bytes - 1;
} }
memcpy(dest, source, len); memmove(dest, source, len);
dest[len] = '\0'; dest[len] = '\0';
return SP_ERROR_NONE; return SP_ERROR_NONE;
@ -880,7 +880,7 @@ int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const cha
needtocheck = true; needtocheck = true;
} }
memcpy(dest, source, len); memmove(dest, source, len);
if ((dest[len-1] & 1<<7) && needtocheck) if ((dest[len-1] & 1<<7) && needtocheck)
{ {
len -= __CheckValidChar(dest+len-1); 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) bool TopMenu::LoadConfiguration(const char *file, char *error, size_t maxlength)
{ {
SMCParseError err; SMCError err;
unsigned int line = 0, col = 0; SMCStates states;
if ((err = textparsers->ParseFile_SMC(file, this, &line, &col)) if ((err = textparsers->ParseFile_SMC(file, this, &states))
!= SMCParse_Okay) != SMCError_Okay)
{ {
const char *err_string = textparsers->GetSMCErrorString(err); const char *err_string = textparsers->GetSMCErrorString(err);
if (!err_string) if (!err_string)
@ -971,7 +971,7 @@ void TopMenu::ReadSMC_ParseStart()
m_Config.cats.clear(); 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) 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 if (ignore_parse_level > 0
|| current_parse_state != PARSE_STATE_CATEGORY || current_parse_state != PARSE_STATE_CATEGORY
|| cur_cat == NULL) || cur_cat == NULL)
{ {
return SMCParse_Continue; return SMCResult_Continue;
} }
if (strcmp(key, "item") == 0) 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)); 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) 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) unsigned int TopMenu::FindCategory(const char *name)

View File

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

View File

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