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: