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);