diff --git a/amxmodx/CTextParsers.cpp b/amxmodx/CTextParsers.cpp index 5ebcfaed..4e79cc87 100644 --- a/amxmodx/CTextParsers.cpp +++ b/amxmodx/CTextParsers.cpp @@ -1,33 +1,13 @@ -/** -* vim: set ts=4 : -* ============================================================================= -* SourceMod -* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. -* ============================================================================= -* -* 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 . -* -* 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 . -* -* Version: $Id$ -*/ +/** + * vim: set ts=4 sw=4 tw=99 noet: + * + * AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). + * Copyright (C) The AMX Mod X Development Team. + * + * This software is licensed under the GNU General Public License, version 3 or higher. + * Additional exceptions apply. For full license details, see LICENSE.txt or visit: + * https://alliedmods.net/amxmodx-license + */ #include "amxmodx.h" #include "CTextParsers.h" @@ -778,7 +758,7 @@ failed: * INI parser */ -SMCError TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listener, unsigned int *line, unsigned int *col) +bool 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,7 +772,7 @@ SMCError TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_lis *line = 0; } - return SMCError_StreamOpen; + return false; } ini_listener->ReadINI_ParseStart(); @@ -800,8 +780,6 @@ SMCError TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_lis char buffer[2048]; char *ptr, *save_ptr; bool in_quote; - SMCError err = SMCError_Okay; - SMCResult res = SMCResult_Continue; while (!feof(fp)) { @@ -853,22 +831,24 @@ SMCError TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_lis { switch (*ptr) { - case '"': - { - in_quote = true; - break; - } - case ';': - { - /* Stop the loop */ - len = i; - /* Terminate the string here */ - *ptr = '\0'; - break; - } + case '"': + { + in_quote = true; + break; + } + case ';': + { + /* Stop the loop */ + len = i; + + /* Terminate the string here */ + *ptr = '\0'; + break; + } } } - else { + else + { if (*ptr == '"') { in_quote = false; @@ -901,9 +881,8 @@ SMCError TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_lis continue; } - if ((res = ini_listener->ReadINI_RawLine(ptr, curline, &curtok)) != SMCResult_Continue) + if (!ini_listener->ReadINI_RawLine(ptr, &curtok)) { - err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; goto event_failed; } @@ -956,9 +935,8 @@ SMCError TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_lis } /* Tell the handler */ - if ((res = ini_listener->ReadINI_NewSection(&ptr[1], invalid_tokens, got_bracket, extra_tokens, &curtok)) != SMCResult_Continue) + if (!ini_listener->ReadINI_NewSection(&ptr[1], invalid_tokens, got_bracket, extra_tokens, &curtok)) { - err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; goto event_failed; } } @@ -1063,9 +1041,8 @@ SMCError TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_lis else curtok = 0; - if ((res = ini_listener->ReadINI_KeyValue(key_ptr, val_ptr, invalid_tokens, equal_token, quotes, &curtok)) != SMCResult_Continue) + if (!ini_listener->ReadINI_KeyValue(key_ptr, val_ptr, invalid_tokens, equal_token, quotes, &curtok)) { - err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; curtok = 0; goto event_failed; } @@ -1084,9 +1061,9 @@ SMCError TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_lis fclose(fp); - ini_listener->ReadINI_ParseEnd(false, false); + ini_listener->ReadINI_ParseEnd(false); - return SMCError_Okay; + return true; event_failed: if (line) @@ -1101,9 +1078,9 @@ event_failed: fclose(fp); - ini_listener->ReadINI_ParseEnd(true, (err == SMCError_Custom)); + ini_listener->ReadINI_ParseEnd(true); - return err; + return false; } const char *TextParsers::GetSMCErrorString(SMCError err) diff --git a/amxmodx/CTextParsers.h b/amxmodx/CTextParsers.h index 7aaafb63..8ceeca6d 100644 --- a/amxmodx/CTextParsers.h +++ b/amxmodx/CTextParsers.h @@ -1,33 +1,13 @@ -/** -* vim: set ts=4 : -* ============================================================================= -* SourceMod -* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. -* ============================================================================= -* -* 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 . -* -* 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 . -* -* Version: $Id$ -*/ +/** + * vim: set ts=4 sw=4 tw=99 noet: + * + * AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). + * Copyright (C) The AMX Mod X Development Team. + * + * This software is licensed under the GNU General Public License, version 3 or higher. + * Additional exceptions apply. For full license details, see LICENSE.txt or visit: + * https://alliedmods.net/amxmodx-license + */ #ifndef _INCLUDE_SOURCEMOD_TEXTPARSERS_H_ #define _INCLUDE_SOURCEMOD_TEXTPARSERS_H_ @@ -51,7 +31,7 @@ class TextParsers : public ITextParsers public: TextParsers(); public: - SMCError ParseFile_INI(const char *file, + bool ParseFile_INI(const char *file, ITextListener_INI *ini_listener, unsigned int *line, unsigned int *col); diff --git a/amxmodx/textparse.cpp b/amxmodx/textparse.cpp index b34bdc61..e72efed0 100644 --- a/amxmodx/textparse.cpp +++ b/amxmodx/textparse.cpp @@ -1,33 +1,13 @@ -/** -* vim: set ts=4 : -* ============================================================================= -* SourceMod -* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. -* ============================================================================= -* -* 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 . -* -* 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 . -* -* Version: $Id$ -*/ +/** + * vim: set ts=4 sw=4 tw=99 noet: + * + * AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). + * Copyright (C) The AMX Mod X Development Team. + * + * This software is licensed under the GNU General Public License, version 3 or higher. + * Additional exceptions apply. For full license details, see LICENSE.txt or visit: + * https://alliedmods.net/amxmodx-license + */ #include "amxmodx.h" #include @@ -35,16 +15,41 @@ TextParserHandles g_TextParsersHandles; -static cell AMX_NATIVE_CALL SMC_CreateParser(AMX *amx, cell *params) +cell createParser() { - int handle = g_TextParsersHandles.create(); - ParseInfo *p = g_TextParsersHandles.lookup(handle); - - p->ini_format = params[1] > 0 ? true : false; - - return static_cast(handle); + return g_TextParsersHandles.create(); } +cell destroyParser(cell *handle) +{ + ParseInfo *p = g_TextParsersHandles.lookup(*handle); + + if (p == NULL) + { + return 0; + } + + if (g_TextParsersHandles.destroy(*handle)) + { + *handle = 0; + return 1; + } + + return 0; +} + + +/** + * SMC CONFIG. + */ + +// native SMCParser:SMC_CreateParser(); +static cell AMX_NATIVE_CALL SMC_CreateParser(AMX *amx, cell *params) +{ + return createParser(); +} + +// native SMC_SetParseStart(SMCParser:handle, const func[]); static cell AMX_NATIVE_CALL SMC_SetParseStart(AMX *amx, cell *params) { ParseInfo *p = g_TextParsersHandles.lookup(params[1]); @@ -55,21 +60,24 @@ static cell AMX_NATIVE_CALL SMC_SetParseStart(AMX *amx, cell *params) return 0; } - int length; - const char* functionName = get_amxstring(amx, params[2], 0, length); - int function = registerSPForwardByName(amx, functionName, FP_CELL, FP_DONE); + int length = 0; + const char *funcName = NULL; - if (function == -1) + if ((funcName = get_amxstring(amx, params[2], 0, length)) && length) { - LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", functionName, g_plugins.findPluginFast(amx)->getName()); - return 0; + p->parse_start = registerSPForwardByName(amx, funcName, FP_CELL, FP_DONE); } - p->parse_start = function; + if (p->parse_start == -1) + { + LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", funcName, g_plugins.findPluginFast(amx)->getName()); + return 0; + } return 1; } +// native SMC_SetParseEnd(SMCParser:handle, const func[]); static cell AMX_NATIVE_CALL SMC_SetParseEnd(AMX *amx, cell *params) { ParseInfo *p = g_TextParsersHandles.lookup(params[1]); @@ -80,21 +88,24 @@ static cell AMX_NATIVE_CALL SMC_SetParseEnd(AMX *amx, cell *params) return 0; } - int length; - const char* funcName = get_amxstring(amx, params[2], 0, length); - int func = registerSPForwardByName(amx, funcName, FP_CELL, FP_CELL, FP_CELL, FP_DONE); + int length = 0; + const char *funcName = NULL; - if (func == -1) + if ((funcName = get_amxstring(amx, params[2], 0, length)) && length) + { + p->parse_end = registerSPForwardByName(amx, funcName, FP_CELL, FP_CELL, FP_CELL, FP_DONE); + } + + if (p->parse_end == -1) { LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", funcName, g_plugins.findPluginFast(amx)->getName()); return 0; } - p->parse_end = func; - return 1; } +// native SMC_SetReaders(SMCParser:smc, const kvFunc[], const nsFunc[] = "", const esFunc[] = ""); static cell AMX_NATIVE_CALL SMC_SetReaders(AMX *amx, cell *params) { ParseInfo *p = g_TextParsersHandles.lookup(params[1]); @@ -105,55 +116,34 @@ static cell AMX_NATIVE_CALL SMC_SetReaders(AMX *amx, cell *params) return 0; } - 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); + int kvLength = 0, nsLength = 0, esLength = 0; + const char *funcName = NULL; - 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) + if ((funcName = get_amxstring(amx, params[2], 0, kvLength)) && kvLength) { - LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", newSectionFuncName, g_plugins.findPluginFast(amx)->getName()); + p->key_value = registerSPForwardByName(amx, funcName, FP_CELL, FP_STRING, FP_STRING, FP_DONE); + } + + if (kvLength && (funcName = get_amxstring(amx, params[3], 1, nsLength)) && nsLength) + { + p->new_section = registerSPForwardByName(amx, funcName, FP_CELL, FP_STRING, FP_DONE); + } + + if (kvLength && (funcName = get_amxstring(amx, params[4], 2, esLength)) && esLength) + { + p->end_section = registerSPForwardByName(amx, funcName, FP_CELL, FP_DONE); + } + + if (p->key_value == -1 || (nsLength && p->new_section == -1) || (esLength && p->end_section == -1)) + { + LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", funcName, g_plugins.findPluginFast(amx)->getName()); return 0; } - 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()); - return 0; - } - - if (!p->ini_format) - { - 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; - return 1; } +// native SMC_SetRawLine(SMCParser:handle, const func[]); static cell AMX_NATIVE_CALL SMC_SetRawLine(AMX *amx, cell *params) { ParseInfo *p = g_TextParsersHandles.lookup(params[1]); @@ -164,26 +154,24 @@ static cell AMX_NATIVE_CALL SMC_SetRawLine(AMX *amx, cell *params) return 0; } - int length; - const char* funcName = get_amxstring(amx, params[2], 0, length); + int length = 0; + const char *funcName = NULL; - 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 ((funcName = get_amxstring(amx, params[2], 0, length)) && length) + { + p->raw_line = registerSPForwardByName(amx, funcName, FP_CELL, FP_STRING, FP_CELL, FP_DONE); + } - if (func == -1) + if (p->raw_line == -1) { LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", funcName, g_plugins.findPluginFast(amx)->getName()); return 0; } - p->raw_line = func; - return 1; } +// native SMCError:SMC_ParseFile(SMCParser:handle, const file[], &line = 0, &col = 0); static cell AMX_NATIVE_CALL SMC_ParseFile(AMX *amx, cell *params) { ParseInfo *p = g_TextParsersHandles.lookup(params[1]); @@ -197,28 +185,16 @@ 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)); - SMCError p_err; + SMCStates states; + SMCError p_err = textparsers->ParseFile_SMC(file, p, &states); - if (p->ini_format) - { - size_t line, col; - p_err = textparsers->ParseFile_INI(file, p, &line, &col); - - *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; - } + *get_amxaddr(amx, params[3]) = states.line; + *get_amxaddr(amx, params[4]) = states.col; return static_cast(p_err); } +// native bool:SMC_GetErrorString(SMCError:error, buffer[], buf_max); static cell AMX_NATIVE_CALL SMC_GetErrorString(AMX *amx, cell *params) { const char *str = textparsers->GetSMCErrorString((SMCError)params[1]); @@ -231,26 +207,170 @@ static cell AMX_NATIVE_CALL SMC_GetErrorString(AMX *amx, cell *params) return set_amxstring(amx, params[2], str, params[3]); } +// native SMC_DestroyParser(&SMCParser:handle); static cell AMX_NATIVE_CALL SMC_DestroyParser(AMX *amx, cell *params) { - cell *ptr = get_amxaddr(amx, params[1]); + return destroyParser(get_amxaddr(amx, params[1])); +} - ParseInfo *p = g_TextParsersHandles.lookup(*ptr); + +/** + * INI CONFIG + */ + +// native INIParser:INI_CreateParser(); +static cell AMX_NATIVE_CALL INI_CreateParser(AMX *amx, cell *params) +{ + return createParser(); +} + +// native bool:INI_ParseFile(INIParser:handle, const file[], &line = 0, &col = 0); +static cell AMX_NATIVE_CALL INI_ParseFile(AMX *amx, cell *params) +{ + ParseInfo *p = g_TextParsersHandles.lookup(params[1]); if (p == NULL) { + LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]); return 0; } - if (g_TextParsersHandles.destroy(*ptr)) + int length; + const char *file = build_pathname("%s", get_amxstring(amx, params[2], 0, length)); + + size_t line, col; + bool result = textparsers->ParseFile_INI(file, p, &line, &col); + + *get_amxaddr(amx, params[3]) = line; + *get_amxaddr(amx, params[4]) = col; + + return result; +} + +// native INI_SetParseStart(INIParser:handle, const func[]); +static cell AMX_NATIVE_CALL INI_SetParseStart(AMX *amx, cell *params) +{ + ParseInfo *p = g_TextParsersHandles.lookup(params[1]); + + if (p == NULL) { - *ptr = 0; - return 1; + LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]); + return 0; } - return 0; + int length = 0; + const char *funcName = NULL; + + if ((funcName = get_amxstring(amx, params[2], 0, length)) && length) + { + p->parse_start = registerSPForwardByName(amx, funcName, FP_CELL, FP_DONE); + } + + if (p->parse_start == -1) + { + LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", funcName, g_plugins.findPluginFast(amx)->getName()); + return 0; + } + + return 1; } +// native INI_SetParseEnd(INIParser:handle, const func[]); +static cell AMX_NATIVE_CALL INI_SetParseEnd(AMX *amx, cell *params) +{ + ParseInfo *p = g_TextParsersHandles.lookup(params[1]); + + if (p == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]); + return 0; + } + + int length = 0; + const char *funcName = NULL; + + if ((funcName = get_amxstring(amx, params[2], 0, length))) + { + p->parse_end = registerSPForwardByName(amx, funcName, FP_CELL, FP_CELL, FP_CELL, FP_DONE); + } + + if (p->parse_end == -1) + { + LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", funcName, g_plugins.findPluginFast(amx)->getName()); + return 0; + } + + return 1; +} + +// native INI_SetReaders(INIParser:smc, const kvFunc[], const nsFunc[] = "" ); +static cell AMX_NATIVE_CALL INI_SetReaders(AMX *amx, cell *params) +{ + ParseInfo *p = g_TextParsersHandles.lookup(params[1]); + + if (p == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]); + return 0; + } + + int kvLength = 0, nsLength = 0; + const char *funcName = NULL; + + if ((funcName = get_amxstring(amx, params[2], 0, kvLength)) && kvLength) + { + p->key_value = registerSPForwardByName(amx, funcName, FP_CELL, FP_STRING, FP_STRING, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE); + } + + if (kvLength && (funcName = get_amxstring(amx, params[3], 1, nsLength)) && nsLength) + { + p->new_section = registerSPForwardByName(amx, funcName, FP_CELL, FP_STRING, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE); + } + + if (p->key_value == -1 || (nsLength && p->new_section == -1)) + { + LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", funcName, g_plugins.findPluginFast(amx)->getName()); + return 0; + } + + return 1; +} + +// native INI_SetRawLine(INIParser:handle, const func[]); +static cell AMX_NATIVE_CALL INI_SetRawLine(AMX *amx, cell *params) +{ + ParseInfo *p = g_TextParsersHandles.lookup(params[1]); + + if (p == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]); + return 0; + } + + int length = 0; + const char *funcName = NULL; + + if ((funcName = get_amxstring(amx, params[2], 0, length)) && length) + { + p->raw_line = registerSPForwardByName(amx, funcName, FP_CELL, FP_STRING, FP_CELL, FP_CELL, FP_DONE); + } + + if (p->raw_line == -1) + { + LogError(amx, AMX_ERR_NATIVE, "Function is not present (function \"%s\") (plugin \"%s\")", funcName, g_plugins.findPluginFast(amx)->getName()); + return 0; + } + + return 1; +} + +// native INI_DestroyParser(&INIParser:handle); +static cell AMX_NATIVE_CALL INI_DestroyParser(AMX *amx, cell *params) +{ + return destroyParser(get_amxaddr(amx, params[1])); +} + + AMX_NATIVE_INFO g_TextParserNatives[] = { { "SMC_CreateParser" , SMC_CreateParser }, @@ -261,5 +381,14 @@ AMX_NATIVE_INFO g_TextParserNatives[] = { "SMC_SetReaders" , SMC_SetReaders }, { "SMC_SetRawLine" , SMC_SetRawLine }, { "SMC_DestroyParser" , SMC_DestroyParser }, + + { "INI_CreateParser" , INI_CreateParser }, + { "INI_ParseFile" , INI_ParseFile }, + { "INI_SetParseStart" , INI_SetParseStart }, + { "INI_SetParseEnd" , INI_SetParseEnd }, + { "INI_SetReaders" , INI_SetReaders }, + { "INI_SetRawLine" , INI_SetRawLine }, + { "INI_DestroyParser" , INI_DestroyParser }, + { NULL, NULL }, }; diff --git a/amxmodx/textparse.h b/amxmodx/textparse.h index 10b55dd3..93d3ffc9 100644 --- a/amxmodx/textparse.h +++ b/amxmodx/textparse.h @@ -1,33 +1,13 @@ -/** -* vim: set ts=4 : -* ============================================================================= -* SourceMod -* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. -* ============================================================================= -* -* 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 . -* -* 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 . -* -* Version: $Id$ -*/ +/** + * vim: set ts=4 sw=4 tw=99 noet: + * + * AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). + * Copyright (C) The AMX Mod X Development Team. + * + * This software is licensed under the GNU General Public License, version 3 or higher. + * Additional exceptions apply. For full license details, see LICENSE.txt or visit: + * https://alliedmods.net/amxmodx-license + */ #ifndef _INCLUDE_TEXTPARSE_H_ #define _INCLUDE_TEXTPARSE_H_ @@ -43,37 +23,31 @@ public: ParseInfo() { parse_start = -1; - parse_end = -1; + parse_end = -1; new_section = -1; - key_value = -1; + key_value = -1; end_section = -1; - raw_line = -1; - handle = -1; - ini_format = false; + raw_line = -1; + handle = -1; } public: + + /** + * SMC CONFIG. + */ + void ReadSMC_ParseStart() { 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) 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) { @@ -82,13 +56,6 @@ public: 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; - } SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value) { @@ -97,13 +64,6 @@ public: 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; - } SMCResult ReadSMC_LeavingSection(const SMCStates *states) { @@ -120,12 +80,46 @@ public: return SMCResult_Continue; } - SMCResult ReadINI_RawLine(const char *line, unsigned int lineno, unsigned int *curtok) + + + /** + * INI CONFIG. + */ + + void ReadINI_ParseStart() + { + if (parse_start != -1) + executeForwards(parse_start, handle); + } + + void ReadINI_ParseEnd(bool halted) + { + if (parse_end != -1) + executeForwards(parse_end, handle, halted ? 1 : 0); + } + + bool ReadINI_NewSection(const char *section, bool invalid_tokens, bool close_bracket, bool extra_tokens, unsigned int *curtok) + { + if (new_section != -1) + return executeForwards(new_section, handle, section, invalid_tokens, close_bracket, extra_tokens, *curtok) > 0 ? false : true; + + return true; + } + + bool ReadINI_KeyValue(const char *key, const char *value, bool invalid_tokens, bool equal_token, bool quotes, unsigned int *curtok) + { + if (key_value != -1) + return executeForwards(key_value, handle, key, value, invalid_tokens, equal_token, quotes, *curtok) > 0 ? false : true; + + return true; + } + + bool ReadINI_RawLine(const char *line, unsigned int *curtok) { if (raw_line != -1) - return (SMCResult)executeForwards(raw_line, handle, line, lineno, *curtok); + return executeForwards(raw_line, handle, line, *curtok) > 0 ? false : true; - return SMCResult_Continue; + return true; } public: int parse_start; @@ -135,7 +129,6 @@ public: int end_section; int raw_line; int handle; - bool ini_format; }; template diff --git a/plugins/include/amxmodx.inc b/plugins/include/amxmodx.inc index 36503ce0..cedbd6d6 100755 --- a/plugins/include/amxmodx.inc +++ b/plugins/include/amxmodx.inc @@ -27,6 +27,8 @@ #include #include #include +#include +#include /* Function is called just after server activation. * Good place for configuration loading, commands and cvars registration. */ diff --git a/plugins/include/textparse.inc b/plugins/include/textparse.inc deleted file mode 100644 index d7eef5ef..00000000 --- a/plugins/include/textparse.inc +++ /dev/null @@ -1,316 +0,0 @@ - -#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. - */ - -/** - * 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: - * ; - * - * 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: - * ; - * // - * / * * / - */ - -enum TextParser -{ - Invalid_TextParser = 0 -}; - -/** - * Parse result directive. - */ -enum SMCResult -{ - SMCParse_Continue, /**< Continue parsing */ - SMCParse_Halt, /**< Stop parsing here */ - SMCParse_HaltFail /**< Stop parsing and return failure */ -}; - -/** - * Parse error codes. - */ -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 */ -}; - - -/** - * Creates a new text parser. - * This is used to set parse hooks. - * - * @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(bool:ini_format = false); - -/** - * Disposes of a text parser. - * - * @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 A SMCParseError result. - * @error Invalid or corrupt handle. - */ -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. - * - * @note SMCError_Okay returns false. - * @note SMCError_Custom (which is thrown on SMCParse_HaltFail) returns false. - * - * @param error The SMCParseError code. - * @param buffer A string buffer for the error (contents undefined on failure). - * @param buf_max The maximum size of the buffer. - * - * @return True on success, false otherwise. - */ -native bool:SMC_GetErrorString(SMCError:error, buffer[], buf_max); \ No newline at end of file diff --git a/plugins/include/textparse_ini.inc b/plugins/include/textparse_ini.inc new file mode 100644 index 00000000..f5891a1c --- /dev/null +++ b/plugins/include/textparse_ini.inc @@ -0,0 +1,196 @@ + +#if defined _textparse_ini_included + #endinput +#endif +#define _textparse_ini_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. + */ + +/** + * 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: + * ; + * + * 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 + */ + +/** + * Parser invalid code. + */ +enum INIParser +{ + Invalid_INIParser = 0 +}; + +/** + * Creates a new INI parser. + * This is used to set parse hooks. + * + * @return A new handle to an INI Parse structure. + */ +native INIParser:INI_CreateParser(); + +/** + * Disposes of a INI parser. + * + * @param handle Handle to a INI Parse. + * + * @return True if disposed, false otherwise. + */ +native INI_DestroyParser(&INIParser:handle); + +/** + * Parses a INI config file. + * + * @param handle A handle to an INI 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 A SMCParseError result. + * @error Invalid or corrupt handle. + */ +native bool:INI_ParseFile(INIParser:handle, const file[], &line = 0, &col = 0); + +/** + * Sets the INI_ParseStart function of a parse handle. + * + * @note Below is the prototype of callback: + * - + * Called when parsing is started. + * + * @param handle The INI Parse handle. + * + * @noreturn + * + * public OnParseStart(INIParser:handle) + * - + * @param handle Handle to a INI Parse. + * @param func A ParseStart callback. + * + * @noreturn + * @error Invalid or corrupt handle. + */ +native INI_SetParseStart(INIParser:handle, const func[]); + +/** + * Sets the INI_ParseEnd of a parse handle. + * + * @note Below is the prototype of callback: + * - + * Called when parsing is halted. + * + * @param handle The INI Parse handle. + * @param halted True if abnormally halted, false otherwise. + * + * @noreturn + * + * public OnParseEnd(INIParser:handle, bool:halted) + * - + * @param handle Handle to a INI Parse. + * @param func A ParseEnd callback.. + * + * @noreturn + * @error Invalid or corrupt handle. + */ +native INI_SetParseEnd(INIParser:handle, const func[]); + +/** + * Sets the three main reader functions. + * + * @note Below is the prototype of callback: + * - + * NewSection: + * Called when the parser finds the end of the current section. + * + * @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 True to keep parsing, false otherwise. + * + * public bool:OnNewSection(INIParser:handle, const section[], bool:invalid_tokens, bool:close_bracket, bool:extra_tokens, curtok) + * + * KeyValue: + * Called when the parser finds a new key/value pair. + * + * @param handle The INI 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 True to keep parsing, false otherwise. + * + * public bool:OnKeyValue(INIParser:handle, const key[], const value[], bool:invalid_tokens, bool:equal_token, bool:quotes, curtok) + * - + * @param handle The INI parse handle. + * @param kv A KeyValue callback. + * @param ns An optional NewSection callback. + * + * @noreturn + */ +native INI_SetReaders(INIParser:smc, const kvFunc[], const nsFunc[] = "" ); + +/** + * Sets a raw line reader on an INI parser handle. + * + * @note Below is the prototype of callback: + * - + * Called whenever a raw line is read. + * + * @param handle The INI 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 True to keep parsing, false otherwise. + * + * public bool:OnRawLine(INIParser:smc, const line[], lineno, curtok) + * + * @param handle Handle to an INI Parse. + * @param func A RawLine callback. + * + * @noreturn + */ +native INI_SetRawLine(INIParser:handle, const func[]); diff --git a/plugins/include/textparse_smc.inc b/plugins/include/textparse_smc.inc new file mode 100644 index 00000000..3b76102d --- /dev/null +++ b/plugins/include/textparse_smc.inc @@ -0,0 +1,242 @@ + +#if defined _textparse_smc_included + #endinput +#endif +#define _textparse_smc_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. + */ + +/** + * 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: + * ; + * // + * / * * / + */ + +/** + * Parser invalid code. + */ +enum SMCParser +{ + Invalid_SMCParser = 0 +}; + +/** + * Parse result directive. + */ +enum SMCResult +{ + SMCParse_Continue, /* Continue parsing */ + SMCParse_Halt, /* Stop parsing here */ + SMCParse_HaltFail /* Stop parsing and return failure */ +}; + +/** + * Parse error codes. + */ +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 */ +}; + +/** + * Creates a new SMC parser. + * This is used to set parse hooks. + * + * @return A new handle to an SMC Parse structure. + */ +native SMCParser:SMC_CreateParser(); + +/** + * Disposes of a SMC parser. + * + * @param handle Handle to a SMC Parse + * + * @return True if disposed, false otherwise. + */ +native SMC_DestroyParser(&SMCParser:handle); + +/** + * Parses a config file. + * + * @param handle A handle to an SMC 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 A SMCParseError result. + * @error Invalid or corrupt handle. + */ +native SMCError:SMC_ParseFile(SMCParser:handle, const file[], &line = 0, &col = 0); + +/** + * Sets the SMC_ParseStart function of a parse handle. + * + * @note Below is the prototype of callback: + * - + * Called when parsing is started. + * + * @param handle The Text Parse handle. + * + * @noreturn + * + * public OnParseStart(SMCParser:handle) + * - + * @param handle Handle to a Text Parse. + * @param func A ParseStart callback. + * + * @noreturn + * @error Invalid or corrupt handle. + */ +native SMC_SetParseStart(SMCParser:handle, const func[]); + +/** + * Sets the SMC_ParseEnd of a parse handle. + * + * @note Below is the prototype of callback: + * - + * 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. + * + * @noreturn + * + * public OnParseEnd(SMCParser: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(SMCParser:handle, const func[]); + +/** + * Sets the three main reader functions. + * + * @note Enclosing quotes are always stripped. + * @note Below is the prototype of callbacks: + * - + * NewSection: + * Called when the parser finds the end of the current section. + * + * @param handle The Text Parse handle. + * @param name String containing section name. + * + * @return An SMCResult action to take. + * + * public SMCResult:OnNewSection(SMCParser:handle, const name[]) + * + * KeyValue: + * Called when the parser finds a new key/value pair. + * + * @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:OnKeyValue(SMCParser:handle, const key[], const value[]) + * + * EndSection: + * Called when the parser finds the end of the current section. + * + * @param handle The Text Parse handle. + * + * public SMCResult:OnEndSection(SMCParser:handle) + * - + * @param handle The Text parse handle. + * @param kv A KeyValue callback. + * @param ns An optional NewSection callback. + * @param es An optional EndSection callback. + * + * @noreturn + */ +native SMC_SetReaders(SMCParser:smc, const kvFunc[], const nsFunc[] = "", const esFunc[] = ""); + +/** + * Sets a raw line reader on an text parser handle. + * + * @note Below is the prototype of callbacks: + * - + * Called whenever a raw line is read. + * + * @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(SMCParser:handle, const line[], lineno) + * - + * @param handle Handle to an Text Parse. + * @param func A RawLine callback. + * + * @noreturn + */ +native SMC_SetRawLine(SMCParser:handle, const func[]); + +/** + * Gets an error string for an SMCError code. + * + * @note SMCError_Okay returns false. + * @note SMCError_Custom (which is thrown on SMCParse_HaltFail) returns false. + * + * @param error The SMCParseError code. + * @param buffer A string buffer for the error (contents undefined on failure). + * @param buf_max The maximum size of the buffer. + * + * @return True on success, false otherwise. + */ +native bool:SMC_GetErrorString(SMCError:error, buffer[], buf_max); diff --git a/plugins/testsuite/textparse_test.sma b/plugins/testsuite/textparse_test.sma index 297aeeb7..0bb37d28 100644 --- a/plugins/testsuite/textparse_test.sma +++ b/plugins/testsuite/textparse_test.sma @@ -1,12 +1,25 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet: + * + * AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). + * Copyright (C) The AMX Mod X Development Team. + * + * This software is licensed under the GNU General Public License, version 3 or higher. + * Additional exceptions apply. For full license details, see LICENSE.txt or visit: + * https://alliedmods.net/amxmodx-license + */ + #include -#include new SuccessCount; new Trie:ExpectedKVData; +new bool:Debug; public plugin_init() { register_concmd("textparse", "ConsoleCommand_TextParse"); + + Debug = !!(plugin_flags() & AMX_FLAG_DEBUG); } public ConsoleCommand_TextParse() @@ -52,9 +65,9 @@ InitializeTextParseSMC() new const expectedKeyValueCount = TrieGetSize(ExpectedKVData); new const expectedLineCount = file_size(configFile, .flag = 1) - 1; - new TextParser:parser = SMC_CreateParser(); + new SMCParser:parser = SMC_CreateParser(); - SMC_SetReaders(parser, "ReadCore_NewSection", "ReadCore_KeyValue", "ReadCore_EndSection"); + SMC_SetReaders(parser, "ReadCore_KeyValue", "ReadCore_NewSection", "ReadCore_EndSection"); SMC_SetParseStart(parser, "ReadCore_ParseStart"); SMC_SetRawLine(parser, "ReadCore_CurrentLine"); SMC_SetParseEnd(parser, "ReadCore_ParseEnd"); @@ -80,18 +93,22 @@ InitializeTextParseSMC() SuccessCount = 0; } -public ReadCore_ParseStart(TextParser:handle) +public ReadCore_ParseStart(SMCParser:handle) { + Debug && server_print("ReadCore_ParseStart"); ++SuccessCount; } -public ReadCore_NewSection(TextParser:handle, const name[]) +public ReadCore_NewSection(SMCParser:handle, const name[]) { + Debug && server_print("^tReadCore_NewSection - %s", name); ++SuccessCount; } -public ReadCore_KeyValue(TextParser:handle, const key[], const value[]) +public ReadCore_KeyValue(SMCParser:handle, const key[], const value[]) { + Debug && server_print("^t^tReadCore_KeyValue - %-32s %s", key, value); + new buffer[128]; if (TrieGetString(ExpectedKVData, key, buffer, charsmax(buffer)) && equal(value, buffer)) { @@ -99,18 +116,21 @@ public ReadCore_KeyValue(TextParser:handle, const key[], const value[]) } } -public ReadCore_EndSection(TextParser:handle) +public ReadCore_EndSection(SMCParser:handle) { + Debug && server_print("^tReadCore_EndSection"); ++SuccessCount; } -public ReadCore_CurrentLine(TextParser:handle, const line[], lineno) +public ReadCore_CurrentLine(SMCParser:handle, const line[], lineno) { - ++SuccessCount; + //Debug && server_print("^t^tReadCore_CurrentLine - %s", line); + ++SuccessCount; } -public ReadCore_ParseEnd(TextParser:handle, bool:halted, bool:failed) -{ +public ReadCore_ParseEnd(SMCParser:handle, bool:halted, bool:failed) +{ + Debug &&server_print("ReadCore_ParseEnd - halted: %s, failed: %s", halted ? "yes" : "no", failed ? "yes" : "no"); ++SuccessCount; } @@ -151,20 +171,19 @@ public InitializeTextParseINI() 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); + new INIParser:parser = INI_CreateParser(); - SMC_SetReaders(parser, "ReadCSDM_NewSection", "ReadCSDM_KeyValue"); - SMC_SetParseStart(parser, "ReadCSDM_ParseStart"); - SMC_SetRawLine(parser, "ReadCSDM_CurrentLine"); - SMC_SetParseEnd(parser, "ReadCSDM_ParseEnd"); + INI_SetReaders(parser, "ReadCSDM_KeyValue", "ReadCSDM_NewSection"); + INI_SetParseStart(parser, "ReadCSDM_ParseStart"); + INI_SetRawLine(parser, "ReadCSDM_CurrentLine"); + INI_SetParseEnd(parser, "ReadCSDM_ParseEnd"); new line, col; - new SMCError:err = SMC_ParseFile(parser, configFile, line, col); + new bool:result = INI_ParseFile(parser, configFile, line, col); - if (err != SMCError_Okay) + if (!result) { - new buffer[64]; - server_print("Error: %s", SMC_GetErrorString(err, buffer, charsmax(buffer)) ? buffer : "Fatal parse error"); + server_print("^tFatal parse error"); } if (line == expectedLineCount + 1) @@ -174,17 +193,20 @@ public InitializeTextParseINI() server_print("^tTests successful: %d/%d", SuccessCount, expectedStartEndCount + expectedSectionCount + expectedKeyValueCount + expectedLineCount + 1); - SMC_DestroyParser(parser); + INI_DestroyParser(parser); TrieDestroy(ExpectedKVData); } -public ReadCSDM_ParseStart(TextParser:handle) +public ReadCSDM_ParseStart(INIParser:handle) { + Debug && server_print("ReadCSDM_ParseStart"); ++SuccessCount; } -public ReadCSDM_NewSection(TextParser:handle, const section[], bool:invalid_tokens, bool:close_bracket, bool:extra_tokens, curtok) +public ReadCSDM_NewSection(INIParser:handle, const section[], bool:invalid_tokens, bool:close_bracket, bool:extra_tokens, curtok) { + Debug && server_print("^tReadCSDM_NewSection - [%s]", section); + if (TrieKeyExists(ExpectedKVData, section)) { if ((equal(section, "secondary") && !extra_tokens) || @@ -197,8 +219,10 @@ public ReadCSDM_NewSection(TextParser:handle, const section[], bool:invalid_toke } } -public ReadCSDM_KeyValue(TextParser:handle, const key[], const value[], bool:invalid_tokens, bool:equal_token, bool:quotes, curtok) +public ReadCSDM_KeyValue(INIParser:handle, const key[], const value[], bool:invalid_tokens, bool:equal_token, bool:quotes, curtok) { + Debug && server_print("^t^tReadCSDM_KeyValue - %-32s %s", key, value); + new buffer[128]; if (TrieGetString(ExpectedKVData, key, buffer, charsmax(buffer)) && equal(value, buffer)) { @@ -211,12 +235,14 @@ public ReadCSDM_KeyValue(TextParser:handle, const key[], const value[], bool:inv } } -public ReadCSDM_CurrentLine(TextParser:handle, const line[], lineno, curtok) +public ReadCSDM_CurrentLine(INIParser:handle, const line[], curtok) { - ++SuccessCount; + //Debug && server_print("^t^tReadCSDM_CurrentLine - %s", line); + ++SuccessCount; } -public ReadCSDM_ParseEnd(TextParser:handle, bool:halted, bool:failed) +public ReadCSDM_ParseEnd(INIParser:handle, bool:halted, bool:failed) { + Debug && server_print("ReadCSDM_ParseStart"); ++SuccessCount; } \ No newline at end of file diff --git a/public/ITextParsers.h b/public/ITextParsers.h index a8ef31b4..2d3dfc5e 100644 --- a/public/ITextParsers.h +++ b/public/ITextParsers.h @@ -1,32 +1,12 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= +/** + * vim: set ts=4 sw=4 tw=99 noet: * - * 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. + * AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). + * Copyright (C) The AMX Mod X Development Team. * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * 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 . - * - * Version: $Id$ + * This software is licensed under the GNU General Public License, version 3 or higher. + * Additional exceptions apply. For full license details, see LICENSE.txt or visit: + * https://alliedmods.net/amxmodx-license */ #ifndef _INCLUDE_SOURCEMOD_TEXTPARSERS_INTERFACE_H_ @@ -81,35 +61,6 @@ 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. */ @@ -135,9 +86,8 @@ namespace SourceMod * @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) + virtual void ReadINI_ParseEnd(bool halted) { } @@ -150,11 +100,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 SMCResult directive. + * @return True to keep parsing, false otherwise. */ - virtual SMCResult ReadINI_NewSection(const char *section, bool invalid_tokens, bool close_bracket, bool extra_tokens, unsigned int *curtok) + virtual bool ReadINI_NewSection(const char *section, bool invalid_tokens, bool close_bracket, bool extra_tokens, unsigned int *curtok) { - return SMCResult_Continue; + return true; } /** @@ -167,24 +117,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 SMCResult directive. + * @return True to keep parsing, false otherwise. */ - virtual SMCResult ReadINI_KeyValue(const char *key, const char *value, bool invalid_tokens, bool equal_token, bool quotes, unsigned int *curtok) + virtual bool ReadINI_KeyValue(const char *key, const char *value, bool invalid_tokens, bool equal_token, bool quotes, unsigned int *curtok) { - return SMCResult_Continue; + return true; } /** * @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 SMCResult directive. + * + * @return True to keep parsing, false otherwise. */ - virtual SMCResult ReadINI_RawLine(const char *line, unsigned int lineno, unsigned int *curtok) + virtual bool ReadINI_RawLine(const char *line, unsigned int *curtok) { - return SMCResult_Continue; + return true; } }; @@ -227,6 +177,35 @@ namespace SourceMod * // * / * */ + /** + * @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 */ @@ -350,9 +329,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 An SMCError result code. + * @return True if parsing succeeded, false if file couldn't be opened or there was a syntax error. */ - virtual SMCError ParseFile_INI(const char *file, + virtual bool ParseFile_INI(const char *file, ITextListener_INI *ini_listener, unsigned int *line, unsigned int *col) =0;