From f19fbac013e4ef84fe56e16598b5025bb3055a14 Mon Sep 17 00:00:00 2001 From: David Anderson <dvander@alliedmods.net> Date: Mon, 6 Nov 2006 10:57:37 +0000 Subject: [PATCH] renamed mm_api, I don't like short names! finished and tested the INI parser and its API --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40150 --- core/CTextParsers.cpp | 286 ++++++++++++++++++++++++++ core/CTextParsers.h | 27 +++ core/interfaces/ITextParsers.h | 54 ++++- core/msvc8/sourcemod_mm.vcproj | 12 +- core/{mm_api.cpp => sourcemm_api.cpp} | 12 +- core/{mm_api.h => sourcemm_api.h} | 0 core/systems/LibrarySys.h | 2 +- 7 files changed, 382 insertions(+), 11 deletions(-) create mode 100644 core/CTextParsers.cpp create mode 100644 core/CTextParsers.h rename core/{mm_api.cpp => sourcemm_api.cpp} (80%) rename core/{mm_api.h => sourcemm_api.h} (100%) diff --git a/core/CTextParsers.cpp b/core/CTextParsers.cpp new file mode 100644 index 00000000..063d1f3b --- /dev/null +++ b/core/CTextParsers.cpp @@ -0,0 +1,286 @@ +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include "CTextParsers.h" + +CTextParsers g_TextParse; + +static int g_ini_chartable1[255] = {0}; + +CTextParsers::CTextParsers() +{ + g_ini_chartable1['_'] = 1; + g_ini_chartable1['-'] = 1; + g_ini_chartable1[','] = 1; + g_ini_chartable1['+'] = 1; + g_ini_chartable1['.'] = 1; + g_ini_chartable1['$'] = 1; + g_ini_chartable1['?'] = 1; + g_ini_chartable1['/'] = 1; +} + +bool CTextParsers::ParseFile_SMC(const char *file, ITextListener_SMC *smc_listener, unsigned int *line, unsigned int *col) +{ + /* :TODO: Implement this */ + if (line) + { + *line = 0; + } + + return false; +} + +bool CTextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listener, unsigned int *line, unsigned int *col) +{ + FILE *fp = fopen(file, "rt"); + unsigned int curline = 0; + unsigned int curtok; + size_t len; + + if (!fp) + { + if (line) + { + *line = 0; + } + + return false; + } + + char buffer[2048]; + char *ptr, *save_ptr; + bool in_quote; + while (!feof(fp)) + { + curline++; + curtok = 0; + buffer[0] = '\0'; + if (fgets(buffer, sizeof(buffer), fp) == NULL) + { + break; + } + + /* Preprocess the string before anything */ + ptr = buffer; + + /* First strip beginning whitespace */ + while ((*ptr != '\0') && isspace(*ptr)) + { + ptr++; + } + + len = strlen(ptr); + + if (!len) + { + continue; + } + + /* Now search for comment characters */ + in_quote = false; + save_ptr = ptr; + for (size_t i=0; i<len; i++,ptr++) + { + if (!in_quote) + { + switch (*ptr) + { + case '"': + { + in_quote = true; + break; + } + case ';': + { + /* Stop the loop */ + len = i; + /* Terminate the string here */ + *ptr = '\0'; + break; + } + } + } else { + if (*ptr == '"') + { + in_quote = false; + } + } + } + + if (!len) + { + continue; + } + + ptr = save_ptr; + + /* Lastly, strip ending whitespace off */ + for (size_t i=len-1; i>=0 && i<len; i--) + { + if (isspace(ptr[i])) + { + ptr[i] = '\0'; + len--; + } else { + break; + } + } + + if (!len) + { + continue; + } + + if (!ini_listener->ReadINI_RawLine(ptr, &curtok)) + { + goto event_failed; + } + + if (*ptr == '[') + { + bool invalid_tokens = false; + bool got_bracket = false; + bool extra_tokens = false; + char c; + + for (size_t i=1; i<len; i++) + { + c = ptr[i]; + if (!isalnum(c) && !g_ini_chartable1[c]) + { + /* First check - is this a bracket? */ + if (c == ']') + { + /* Yes! */ + got_bracket = true; + /* If this isn't the last character... */ + if (i != len - 1) + { + extra_tokens = true; + } + /* terminate */ + ptr[i] = '\0'; + break; + } else { + /* n...No! Continue copying. */ + invalid_tokens = true; + } + } + } + + /* Tell the handler */ + if (!ini_listener->ReadINI_NewSection(&ptr[1], invalid_tokens, got_bracket, extra_tokens, &curtok)) + { + goto event_failed; + } + } else { + char *key_ptr = ptr; + char *val_ptr = NULL; + char c; + size_t first_space = 0; + bool invalid_tokens = false; + bool equal_token = false; + bool quotes = false; + + for (size_t i=0; i<len; i++) + { + c = ptr[i]; + /* is this an invalid char? */ + if (!isalnum(c) && !g_ini_chartable1[c]) + { + if (isspace(c)) + { + /* if it's a space, keep track of the last space */ + if (!first_space) + { + first_space = i; + } + } else { + if (c == '=') + { + /* if it's an equal sign, we're done with the key */ + if (first_space) + { + /* remove excess whitespace */ + key_ptr[first_space] = '\0'; + } else { + /* remove the equal sign */ + key_ptr[i] = '\0'; + } + if (ptr[++i] != '\0') + { + /* If this isn't the end, set next pointer */ + val_ptr = &ptr[i]; + } + equal_token = true; + break; + } else { + /* Mark that we got something invalid! */ + invalid_tokens = true; + first_space = 0; + } + } + } + } + + /* Now we need to parse the value, if any */ + if (val_ptr) + { + /* eat up spaces! there shouldn't be any h*/ + while ((*val_ptr != '\0') && isspace(*val_ptr)) + { + val_ptr++; + } + if (*val_ptr == '\0') + { + val_ptr = NULL; + goto skip_value; + } + /* Do we have an initial quote? If so, the parsing rules change! */ + if (*val_ptr == '"' && *val_ptr != '\0') + { + len = strlen(val_ptr); + if (val_ptr[len-1] == '"') + { + /* Strip quotes! */ + val_ptr[--len] = '\0'; + val_ptr++; + quotes = true; + } + } + } +skip_value: + /* We're done! */ + curtok = val_ptr - buffer; + if (!ini_listener->ReadINI_KeyValue(key_ptr, val_ptr, invalid_tokens, equal_token, quotes, &curtok)) + { + curtok = 0; + goto event_failed; + } + } + } + + if (line) + { + *line = curline; + } + + fclose(fp); + + return true; + +event_failed: + if (line) + { + *line = curline; + } + + if (col) + { + *col = curtok; + } + + fclose(fp); + + return false; +} diff --git a/core/CTextParsers.h b/core/CTextParsers.h new file mode 100644 index 00000000..d6c255ae --- /dev/null +++ b/core/CTextParsers.h @@ -0,0 +1,27 @@ +#ifndef _INCLUDE_SOURCEMOD_TEXTPARSERS_H_ +#define _INCLUDE_SOURCEMOD_TEXTPARSERS_H_ + +#include "interfaces/ITextParsers.h" + +using namespace SourceMod; + +class CTextParsers : public ITextParsers +{ + +public: + CTextParsers(); +public: + virtual bool ParseFile_INI(const char *file, + ITextListener_INI *ini_listener, + unsigned int *line, + unsigned int *col); + + virtual bool ParseFile_SMC(const char *file, + ITextListener_SMC *smc_listener, + unsigned int *line, + unsigned int *col); +}; + +extern CTextParsers g_TextParse; + +#endif //_INCLUDE_SOURCEMOD_TEXTPARSERS_H_ diff --git a/core/interfaces/ITextParsers.h b/core/interfaces/ITextParsers.h index 6654e6be..11ac1ae3 100644 --- a/core/interfaces/ITextParsers.h +++ b/core/interfaces/ITextParsers.h @@ -1,5 +1,5 @@ -#ifndef _INCLUDE_SOURCEMOD_TEXTPARSERS_H_ -#define _INCLUDE_SOURCEMOD_TEXTPARSERS_H_ +#ifndef _INCLUDE_SOURCEMOD_TEXTPARSERS_INTERFACE_H_ +#define _INCLUDE_SOURCEMOD_TEXTPARSERS_INTERFACE_H_ #include <IShareSys.h> @@ -50,9 +50,18 @@ namespace SourceMod * @brief Called when a new section is encountered in an INI file. * * @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. + * You can add to this offset when failing to point to a token. * @return True to keep parsing, false otherwise. */ - virtual bool ReadINI_NewSection(const char *section) + virtual bool ReadINI_NewSection(const char *section, + bool invalid_tokens, + bool close_bracket, + bool extra_tokens, + unsigned int *curtok) { return true; } @@ -61,11 +70,32 @@ namespace SourceMod * @brief Called when encountering a key/value pair in an INI file. * * @param key Name of key. - * @param value Name of value. + * @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 curtoken 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. */ - virtual bool ReadINI_KeyValue(const char *key, const char *value, bool quotes) + virtual bool ReadINI_KeyValue(const char *key, + const char *value, + bool invalid_tokens, + bool equal_token, + bool quotes, + unsigned int *curtok) + { + return true; + } + + /** + * @brief Called after a line has been preprocessed, if it has text. + * + * @param line Contents of line. + * @param curtok Pointer to optionally store failed position in string. + * @return True to keep parsing, false otherwise. + */ + virtual bool ReadINI_RawLine(const char *line, unsigned int *cutok) { return true; } @@ -163,8 +193,20 @@ namespace SourceMod } }; + #define SMINTERFACE_TEXTPARSERS_NAME "ITextParsers" + #define SMINTERFACE_TEXTPARSERS_VERSION 1 + class ITextParsers : public SMInterface { + public: + virtual const char *GetInterfaceName() + { + return SMINTERFACE_TEXTPARSERS_NAME; + } + virtual unsigned int GetInterfaceVersion() + { + return SMINTERFACE_TEXTPARSERS_VERSION; + } public: /** * @brief Parses an INI-format file. @@ -199,4 +241,4 @@ namespace SourceMod }; }; -#endif //_INCLUDE_SOURCEMOD_TEXTPARSERS_H_ +#endif //_INCLUDE_SOURCEMOD_TEXTPARSERS_INTERFACE_H_ diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index 601f50e8..99938f78 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -179,7 +179,11 @@ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > <File - RelativePath="..\mm_api.cpp" + RelativePath="..\CTextParsers.cpp" + > + </File> + <File + RelativePath="..\sourcemm_api.cpp" > </File> </Filter> @@ -189,7 +193,7 @@ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > <File - RelativePath="..\mm_api.h" + RelativePath="..\CTextParsers.h" > </File> <File @@ -200,6 +204,10 @@ RelativePath="..\sm_version.h" > </File> + <File + RelativePath="..\sourcemm_api.h" + > + </File> </Filter> <Filter Name="Resource Files" diff --git a/core/mm_api.cpp b/core/sourcemm_api.cpp similarity index 80% rename from core/mm_api.cpp rename to core/sourcemm_api.cpp index 77691245..4c4f0bfd 100644 --- a/core/mm_api.cpp +++ b/core/sourcemm_api.cpp @@ -1,16 +1,24 @@ #include <oslink.h> -#include "mm_api.h" +#include "sourcemm_api.h" #include "sm_version.h" -#include "systems/LibrarySys.h" +#include "CTextParsers.h" SourceMod_Core g_SourceMod; PLUGIN_EXPOSE(SourceMod, g_SourceMod); +class Test : public ITextListener_INI +{ +public: +}; + bool SourceMod_Core::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late) { PLUGIN_SAVEVARS(); + Test test; + bool result = g_TextParse.ParseFile_INI("c:\\gaben.ini", &test, NULL, NULL); + return true; } diff --git a/core/mm_api.h b/core/sourcemm_api.h similarity index 100% rename from core/mm_api.h rename to core/sourcemm_api.h diff --git a/core/systems/LibrarySys.h b/core/systems/LibrarySys.h index f471745d..1937523c 100644 --- a/core/systems/LibrarySys.h +++ b/core/systems/LibrarySys.h @@ -48,7 +48,7 @@ private: LibraryHandle m_lib; }; -class LibrarySystem +class LibrarySystem : public ILibrarySys { public: virtual ILibrary *OpenLibrary(const char *path, char *error, size_t err_max);