Expose ParseFile_INI to API.

Some minor changes have been made in parser:

- Added ReadINI_ParseStart and ReadINI_ParseEnd call.
- Function returns SMCResult instead of bool. To avoid unecessary complexity and duplicating natives, this feels more appropriate to let these functions to share some SMCResult and SMCParse constants. Since properly documented, this should be ok.
- Made sure curtok is set to 0 when ptr_val is null, otherwise unexpected value would be passed into the forward.
This commit is contained in:
Arkshine 2014-07-23 11:56:35 +02:00
parent 9af7931a79
commit 0cf5a2e12f
12 changed files with 776 additions and 285 deletions

View File

@ -778,7 +778,7 @@ failed:
* INI parser
*/
bool TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listener, unsigned int *line, unsigned int *col)
SMCError TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listener, unsigned int *line, unsigned int *col)
{
FILE *fp = fopen(file, "rt");
unsigned int curline = 0;
@ -792,12 +792,16 @@ bool TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listene
*line = 0;
}
return false;
return SMCError_StreamOpen;
}
ini_listener->ReadINI_ParseStart();
char buffer[2048];
char *ptr, *save_ptr;
bool in_quote;
SMCError err = SMCError_Okay;
SMCResult res = SMCResult_Continue;
while (!feof(fp))
{
@ -897,8 +901,9 @@ bool TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listene
continue;
}
if (!ini_listener->ReadINI_RawLine(ptr, &curtok))
if ((res = ini_listener->ReadINI_RawLine(ptr, curline, &curtok)) != SMCResult_Continue)
{
err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
goto event_failed;
}
@ -951,8 +956,9 @@ bool TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listene
}
/* Tell the handler */
if (!ini_listener->ReadINI_NewSection(&ptr[1], invalid_tokens, got_bracket, extra_tokens, &curtok))
if ((res = ini_listener->ReadINI_NewSection(&ptr[1], invalid_tokens, got_bracket, extra_tokens, &curtok)) != SMCResult_Continue)
{
err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
goto event_failed;
}
}
@ -1052,9 +1058,14 @@ bool TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listene
}
skip_value:
/* We're done! */
curtok = val_ptr - buffer;
if (!ini_listener->ReadINI_KeyValue(key_ptr, val_ptr, invalid_tokens, equal_token, quotes, &curtok))
if (val_ptr)
curtok = val_ptr - buffer;
else
curtok = 0;
if ((res = ini_listener->ReadINI_KeyValue(key_ptr, val_ptr, invalid_tokens, equal_token, quotes, &curtok)) != SMCResult_Continue)
{
err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
curtok = 0;
goto event_failed;
}
@ -1066,9 +1077,16 @@ bool TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listene
*line = curline;
}
if (col)
{
*col = curtok;
}
fclose(fp);
return true;
ini_listener->ReadINI_ParseEnd(false, false);
return SMCError_Okay;
event_failed:
if (line)
@ -1083,7 +1101,9 @@ event_failed:
fclose(fp);
return false;
ini_listener->ReadINI_ParseEnd(true, (err == SMCError_Custom));
return err;
}
const char *TextParsers::GetSMCErrorString(SMCError err)

View File

@ -51,7 +51,7 @@ class TextParsers : public ITextParsers
public:
TextParsers();
public:
bool ParseFile_INI(const char *file,
SMCError ParseFile_INI(const char *file,
ITextListener_INI *ini_listener,
unsigned int *line,
unsigned int *col);

View File

@ -392,6 +392,7 @@
<ClInclude Include="..\sh_stack.h" />
<ClInclude Include="..\sh_tinyhash.h" />
<ClInclude Include="..\svn_version.h" />
<ClInclude Include="..\textparse.h" />
<ClInclude Include="..\trie_natives.h" />
<ClInclude Include="..\zlib\zconf.h" />
<ClInclude Include="..\zlib\zlib.h" />

View File

@ -323,18 +323,12 @@
<ClInclude Include="..\CDataPack.h">
<Filter>Header Files</Filter>
</ClInclude>
<<<<<<< HEAD
=======
<ClInclude Include="..\sm_stringhashmap.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\sm_memtable.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\CTextParsers.h">
<Filter>Header Files</Filter>
</ClInclude>
>>>>>>> Introduce TextParser API.
</ClInclude>
<ClInclude Include="..\textparse.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\version.rc">

View File

@ -37,7 +37,12 @@ TextParserHandles<ParseInfo> g_TextParsersHandles;
static cell AMX_NATIVE_CALL SMC_CreateParser(AMX *amx, cell *params)
{
return static_cast<cell>(g_TextParsersHandles.create());
int handle = g_TextParsersHandles.create();
ParseInfo *p = g_TextParsersHandles.lookup(handle);
p->ini_format = params[1] > 0 ? true : false;
return static_cast<cell>(handle);
}
static cell AMX_NATIVE_CALL SMC_SetParseStart(AMX *amx, cell *params)
@ -77,7 +82,7 @@ static cell AMX_NATIVE_CALL SMC_SetParseEnd(AMX *amx, cell *params)
int length;
const char* funcName = get_amxstring(amx, params[2], 0, length);
int func = registerSPForwardByName(amx, funcName, FP_CELL, FP_DONE);
int func = registerSPForwardByName(amx, funcName, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
if (func == -1)
{
@ -101,34 +106,50 @@ static cell AMX_NATIVE_CALL SMC_SetReaders(AMX *amx, cell *params)
}
int length;
const char* NewSectionFuncName = get_amxstring(amx, params[2], 0, length);
const char* KeyValueFuncName = get_amxstring(amx, params[3], 1, length);
const char* EndSectionFuncName = get_amxstring(amx, params[4], 2, length);
const char* newSectionFuncName = get_amxstring(amx, params[2], 0, length);
const char* keyValueFuncName = get_amxstring(amx, params[3], 1, length);
const char* endSectionFuncName = get_amxstring(amx, params[4], 2, length);
int NewSectionFunc = registerSPForwardByName(amx, NewSectionFuncName, FP_CELL, FP_STRING, FP_DONE);
if (NewSectionFunc == -1)
int newSectionFunc;
int keyValueFunc;
int endSectionFunc;
if (p->ini_format)
newSectionFunc = registerSPForwardByName(amx, newSectionFuncName, FP_CELL, FP_STRING, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
else
newSectionFunc = registerSPForwardByName(amx, newSectionFuncName, FP_CELL, FP_STRING, FP_DONE);
if (newSectionFunc == -1)
{
LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", NewSectionFuncName, g_plugins.findPluginFast(amx)->getName());
LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", newSectionFuncName, g_plugins.findPluginFast(amx)->getName());
return 0;
}
int KeyValueFunc = registerSPForwardByName(amx, KeyValueFuncName, FP_CELL, FP_STRING, FP_STRING, FP_DONE);
if (KeyValueFunc == -1)
if (p->ini_format)
keyValueFunc = registerSPForwardByName(amx, keyValueFuncName, FP_CELL, FP_STRING, FP_STRING, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
else
keyValueFunc = registerSPForwardByName(amx, keyValueFuncName, FP_CELL, FP_STRING, FP_STRING, FP_DONE);
if (keyValueFunc == -1)
{
LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", KeyValueFuncName, g_plugins.findPluginFast(amx)->getName());
LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", keyValueFuncName, g_plugins.findPluginFast(amx)->getName());
return 0;
}
int EndSectionFunc = registerSPForwardByName(amx, EndSectionFuncName, FP_CELL, FP_DONE);
if (EndSectionFunc == -1)
if (!p->ini_format)
{
LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", EndSectionFuncName, g_plugins.findPluginFast(amx)->getName());
return 0;
endSectionFunc = registerSPForwardByName(amx, endSectionFuncName, FP_CELL, FP_DONE);
if (endSectionFunc == -1)
{
LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", endSectionFuncName, g_plugins.findPluginFast(amx)->getName());
return 0;
}
p->end_section = endSectionFunc;
}
p->new_section = NewSectionFunc;
p->key_value = KeyValueFunc;
p->end_section = EndSectionFunc;
p->new_section = newSectionFunc;
p->key_value = keyValueFunc;
return 1;
}
@ -146,7 +167,12 @@ static cell AMX_NATIVE_CALL SMC_SetRawLine(AMX *amx, cell *params)
int length;
const char* funcName = get_amxstring(amx, params[2], 0, length);
int func = registerSPForwardByName(amx, funcName, FP_CELL, FP_STRING, FP_CELL, FP_DONE);
int func;
if (p->ini_format)
func = registerSPForwardByName(amx, funcName, FP_CELL, FP_STRING, FP_CELL, FP_CELL, FP_DONE);
else
func = registerSPForwardByName(amx, funcName, FP_CELL, FP_STRING, FP_CELL, FP_DONE);
if (func == -1)
{
LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", funcName, g_plugins.findPluginFast(amx)->getName());
@ -171,13 +197,26 @@ static cell AMX_NATIVE_CALL SMC_ParseFile(AMX *amx, cell *params)
int length;
const char *file = build_pathname("%s", get_amxstring(amx, params[2], 0, length));
SMCStates states;
SMCError p_err = textparsers->ParseFile_SMC(file, p, &states);
SMCError p_err;
*get_amxaddr(amx, params[3]) = states.line;
*get_amxaddr(amx, params[4]) = states.col;
if (p->ini_format)
{
size_t line, col;
p_err = textparsers->ParseFile_INI(file, p, &line, &col);
return (cell)p_err;
*get_amxaddr(amx, params[3]) = line;
*get_amxaddr(amx, params[4]) = col;
}
else
{
SMCStates states;
p_err = textparsers->ParseFile_SMC(file, p, &states);
*get_amxaddr(amx, params[3]) = states.line;
*get_amxaddr(amx, params[4]) = states.col;
}
return static_cast<cell>(p_err);
}
static cell AMX_NATIVE_CALL SMC_GetErrorString(AMX *amx, cell *params)

View File

@ -35,7 +35,9 @@
#include "amxmodx.h"
#include "CTextParsers.h"
class ParseInfo : public ITextListener_SMC
class ParseInfo :
public ITextListener_SMC,
public ITextListener_INI
{
public:
ParseInfo()
@ -47,31 +49,43 @@ public:
end_section = -1;
raw_line = -1;
handle = -1;
ini_format = false;
}
~ParseInfo() {}
public:
void ReadSMC_ParseStart()
{
if (parse_start != -1)
{
if (parse_start != -1)
executeForwards(parse_start, handle);
}
void ReadINI_ParseStart()
{
if (parse_start != -1)
executeForwards(parse_start, handle);
}
}
void ReadSMC_ParseEnd(bool halted, bool failed)
{
if (parse_end != -1)
{
if (parse_end != -1)
executeForwards(parse_end, handle, halted ? 1 : 0, failed ? 1 : 0);
}
void ReadINI_ParseEnd(bool halted, bool failed)
{
if (parse_end != -1)
executeForwards(parse_end, handle, halted ? 1 : 0, failed ? 1 : 0);
}
}
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
{
if (new_section != -1)
{
return (SMCResult)executeForwards(new_section, handle, name);
}
return SMCResult_Continue;
}
SMCResult ReadINI_NewSection(const char *section, bool invalid_tokens, bool close_bracket, bool extra_tokens, unsigned int *curtok)
{
if (new_section != -1)
return (SMCResult)executeForwards(new_section, handle, section, invalid_tokens, close_bracket, extra_tokens, *curtok);
return SMCResult_Continue;
}
@ -79,9 +93,14 @@ public:
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
if (key_value != -1)
{
return (SMCResult)executeForwards(key_value, handle, key, value);
}
return SMCResult_Continue;
}
SMCResult ReadINI_KeyValue(const char *key, const char *value, bool invalid_tokens, bool equal_token, bool quotes, unsigned int *curtok)
{
if (key_value != -1)
return (SMCResult)executeForwards(key_value, handle, key, value, invalid_tokens, equal_token, quotes, *curtok);
return SMCResult_Continue;
}
@ -89,9 +108,7 @@ public:
SMCResult ReadSMC_LeavingSection(const SMCStates *states)
{
if (end_section != -1)
{
return (SMCResult)executeForwards(end_section, handle, handle);
}
return (SMCResult)executeForwards(end_section, handle);
return SMCResult_Continue;
}
@ -99,9 +116,14 @@ public:
SMCResult ReadSMC_RawLine(const SMCStates *states, const char *line)
{
if (raw_line != -1)
{
return (SMCResult)executeForwards(raw_line, handle, line, states->line);
}
return SMCResult_Continue;
}
SMCResult ReadINI_RawLine(const char *line, unsigned int lineno, unsigned int *curtok)
{
if (raw_line != -1)
return (SMCResult)executeForwards(raw_line, handle, line, lineno, *curtok);
return SMCResult_Continue;
}
@ -113,6 +135,7 @@ public:
int end_section;
int raw_line;
int handle;
bool ini_format;
};
template <typename T>

View File

@ -1,46 +1,98 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This file is part of the SourceMod/SourcePawn SDK.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#if defined _textparse_included
#endinput
#endif
#define _textparse_included
/**
* This parser API is entirely event based. You must hook events to receive data.
* The file format can ben either INI or SMC (also known as "SourceMod Configuration".
* SMC format is nearly identical to VDF (also known as "Valve's KeyValues format").
* Also please note INI format is handled differently. Because more "simple" to parse, some
* event doesn't exist and callback prototype can be different.
*/
/**
* Everything below describes the SMC Parse, or "SourceMod Configuration" format.
* This parser is entirely event based. You must hook events to receive data.
* The file format itself is nearly identical to Valve's KeyValues format.
* The INI file format is defined as:
* WHITESPACE: 0x20, \n, \t, \r
* IDENTIFIER: A-Z a-z 0-9 _ - , + . $ ? /
* STRING : Any set of symbols
*
* Basic syntax is comprised of SECTIONs.
* A SECTION is defined as:
* [SECTIONNAME]
* OPTION
* OPTION
* OPTION...
*
* SECTIONNAME is an IDENTIFIER.
* OPTION can be repeated any number of times, once per line.
* OPTION is defined as one of:
* KEY = "VALUE"
* KEY = VALUE
* KEY
* Where KEY is an IDENTIFIER and VALUE is a STRING.
*
* WHITESPACE should always be omitted.
* COMMENTS should be stripped, and are defined as text occurring in:
* ;<TEXT>
*
* Example file below. Note that the second line is technically invalid.
* The event handler must decide whether this should be allowed.
* --FILE BELOW--
* [gaben]
* hi = clams
* bye = "NO CLAMS"
*
* [valve]
* cannot
* maintain
* products
*/
/**
* The SMC file format is defined as:
* WHITESPACE: 0x20, \n, \t, \r
* IDENTIFIER: Any ASCII character EXCLUDING ", {, }, ;, //, / *, or WHITESPACE.
* STRING : Any set of symbols enclosed in quotes.
*
* Note: if a STRING does not have quotes, it is parsed as an IDENTIFIER.
*
* Basic syntax is comprised of SECTIONBLOCKs.
* A SECTIONBLOCK defined as:
*
* SECTIONNAME
* {
* OPTION
* }
*
* OPTION can be repeated any number of times inside a SECTIONBLOCK.
* A new line will terminate an OPTION, but there can be more than one OPTION per line.
* OPTION is defined any of:
* "KEY" "VALUE"
* SECTIONBLOCK
*
* SECTIONNAME, KEY, VALUE, and SINGLEKEY are strings
* SECTIONNAME cannot have trailing characters if quoted, but the quotes can be optionally removed.
* If SECTIONNAME is not enclosed in quotes, the entire sectionname string is used (minus surrounding whitespace).
* If KEY is not enclosed in quotes, the key is terminated at first whitespace.
* If VALUE is not properly enclosed in quotes, the entire value string is used (minus surrounding whitespace).
* The VALUE may have inner quotes, but the key string may not.
*
* For an example, see scripting/testsuite/textparse_test.cfg
*
* WHITESPACE should be ignored.
* Comments are text occurring inside the following tokens, and should be stripped
* unless they are inside literal strings:
* ;<TEXT>
* //<TEXT>
* / *<TEXT> * /
*/
enum TextParser
{
Invalid_TextParser = 0
};
/**
* Parse result directive.
@ -71,31 +123,183 @@ enum SMCError
SMCError_InvalidProperty1, /**< A property was declared outside of any section */
};
enum TextParser
{
Invalid_TextPaser = 0
};
/**
* Creates a new SMC file format parser.
* Creates a new text parser.
* This is used to set parse hooks.
*
* @return A new Handle to an SMC Parse structure.
* @param ini_format Sets whether INI format or SMC should be used.
*
* @return A new handle to an Text Parse structure.
*/
native TextParser:SMC_CreateParser();
native TextParser:SMC_CreateParser(bool:ini_format = false);
/**
* Parses an SMC file.
* Disposes of a text parser.
*
* @param smc A Handle to an SMC Parse structure.
* @param handle Handle to a Text Parse.
* @return True if disposed, false otherwise.
*/
native SMC_DestroyParser(&TextParser:handle);
/**
* Parses a config file.
*
* @note If using INI format, you can expect only the following SMCError_* constants:
* SMCError_Okay, SMCError_StreamOpen and SMCError_Custom.
*
* @param handle A handle to an Text Parse structure.
* @param file A string containing the file path.
* @param line An optional by reference cell to store the last line number read.
* @param col An optional by reference cell to store the last column number read.
* @return An SMCParseError result.
* @error Invalid or corrupt Handle.
* @return A SMCParseError result.
* @error Invalid or corrupt handle.
*/
native SMCError:SMC_ParseFile(TextParser:smc, const file[], &line = 0, &col = 0);
native SMCError:SMC_ParseFile(TextParser:handle, const file[], &line = 0, &col = 0);
/**
* Sets the SMC_ParseStart function of a parse handle.
* Below is the prototype of callback.:
*
* - ParseStart:
* Called when parsing is started.
*
* @param handle The Text Parse handle.
* @noreturn
*
* public SMC_ParseStart(TextParser:handle)
*
* @param handle Handle to a Text Parse.
* @param func A ParseStart callback.
*
* @noreturn
* @error Invalid or corrupt handle.
*/
native SMC_SetParseStart(TextParser:handle, const func[]);
/**
* Sets the SMC_ParseEnd of a parse handle.
* Below is the prototype of callback.:
*
* - ParseEnd:
* Called when parsing is halted.
*
* @param handle The Text Parse handle.
* @param halted True if abnormally halted, false otherwise.
* @param failed True if parsing failed, false otherwise.
* If using INI format, this is only true when
* a callback returns SMCResult_HaltFail.
* @noreturn
*
* public SMC_ParseEnd(TextParser:handle, bool:halted, bool:failed)
*
* @param handle Handle to a Text Parse.
* @param func A ParseEnd callback..
*
* @noreturn
* @error Invalid or corrupt handle.
*/
native SMC_SetParseEnd(TextParser:handle, const func[]);
/**
* Sets the three main reader functions.
* Below are the different prototypes of callbacks:
*
* - NewSection:
* Called when the parser finds the end of the current section.
*
* INI:
* @param section Name of section in between the [ and ] characters.
* @param invalid_tokens True if invalid tokens were detected in the name.
* @param close_bracket True if a closing bracket was detected, false otherwise.
* @param extra_tokens True if extra tokens were detected on the line.
* @param curtok Contains current token in the line where the section name starts.
* can add to this offset when failing to point to a token.
* @return An SMCResult action to take.
*
* public SMCResult:SMC_NewSection(TextParser:handle, const section[], bool:invalid_tokens, bool:close_bracket, bool:extra_tokens, curtok)
*
* SMC:
* @note Enclosing quotes are always stripped.
* @param handle The Text Parse handle.
* @param name String containing section name.
* @return An SMCResult action to take.
*
* public SMCResult:SMC_NewSection(TextParser:handle, const name[])
*
* - KeyValue:
* Called when the parser finds a new key/value pair.
*
* INI:
* @param handle The Text Parse handle.
* @param key Name of key.
* @param value String containing value (with quotes stripped, if any).
* @param invalid_tokens Whether or not the key contained invalid tokens.
* @param equal_token There was an '=' sign present (in case the value is missing).
* @param quotes Whether value was enclosed in quotes.
* @param curtok Contains the token index of the start of the value string.
* This can be changed when returning false.
* @return SMCResult directive.
*
* public SMCResult:SMC_KeyValue(TextParser:handle, const key[], const value[], bool:invalid_tokens, bool:equal_token, bool:quotes, curtok)
*
* SMC:
* @note Enclosing quotes are always stripped.
*
* @param handle The Text Parse handle.
* @param key String containing key name.
* @param value String containing value name.
* @return A SMCResult action to take.
*
* public SMCResult:SMC_KeyValue(TextParser:handle, const key[], const value[])
*
* - EndSection:
* Called when the parser finds the end of the current section.
* Only with SMC format.
* @param handle The Text Parse handle.
*
* public SMCResult:SMC_EndSection(TextParser:handle)
* -
* @param handle The Text parse handle.
* @param ns A NewSection callback.
* @param kv A KeyValue callback.
* @param es An EndSection callback. Only available for SMC config format.
*
* @noreturn
*/
native SMC_SetReaders(TextParser:smc, const nsFunc[], const kvFunc[], const esFunc[] = "");
/**
* Sets a raw line reader on an text parser handle.
* Below is the prototype of callback.:
*
* - RawLine:
* Called whenever a raw line is read.
*
* INI:
* @param handle The Text Parse handle.
* @param line Contents of line.
* @param lineno The line number it occurs on.
* @param curtok Pointer to optionally store failed position in string.
* @return SMCResult directive.
*
* public SMCResult:SMC_RawLine(TextParser:smc, const line[], lineno, curtok)
*
* SMC:
* @param handle The Text Parse handle.
* @param line A string containing the raw line from the file.
* @param lineno The line number it occurs on.
* @return An SMCResult action to take.
*
* public SMCResult:SMC_RawLine(TextParser:handle, const line[], lineno)
*
* @param handle Handle to an Text Parse.
* @param func A RawLine callback.
* @noreturn
*/
native SMC_SetRawLine(TextParser:handle, const func[]);
/**
* Gets an error string for an SMCError code.
@ -109,115 +313,4 @@ native SMCError:SMC_ParseFile(TextParser:smc, const file[], &line = 0, &col = 0)
*
* @return True on success, false otherwise.
*/
native bool:SMC_GetErrorString(SMCError:error, buffer[], buf_max);
/**
* Sets the SMC_ParseStart function of a parse Handle.
* Below is the prototype of callback.:
*
* - ParseStart:
* Called when parsing is started.
*
* @param smc The SMC Parse Handle.
* @noreturn
*
* public SMC_ParseStart(TextParser:smc)
*
* @param smc Handle to an SMC Parse.
* @param func A ParseStart callback.
*
* @noreturn
* @error Invalid or corrupt Handle.
*/
native SMC_SetParseStart(TextParser:smc, const func[]);
/**
* Sets the SMC_ParseEnd of a parse handle.
* Below is the prototype of callback.:
*
* - ParseEnd:
* Called when parsing is halted.
*
* @param smc The SMC Parse Handle.
* @param halted True if abnormally halted, false otherwise.
* @param failed True if parsing failed, false otherwise.
* @noreturn
*
* public SMC_ParseEnd(TextParser:smc, bool:halted, bool:failed)
*
* @param smc Handle to an SMC Parse.
* @param func A ParseEnd callback..
*
* @noreturn
* @error Invalid or corrupt Handle.
*/
native SMC_SetParseEnd(TextParser:smc, const func[]);
/**
* Sets the three main reader functions.
* Below are the different prototypes of callbacks:
*
* - NewSection:
* Called when the parser finds the end of the current section.
* @note Enclosing quotes are always stripped.
*
* @param smc The SMC Parse Handle.
* @param name String containing section name.
* @return An SMCResult action to take.
*
* public SMCResult:SMC_NewSection(TextParser:smc, const name[])
*
* - KeyValue:
* Called when the parser finds a new key/value pair.
* @note Enclosing quotes are always stripped.
*
* @param smc The SMC Parse Handle.
* @param key String containing key name.
* @param value String containing value name.
* @return An SMCResult action to take.
*
* public SMCResult:SMC_KeyValue(TextParser:smc, const key[], const value[])
*
* - EndSection:
* Called when the parser finds the end of the current section.
*
* @param smc The SMC Parse Handle.
*
* public SMCResult:SMC_EndSection(TextParser:smc)
* -
* @param smc An SMC parse Handle.
* @param ns A NewSection callback.
* @param kv A KeyValue callback.
* @param es An EndSection callback.
*
* @noreturn
*/
native SMC_SetReaders(TextParser:smc, const nsFunc[], const kvFunc[], const esFunc[]);
/**
* Sets a raw line reader on an SMC parser Handle.
* Below is the prototype of callback.:
*
* - RawLine:
* Called whenever a raw line is read.
*
* @param smc The SMC Parse Handle.
* @param line A string containing the raw line from the file.
* @param lineno The line number it occurs on.
* @return An SMCResult action to take.
*
* public SMCResult:SMC_RawLine(TextParser:smc, const line[], lineno)
*
* @param smc Handle to an SMC Parse.
* @param func A RawLine callback..
* @noreturn
*/
native SMC_SetRawLine(TextParser:smc, const func[]);
/**
* Disposes of a text parser.
*
* @param smc Handle to an SMC Parse.
* @return True if disposed, false otherwise.
*/
native SMC_DestroyParser(&TextParser:smc);
native bool:SMC_GetErrorString(SMCError:error, buffer[], buf_max);

View File

@ -0,0 +1,135 @@
/**
* This file is used to set various options that are important to SourceMod's core.
* If this file is missing or an option in this file is missing, then the default values will be used.
*/
"Core"
{
/**
* This option determines if SourceMod logging is enabled.
*
* "on" - Logging is enabled (default)
* "off" - Logging is disabled
*/
"Logging" "on"
/**
* This option determines how SourceMod logging should be handled.
*
* "daily" - New log file is created for each day (default)
* "map" - New log file is created for each map change
* "game" - Use game's log files
*/
"LogMode" "daily"
/**
* Language that multilingual enabled plugins and extensions will use to print messages.
* Only languages listed in languages.cfg are valid.
*
* The default value is "en"
*/
"ServerLang" "en"
/**
* String to use as the public chat trigger. Set an empty string to disable.
*/
"PublicChatTrigger" "!"
/**
* String to use as the silent chat trigger. Set an empty string to disable.
*/
"SilentChatTrigger" "/"
/**
* If a say command is a silent chat trigger, and is used by an admin,
* but it does not evaluate to an actual command, it will be displayed
* publicly. This setting allows you to suppress accidental typings.
*
* The default value is "no". A value of "yes" will supress.
*/
"SilentFailSuppress" "no"
/**
* Password setinfo key that clients must set. You must change this in order for
* passwords to work, for security reasons.
*/
"PassInfoVar" "_password"
/**
* Specifies the sound that gets played when an item is selected from a menu.
*/
"MenuItemSound" "buttons/button14.wav"
/**
* Specifies the sound that gets played when an "Exit" button is selected
* from a menu.
*/
"MenuExitSound" "buttons/combine_button7.wav"
/**
* Specifies the sound that gets played when an "Exit Back" button is selected
* from a menu. This is the special "Back" button that is intended to roll back
* to a previous menu.
*/
"MenuExitBackSound" "buttons/combine_button7.wav"
/**
* Enables or disables whether SourceMod reads a client's cl_language cvar to set
* their language for server-side phrase translation.
*
* "on" - Translate using the client's language (default)
* "off" - Translate using default server's language
*/
"AllowClLanguageVar" "On"
/**
* Enables or Disables SourceMod's automatic gamedata updating.
*
* The default value is "no". A value of "yes" will block the Auto Updater.
*/
"DisableAutoUpdate" "no"
/**
* If set to yes, a successful gamedata update will attempt to restart SourceMod.
* SourceMod is unloaded and reloaded, and the map is changed to the current map.
* Since gamedata updates occur when the server loads, impact should be minimal.
* But to be safe, this option is disabled by default.
*/
"ForceRestartAfterUpdate" "no"
/**
* URL to use for retrieving update information.
* SSL is not yet supported.
*/
"AutoUpdateURL" "http://update.sourcemod.net/update/"
/**
* Whether to show debug spew.
* Currently this will log details about the gamedata updating process.
*/
"DebugSpew" "no"
/**
* If set to yes, SourceMod will validate steamid auth strings with the Steam backend before giving out admin access.
* This can prevent malicious users from impersonating admins with stolen Steam apptickets.
* If Steam is down, admins will not be authenticated until Steam comes back up.
* This option increases the security of your server, but is still experimental.
*/
"SteamAuthstringValidation" "yes"
/**
* Enables or disables whether SourceMod blocks known or potentially malicious plugins from loading.
* It is STRONGLY advised that this is left enabled, there have been cases in the past with plugins that
* allow anyone to delete files on the server, gain full rcon control, etc.
*
* "yes" - Block malware or illegal plugins from loading (default)
* "no" - Warn about malware or illegal plugins loading
*/
"BlockBadPlugins" "yes"
/**
* If a plugin takes too long to execute, hanging or freezing the game server in the process,
* SourceMod will attempt to terminate that plugin after the specified timeout length has
* passed. You can disable this feature by setting the value to "0".
*/
"SlowScriptTimeout" "8"
}

View File

@ -0,0 +1,68 @@
;CSDM Configuration File
; Default settings by BAILOPAN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;You must be running the Main plugin for this section
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[settings]
;Sets whether CSDM is enabled or not.
enabled = 1
;Sets whether or not players should be stripped of weapons on round start
; (excludes knife)
strip_weapons = 1
;Sets how long weapons should stay on the ground for after being dropped
;in seconds. note that enabling this can create lots of lag for clients
; AND server. 0 is immediate, -1 is infinite.
weapons_stay = 0
;Sets the spawn mode.
; "none" - users spawn at normal map spawn points
; "preset" - csdm_spawn_preset.amxx required, uses predefined spawns in config files
; -- others may be supplied by 3rd party plugins
spawnmode = preset
;Sets whether the bomb is removed
remove_bomb = 1
;Sets the spawn waiting time
spawn_wait_time = 0.75
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;You must be running the protection plugin for this section
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[protection] ; some comment
;Is spawn protection enabled?
enabled = 1
;Colors of glow shell, leave this in quotes
;The digits are R, G, B, A where A is the alpha transparency
; (as A gets higher, the glow shell is thicker)
colors = "0 255 0 200"
;Number of seconds someone is respawned for.
time = 2
;;;;;;;;;;;;;;;;
;;WEAPON MENUS;;
;;;;;;;;;;;;;;;;
;Format for weapon menus is:
;shortname "Display Name" menupage
;Change the '1' to a '0' to block the weapon
;Removing or moving things
; from the list will change the order of the menus!
[secondary] gabe // < just to test param.
usp USP 1
glock18 Glock 1
deagle Deagle 1
;List weapons here the bots can randomly have
;The short name must match one in the list above
[botsecondary
deagle
usp

View File

@ -6,17 +6,24 @@ new Trie:ExpectedKVData;
public plugin_init()
{
register_concmd("textparse_vdf", "ConsoleCommand_TextParseVDF");
register_clcmd("textparse_ini", "ServerCommand_TextParseINI");
register_concmd("textparse", "ConsoleCommand_TextParse");
}
public ConsoleCommand_TextParse()
{
InitializeTextParseSMC();
InitializeTextParseINI();
}
/**
* VDF Config format
* SMC Config format
*/
public ConsoleCommand_TextParseVDF()
InitializeTextParseSMC()
{
server_print("Testing text parser with VDF config file format...");
SuccessCount = 0;
server_print("Testing text parser with SMC config file format...");
new const configFile[] = "addons/amxmodx/scripting/testsuite/textparse_test.cfg";
@ -53,7 +60,7 @@ public ConsoleCommand_TextParseVDF()
SMC_SetParseEnd(parser, "ReadCore_ParseEnd");
new line, col;
new SMCError:err = SMC_ParseFile_VDF(parser, configFile, line, col);
new SMCError:err = SMC_ParseFile(parser, configFile, line, col);
if (err != SMCError_Okay)
{
@ -66,22 +73,24 @@ public ConsoleCommand_TextParseVDF()
++SuccessCount;
}
SMC_DestroyParser(parser);
server_print("^tTests successful: %d/%d", SuccessCount, expectedStartEndCount + expectedSectionCount + expectedKeyValueCount + expectedLineCount + 1);
SMC_DestroyParser(parser);
TrieDestroy(ExpectedKVData);
SuccessCount = 0;
}
public ReadCore_ParseStart(TextParser:smc)
public ReadCore_ParseStart(TextParser:handle)
{
++SuccessCount;
}
public ReadCore_NewSection(TextParser:smc, const name[])
public ReadCore_NewSection(TextParser:handle, const name[])
{
++SuccessCount;
}
public ReadCore_KeyValue(TextParser:smc, const key[], const value[])
public ReadCore_KeyValue(TextParser:handle, const key[], const value[])
{
new buffer[128];
if (TrieGetString(ExpectedKVData, key, buffer, charsmax(buffer)) && equal(value, buffer))
@ -90,27 +99,124 @@ public ReadCore_KeyValue(TextParser:smc, const key[], const value[])
}
}
public ReadCore_EndSection(TextParser:smc)
public ReadCore_EndSection(TextParser:handle)
{
++SuccessCount;
}
public ReadCore_CurrentLine(TextParser:smc, const line[], lineno)
public ReadCore_CurrentLine(TextParser:handle, const line[], lineno)
{
++SuccessCount;
}
public ReadCore_ParseEnd(TextParser:smc)
public ReadCore_ParseEnd(TextParser:handle, bool:halted, bool:failed)
{
++SuccessCount;
}
/**
* INI Config format
*/
public ServerCommand_TextParseINI()
{
public InitializeTextParseINI()
{
SuccessCount = 0;
server_print("Testing text parser with INI config file format...");
new const configFile[] = "addons/amxmodx/scripting/testsuite/textparse_test.ini";
ExpectedKVData = TrieCreate();
TrieSetString(ExpectedKVData, "settings", "");
TrieSetString(ExpectedKVData, "enabled", "1");
TrieSetString(ExpectedKVData, "strip_weapons", "1");
TrieSetString(ExpectedKVData, "weapons_stay", "0");
TrieSetString(ExpectedKVData, "spawnmode", "preset");
TrieSetString(ExpectedKVData, "remove_bomb", "1");
TrieSetString(ExpectedKVData, "spawn_wait_time", "0.75");
TrieSetString(ExpectedKVData, "protection", "");
TrieSetString(ExpectedKVData, "colors", "0 255 0 200");
TrieSetString(ExpectedKVData, "time", "time");
TrieSetString(ExpectedKVData, "secondary", "");
TrieSetString(ExpectedKVData, "usp USP 1", "");
TrieSetString(ExpectedKVData, "glock18 Glock 1", "");
TrieSetString(ExpectedKVData, "deagle Deagle 1", "");
TrieSetString(ExpectedKVData, "botsecondary", "");
TrieSetString(ExpectedKVData, "deagle", "");
TrieSetString(ExpectedKVData, "usp", "");
new const expectedSectionCount = 4;
new const expectedStartEndCount = 2;
new const expectedKeyValueCount = TrieGetSize(ExpectedKVData) - expectedSectionCount;
new const expectedLineCount = TrieGetSize(ExpectedKVData); // This doesn't include blanck/comments line.
new TextParser:parser = SMC_CreateParser(.ini_format = true);
SMC_SetReaders(parser, "ReadCSDM_NewSection", "ReadCSDM_KeyValue");
SMC_SetParseStart(parser, "ReadCSDM_ParseStart");
SMC_SetRawLine(parser, "ReadCSDM_CurrentLine");
SMC_SetParseEnd(parser, "ReadCSDM_ParseEnd");
new line, col;
new SMCError:err = SMC_ParseFile(parser, configFile, line, col);
if (err != SMCError_Okay)
{
new buffer[64];
server_print("Error: %s", SMC_GetErrorString(err, buffer, charsmax(buffer)) ? buffer : "Fatal parse error");
}
if (line == expectedLineCount + 1)
{
++SuccessCount;
}
server_print("^tTests successful: %d/%d", SuccessCount, expectedStartEndCount + expectedSectionCount + expectedKeyValueCount + expectedLineCount + 1);
SMC_DestroyParser(parser);
TrieDestroy(ExpectedKVData);
}
public ReadCSDM_ParseStart(TextParser:handle)
{
++SuccessCount;
}
public ReadCSDM_NewSection(TextParser:handle, const section[], bool:invalid_tokens, bool:close_bracket, bool:extra_tokens, curtok)
{
if (TrieKeyExists(ExpectedKVData, section))
{
if ((equal(section, "secondary") && !extra_tokens) ||
(equal(section, "botsecondary") && close_bracket))
{
return;
}
++SuccessCount;
}
}
public ReadCSDM_KeyValue(TextParser:handle, const key[], const value[], bool:invalid_tokens, bool:equal_token, bool:quotes, curtok)
{
new buffer[128];
if (TrieGetString(ExpectedKVData, key, buffer, charsmax(buffer)) && equal(value, buffer))
{
if (equal(value, "colors") && !quotes)
{
return;
}
++SuccessCount;
}
}
public ReadCSDM_CurrentLine(TextParser:handle, const line[], lineno, curtok)
{
++SuccessCount;
}
public ReadCSDM_ParseEnd(TextParser:handle, bool:halted, bool:failed)
{
++SuccessCount;
}

View File

@ -81,6 +81,35 @@ namespace SourceMod
* products
*/
/**
* @brief Lists actions to take when an SMC parse hook is done.
*/
enum SMCResult
{
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 SMCError
{
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 Contains parse events for INI files.
*/
@ -95,6 +124,23 @@ namespace SourceMod
return SMINTERFACE_TEXTPARSERS_VERSION;
}
public:
/**
* @brief Called when starting parsing.
*/
virtual void ReadINI_ParseStart()
{
};
/**
* @brief Called when ending parsing.
*
* @param halted True if abnormally halted, false otherwise.
* @param failed True if parsing failed, false otherwise.
*/
virtual void ReadINI_ParseEnd(bool halted, bool failed)
{
}
/**
* @brief Called when a new section is encountered in an INI file.
*
@ -104,15 +150,11 @@ namespace SourceMod
* @param extra_tokens True if extra tokens were detected on the line.
* @param curtok Contains current token in the line where the section name starts.
* You can add to this offset when failing to point to a token.
* @return True to keep parsing, false otherwise.
* @return SMCResult directive.
*/
virtual bool ReadINI_NewSection(const char *section,
bool invalid_tokens,
bool close_bracket,
bool extra_tokens,
unsigned int *curtok)
virtual SMCResult ReadINI_NewSection(const char *section, bool invalid_tokens, bool close_bracket, bool extra_tokens, unsigned int *curtok)
{
return true;
return SMCResult_Continue;
}
/**
@ -125,28 +167,24 @@ namespace SourceMod
* @param quotes Whether value was enclosed in quotes.
* @param curtok Contains the token index of the start of the value string.
* This can be changed when returning false.
* @return True to keep parsing, false otherwise.
* @return SMCResult directive.
*/
virtual bool ReadINI_KeyValue(const char *key,
const char *value,
bool invalid_tokens,
bool equal_token,
bool quotes,
unsigned int *curtok)
virtual SMCResult ReadINI_KeyValue(const char *key, const char *value, bool invalid_tokens, bool equal_token, bool quotes, unsigned int *curtok)
{
return true;
return SMCResult_Continue;
}
/**
* @brief Called after a line has been preprocessed, if it has text.
*
* @param line Contents of line.
* @param lineno The line number it occurs on.
* @param curtok Pointer to optionally store failed position in string.
* @return True to keep parsing, false otherwise.
* @return SMCResult directive.
*/
virtual bool ReadINI_RawLine(const char *line, unsigned int *curtok)
virtual SMCResult ReadINI_RawLine(const char *line, unsigned int lineno, unsigned int *curtok)
{
return true;
return SMCResult_Continue;
}
};
@ -189,35 +227,6 @@ namespace SourceMod
* //<TEXT>
* / *<TEXT> */
/**
* @brief Lists actions to take when an SMC parse hook is done.
*/
enum SMCResult
{
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 SMCError
{
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
*/
@ -341,9 +350,9 @@ namespace SourceMod
* @param ini_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 True if parsing succeeded, false if file couldn't be opened or there was a syntax error.
* @return An SMCError result code.
*/
virtual bool ParseFile_INI(const char *file,
virtual SMCError ParseFile_INI(const char *file,
ITextListener_INI *ini_listener,
unsigned int *line,
unsigned int *col) =0;

View File

@ -235,6 +235,9 @@ scripting_files = [
'testsuite/trietest.sma',
'testsuite/utf8test.sma',
'testsuite/stacktest.sma',
'testsuite/textparse_test.sma',
'testsuite/textparse_test.cfg',
'testsuite/textparse_test.ini',
'include/amxconst.inc',
'include/amxmisc.inc',
'include/amxmodx.inc',