dvander@372: /** dvander@375: * vim: set ts=4 : ds@1330: * ============================================================================= dvander@1226: * SourceMod ds@1947: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. ds@1330: * ============================================================================= dvander@372: * ds@1330: * This program is free software; you can redistribute it and/or modify it under ds@1330: * the terms of the GNU General Public License, version 3.0, as published by the ds@1330: * Free Software Foundation. dvander@379: * ds@1330: * This program is distributed in the hope that it will be useful, but WITHOUT ds@1330: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ds@1330: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ds@1330: * details. dvander@1226: * ds@1330: * You should have received a copy of the GNU General Public License along with ds@1330: * this program. If not, see . dvander@1226: * ds@1330: * As a special exception, AlliedModders LLC gives you permission to link the ds@1330: * code of this program (as well as its derivative works) to "Half-Life 2," the ds@1330: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software ds@1330: * by the Valve Corporation. You must obey the GNU General Public License in ds@1330: * all respects for all other code used. Additionally, AlliedModders LLC grants ds@1330: * this exception to all derivative works. AlliedModders LLC defines further ds@1330: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), ds@1330: * or . dvander@372: * dvander@372: * Version: $Id$ dvander@372: */ dvander@372: dvander@324: #ifndef _INCLUDE_SOURCEMOD_TEXTPARSERS_INTERFACE_H_ dvander@324: #define _INCLUDE_SOURCEMOD_TEXTPARSERS_INTERFACE_H_ dvander@324: dvander@372: /** dvander@372: * @file ITextParsers.h dvander@372: * @brief Defines various text/file parsing functions, as well as UTF-8 support code. dvander@372: */ dvander@372: dvander@324: #include dvander@324: dvander@324: namespace SourceMod dvander@324: { dvander@1656: dvander@1656: #define SMINTERFACE_TEXTPARSERS_NAME "ITextParsers" dvander@2558: #define SMINTERFACE_TEXTPARSERS_VERSION 4 dvander@1656: dvander@324: /** dvander@324: * The INI file format is defined as: dvander@324: * WHITESPACE: 0x20, \n, \t, \r dvander@324: * IDENTIFIER: A-Z a-z 0-9 _ - , + . $ ? / dvander@324: * STRING: Any set of symbols dvander@324: * dvander@324: * Basic syntax is comprised of SECTIONs. dvander@324: * A SECTION is defined as: dvander@324: * [SECTIONNAME] dvander@324: * OPTION dvander@324: * OPTION dvander@324: * OPTION... dvander@324: * dvander@324: * SECTIONNAME is an IDENTIFIER. dvander@324: * OPTION can be repeated any number of times, once per line. dvander@324: * OPTION is defined as one of: dvander@324: * KEY = "VALUE" dvander@324: * KEY = VALUE dvander@324: * KEY dvander@324: * Where KEY is an IDENTIFIER and VALUE is a STRING. dvander@324: * dvander@324: * WHITESPACE should always be omitted. ds@729: * COMMENTS should be stripped, and are defined as text occurring in: dvander@324: * ; dvander@324: * dvander@324: * Example file below. Note that dvander@324: * The second line is technically invalid. The event handler dvander@324: * must decide whether this should be allowed. dvander@324: * --FILE BELOW-- dvander@324: * [gaben] dvander@324: * hi = clams dvander@324: * bye = "NO CLAMS" dvander@324: * dvander@324: * [valve] dvander@324: * cannot dvander@324: * maintain dvander@324: * products dvander@324: */ dvander@372: dvander@372: /** dvander@372: * @brief Contains parse events for INI files. dvander@372: */ dvander@324: class ITextListener_INI dvander@324: { dvander@324: public: dvander@1656: /** dvander@1656: * @brief Returns version number. dvander@1656: */ dvander@1656: virtual unsigned int GetTextParserVersion1() dvander@1656: { dvander@1656: return SMINTERFACE_TEXTPARSERS_VERSION; dvander@1656: } dvander@1656: public: dvander@324: /** dvander@324: * @brief Called when a new section is encountered in an INI file. dvander@324: * dvander@324: * @param section Name of section in between the [ and ] characters. dvander@324: * @param invalid_tokens True if invalid tokens were detected in the name. dvander@324: * @param close_bracket True if a closing bracket was detected, false otherwise. dvander@324: * @param extra_tokens True if extra tokens were detected on the line. dvander@324: * @param curtok Contains current token in the line where the section name starts. dvander@324: * You can add to this offset when failing to point to a token. dvander@324: * @return True to keep parsing, false otherwise. dvander@324: */ dvander@324: virtual bool ReadINI_NewSection(const char *section, dvander@324: bool invalid_tokens, dvander@324: bool close_bracket, dvander@324: bool extra_tokens, dvander@324: unsigned int *curtok) dvander@324: { dvander@324: return true; dvander@324: } dvander@324: dvander@324: /** dvander@324: * @brief Called when encountering a key/value pair in an INI file. dvander@324: * dvander@324: * @param key Name of key. dvander@324: * @param value String containing value (with quotes stripped, if any). dvander@324: * @param invalid_tokens Whether or not the key contained invalid tokens. dvander@324: * @param equal_token There was an '=' sign present (in case the value is missing). dvander@324: * @param quotes Whether value was enclosed in quotes. dvander@372: * @param curtok Contains the token index of the start of the value string. dvander@324: * This can be changed when returning false. dvander@324: * @return True to keep parsing, false otherwise. dvander@324: */ dvander@324: virtual bool ReadINI_KeyValue(const char *key, dvander@324: const char *value, dvander@324: bool invalid_tokens, dvander@324: bool equal_token, dvander@324: bool quotes, dvander@324: unsigned int *curtok) dvander@324: { dvander@324: return true; dvander@324: } dvander@324: dvander@324: /** dvander@324: * @brief Called after a line has been preprocessed, if it has text. dvander@324: * dvander@324: * @param line Contents of line. dvander@324: * @param curtok Pointer to optionally store failed position in string. dvander@324: * @return True to keep parsing, false otherwise. dvander@324: */ dvander@372: virtual bool ReadINI_RawLine(const char *line, unsigned int *curtok) dvander@324: { dvander@324: return true; dvander@324: } dvander@324: }; dvander@324: dvander@324: /** dvander@324: * :TODO: write this in CFG (context free grammar) format so it makes sense dvander@324: * dvander@324: * The SMC file format is defined as: dvander@324: * WHITESPACE: 0x20, \n, \t, \r dvander@351: * IDENTIFIER: Any ASCII character EXCLUDING ", {, }, ;, //, / *, or WHITESPACE. dvander@324: * STRING: Any set of symbols enclosed in quotes. dvander@324: * Note: if a STRING does not have quotes, it is parsed as an IDENTIFIER. dvander@324: * dvander@324: * Basic syntax is comprised of SECTIONBLOCKs. dvander@324: * A SECTIONBLOCK defined as: dvander@324: * dvander@324: * SECTIONNAME dvander@324: * { dvander@324: * OPTION dvander@324: * } dvander@324: * dvander@324: * OPTION can be repeated any number of times inside a SECTIONBLOCK. dvander@324: * A new line will terminate an OPTION, but there can be more than one OPTION per line. dvander@324: * OPTION is defined any of: dvander@324: * "KEY" "VALUE" dvander@324: * SECTIONBLOCK dvander@324: * dvander@324: * SECTIONNAME, KEY, VALUE, and SINGLEKEY are strings dvander@324: * SECTIONNAME cannot have trailing characters if quoted, but the quotes can be optionally removed. dvander@324: * If SECTIONNAME is not enclosed in quotes, the entire sectionname string is used (minus surrounding whitespace). dvander@324: * If KEY is not enclosed in quotes, the key is terminated at first whitespace. dvander@324: * If VALUE is not properly enclosed in quotes, the entire value string is used (minus surrounding whitespace). dvander@324: * The VALUE may have inner quotes, but the key string may not. dvander@324: * dvander@324: * For an example, see configs/permissions.cfg dvander@324: * dvander@324: * WHITESPACE should be ignored. ds@729: * Comments are text occurring inside the following tokens, and should be stripped dvander@324: * unless they are inside literal strings: dvander@324: * ; dvander@324: * // dvander@351: * / * */ dvander@324: dvander@372: /** dvander@372: * @brief Lists actions to take when an SMC parse hook is done. dvander@372: */ dvander@1656: enum SMCResult dvander@324: { dvander@1656: SMCResult_Continue, /**< Continue parsing */ dvander@1656: SMCResult_Halt, /**< Stop parsing here */ dvander@1656: SMCResult_HaltFail /**< Stop parsing and return SMCError_Custom */ dvander@324: }; dvander@324: dvander@372: /** dvander@372: * @brief Lists error codes possible from parsing an SMC file. dvander@372: */ dvander@1656: enum SMCError dvander@324: { dvander@1656: SMCError_Okay = 0, /**< No error */ dvander@1656: SMCError_StreamOpen, /**< Stream failed to open */ dvander@1656: SMCError_StreamError, /**< The stream died... somehow */ dvander@1656: SMCError_Custom, /**< A custom handler threw an error */ dvander@1656: SMCError_InvalidSection1, /**< A section was declared without quotes, and had extra tokens */ dvander@1656: SMCError_InvalidSection2, /**< A section was declared without any header */ dvander@1656: SMCError_InvalidSection3, /**< A section ending was declared with too many unknown tokens */ dvander@1656: SMCError_InvalidSection4, /**< A section ending has no matching beginning */ dvander@1656: SMCError_InvalidSection5, /**< A section beginning has no matching ending */ dvander@1656: SMCError_InvalidTokens, /**< There were too many unidentifiable strings on one line */ dvander@1656: SMCError_TokenOverflow, /**< The token buffer overflowed */ dvander@1656: SMCError_InvalidProperty1, /**< A property was declared outside of any section */ dvander@1656: }; dvander@1656: dvander@1656: /** dvander@1656: * @brief States for line/column dvander@1656: */ dvander@1656: struct SMCStates dvander@1656: { dvander@1656: unsigned int line; /**< Current line */ dvander@1656: unsigned int col; /**< Current col */ dvander@324: }; dvander@324: dvander@372: /** dvander@372: * @brief Describes the events available for reading an SMC stream. dvander@372: */ dvander@324: class ITextListener_SMC dvander@324: { dvander@324: public: dvander@1656: /** dvander@1656: * @brief Returns version number. dvander@1656: */ dvander@1656: virtual unsigned int GetTextParserVersion2() dvander@1656: { dvander@1656: return SMINTERFACE_TEXTPARSERS_VERSION; dvander@1656: } dvander@1656: public: dvander@324: /** dvander@324: * @brief Called when starting parsing. dvander@324: */ dvander@324: virtual void ReadSMC_ParseStart() dvander@324: { dvander@324: }; dvander@324: dvander@324: /** dvander@324: * @brief Called when ending parsing. dvander@324: * dvander@324: * @param halted True if abnormally halted, false otherwise. dvander@324: * @param failed True if parsing failed, false otherwise. dvander@324: */ dvander@324: virtual void ReadSMC_ParseEnd(bool halted, bool failed) dvander@324: { dvander@324: } dvander@324: dvander@324: /** dvander@324: * @brief Called when entering a new section dvander@324: * dvander@1656: * @param states Parsing states. dvander@324: * @param name Name of section, with the colon omitted. dvander@1656: * @return SMCResult directive. dvander@324: */ dvander@1656: virtual SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name) dvander@324: { dvander@1656: return SMCResult_Continue; dvander@324: } dvander@324: dvander@324: /** dvander@324: * @brief Called when encountering a key/value pair in a section. dvander@324: * dvander@1656: * @param states Parsing states. dvander@324: * @param key Key string. dvander@324: * @param value Value string. If no quotes were specified, this will be NULL, dvander@1656: * and key will contain the entire string. dvander@1656: * @return SMCResult directive. dvander@324: */ dvander@1656: virtual SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value) dvander@324: { dvander@1656: return SMCResult_Continue; dvander@324: } dvander@324: dvander@324: /** dvander@324: * @brief Called when leaving the current section. dvander@324: * dvander@1678: * @param states Parsing states. dvander@1656: * @return SMCResult directive. dvander@324: */ dvander@1656: virtual SMCResult ReadSMC_LeavingSection(const SMCStates *states) dvander@324: { dvander@1656: return SMCResult_Continue; dvander@324: } dvander@324: dvander@324: /** dvander@324: * @brief Called after an input line has been preprocessed. dvander@324: * dvander@1656: * @param states Parsing states. dvander@1656: * @param line Contents of the line, null terminated at the position dvander@1656: * of the newline character (thus, no newline will exist). dvander@1656: * @return SMCResult directive. dvander@324: */ dvander@1656: virtual SMCResult ReadSMC_RawLine(const SMCStates *states, const char *line) dvander@324: { dvander@1656: return SMCResult_Continue; dvander@324: } dvander@324: }; dvander@324: dvander@372: /** dvander@372: * @brief Contains various text stream parsing functions. dvander@372: */ dvander@324: class ITextParsers : public SMInterface dvander@324: { dvander@324: public: dvander@324: virtual const char *GetInterfaceName() dvander@324: { dvander@324: return SMINTERFACE_TEXTPARSERS_NAME; dvander@324: } dvander@324: virtual unsigned int GetInterfaceVersion() dvander@324: { dvander@324: return SMINTERFACE_TEXTPARSERS_VERSION; dvander@324: } dvander@1656: virtual bool IsVersionCompatible(unsigned int version) dvander@1656: { dvander@1656: if (version < 2) dvander@1656: { dvander@1656: return false; dvander@1656: } dvander@1656: return SMInterface::IsVersionCompatible(version); dvander@1656: } dvander@324: public: dvander@324: /** dvander@324: * @brief Parses an INI-format file. dvander@324: * dvander@324: * @param file Path to file. dvander@324: * @param ini_listener Event handler for reading file. dvander@324: * @param line If non-NULL, will contain last line parsed (0 if file could not be opened). dvander@324: * @param col If non-NULL, will contain last column parsed (undefined if file could not be opened). ds@729: * @return True if parsing succeeded, false if file couldn't be opened or there was a syntax error. dvander@324: */ dvander@324: virtual bool ParseFile_INI(const char *file, dvander@324: ITextListener_INI *ini_listener, dvander@324: unsigned int *line, dvander@324: unsigned int *col) =0; dvander@324: dvander@324: /** dvander@324: * @brief Parses an SMC-format text file. dvander@324: * Note that the parser makes every effort to obey broken syntax. dvander@324: * For example, if an open brace is missing, but the section name has a colon, dvander@324: * it will let you know. It is up to the event handlers to decide whether to be strict or not. dvander@324: * dvander@324: * @param file Path to file. dvander@324: * @param smc_listener Event handler for reading file. dvander@1656: * @param states Optional pointer to store last known states. dvander@1656: * @return An SMCError result code. dvander@324: */ dvander@1656: virtual SMCError ParseFile_SMC(const char *file, dvander@324: ITextListener_SMC *smc_listener, dvander@1656: SMCStates *states) =0; dvander@324: dvander@324: /** dvander@1656: * @brief Converts an SMCError to a string. dvander@324: * dvander@1656: * @param err SMCError. dvander@324: * @return String error message, or NULL if none. dvander@324: */ dvander@1656: virtual const char *GetSMCErrorString(SMCError err) =0; dvander@324: dvander@324: public: dvander@324: /** dvander@324: * @brief Returns the number of bytes that a multi-byte character contains in a UTF-8 stream. dvander@324: * If the current character is not multi-byte, the function returns 1. dvander@324: * dvander@324: * @param stream Pointer to multi-byte ANSI character string. dvander@324: * @return Number of bytes in current character. dvander@324: */ dvander@324: virtual unsigned int GetUTF8CharBytes(const char *stream) =0; dvander@1406: dvander@1406: /** dvander@1406: * @brief Returns whether the first multi-byte character in the given stream dvander@1406: * is a whitespace character. dvander@1406: * dvander@1406: * @param stream Pointer to multi-byte character string. dvander@1406: * @return True if first character is whitespace, false otherwise. dvander@1406: */ dvander@1406: virtual bool IsWhitespace(const char *stream) =0; dvander@1815: dvander@1815: /** dvander@1815: * @brief Same as ParseFile_SMC, but with an extended error buffer. dvander@1815: * dvander@1815: * @param file Path to file. dvander@1815: * @param smc_listener Event handler for reading file. dvander@1815: * @param states Optional pointer to store last known states. dvander@1815: * @param buffer Error message buffer. dvander@1815: * @param maxsize Maximum size of the error buffer. dvander@2558: * @return Error code. dvander@1815: */ dvander@1815: virtual SMCError ParseSMCFile(const char *file, dvander@1815: ITextListener_SMC *smc_listener, dvander@1815: SMCStates *states, dvander@1815: char *buffer, dvander@1815: size_t maxsize) =0; dvander@2558: dvander@2558: /** dvander@2558: * @brief Parses a raw UTF8 stream as an SMC file. dvander@2558: * dvander@2558: * @param stream Memory containing data. dvander@2558: * @param length Number of bytes in the stream. dvander@2558: * @param smc_listener Event handler for reading file. dvander@2558: * @param states Optional pointer to store last known states. dvander@2558: * @param buffer Error message buffer. dvander@2558: * @param maxsize Maximum size of the error buffer. dvander@2558: * @return Error code. dvander@2558: */ dvander@2558: virtual SMCError ParseSMCStream(const char *stream, dvander@2558: size_t length, dvander@2558: ITextListener_SMC *smc_listener, dvander@2558: SMCStates *states, dvander@2558: char *buffer, dvander@2558: size_t maxsize) =0; dvander@324: }; dvander@1406: dvander@1406: inline unsigned int _GetUTF8CharBytes(const char *stream) dvander@1406: { dvander@1406: unsigned char c = *(unsigned char *)stream; dvander@1406: if (c & (1<<7)) dvander@1406: { dvander@1406: if (c & (1<<5)) dvander@1406: { dvander@1406: if (c & (1<<4)) dvander@1406: { dvander@1406: return 4; dvander@1406: } dvander@1406: return 3; dvander@1406: } dvander@1406: return 2; dvander@1406: } dvander@1406: return 1; dvander@1406: } ds@432: } dvander@324: dvander@1406: extern SourceMod::ITextParsers *textparsers; dvander@1406: dvander@324: #endif //_INCLUDE_SOURCEMOD_TEXTPARSERS_INTERFACE_H_ dvander@1656: