Viewing file: IniFile.h (11.86 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/* * Phusion Passenger - https://www.phusionpassenger.com/ * Copyright (c) 2011-2017 Phusion Holding B.V. * * "Passenger", "Phusion Passenger" and "Union Station" are registered * trademarks of Phusion Holding B.V. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef _PASSENGER_INI_FILE_H_ #define _PASSENGER_INI_FILE_H_
#include <utility> #include <string> #include <map> #include <fstream> #include <iostream> #include <cstdio> #include <cstring> #include <cassert> #include <cctype> #include <boost/shared_ptr.hpp> #include <boost/make_shared.hpp> #include <Exceptions.h>
namespace Passenger {
using namespace std; using namespace boost;
class IniFileSection { protected: typedef map<string, string> ValueMap; string sectionName; ValueMap values;
public: IniFileSection(const string §ionName) { this->sectionName = sectionName; }
bool hasKey(const string &keyName) const { return values.find(keyName) != values.end(); }
string get(const string &keyName) const { ValueMap::const_iterator it = values.find(keyName); if (it != values.end()) { return it->second; } else { return string(); } } string operator[](const string &keyName) const { return get(keyName); }
void set(const string &keyName, const string &value) { values[keyName] = value; }
string getSectionName() const { return sectionName; }
void inspect() const { ValueMap::const_iterator it = values.begin(); while (it != values.end()) { cout << it->first << " = " << it->second << endl; it++; } } };
class IniFileLexer { public: class Token { public: enum Kind { UNKNOWN = 0, NEWLINE, SECTION_NAME, IDENTIFIER, ASSIGNMENT, TEXT, END_OF_FILE };
const Kind kind; const string value; const int line; const int column;
// String representations of the Kind enum. const static char *identityByKind(Kind kind) { const static char* KIND_IDENTITY_TABLE[] = { "<T_UNKNOWN>", "<T_NEWLINE>", "<T_SECTION_NAME>", "<T_IDENTIFIER>", "<T_ASSIGNMENT>", "<T_TEXT>", "<T_EOF>" }; return KIND_IDENTITY_TABLE[kind]; }
Token(const Kind kind, const string &value, const int line, const int column) : kind(kind), value(value), line(line), column(column) { } class ExpectanceException : public std::exception { private: char message[255];
public: ExpectanceException(char expected, char got, int line, int column) { int messageSize = sizeof(message); memset(message, 0, messageSize); snprintf(message, messageSize, "On line %i, column %i: Expected '%c', got '%c' instead.", line, column, expected, got); }
ExpectanceException(Token::Kind expected, Token got) { const char *expectedKindString = Token::identityByKind(expected); int messageSize = sizeof(message); memset(message, 0, messageSize); snprintf(message, messageSize, "On line %i, column %i: Expected '%s', got '%s' instead.", got.line, got.column, expectedKindString, got.value.c_str()); }
ExpectanceException(char expected, Token::Kind got, int line, int column) { const char *gotKindString = Token::identityByKind(got); int messageSize = sizeof(message); memset(message, 0, messageSize); snprintf(message, messageSize, "On line %i, column %i: Expected '%c', got '%s' instead.", line, column, expected, gotKindString); }
virtual const char* what() const throw() { return message; } }; };
typedef boost::shared_ptr<IniFileLexer::Token> TokenPtr;
protected: ifstream iniFileStream;
char lastAcceptedChar; int upcomingChar; bool upcomingTokenPtrIsStale;
int currentLine; int currentColumn;
TokenPtr upcomingTokenPtr;
void expect(char ch) { int upcomingChar = iniFileStream.peek(); if (ch != upcomingChar) { switch(upcomingChar) { case EOF: throw Token::ExpectanceException(ch, Token::END_OF_FILE, currentLine, currentColumn + 1); case '\n': throw Token::ExpectanceException(ch, upcomingChar, currentLine + 1, 0); default: throw Token::ExpectanceException(ch, upcomingChar, currentLine, currentColumn + 1); } } }
void accept() { if (upcomingChar == EOF) return; lastAcceptedChar = (char)iniFileStream.get(); upcomingChar = iniFileStream.peek(); currentColumn++; if (lastAcceptedChar == '\n') { currentLine++; currentColumn = 1; } }
void ignore() { if (upcomingChar == EOF) return; upcomingChar = iniFileStream.peek(); currentColumn++; if ((char)iniFileStream.get() == '\n') { currentLine++; currentColumn = 1; } }
void expectAndAccept(char ch) { expect(ch); accept(); }
void acceptWhileNewLine() { while (iniFileStream.good() && upcomingChar == '\n') { accept(); } }
void ignoreWhileNotNewLine() { while (iniFileStream.good() && upcomingChar != '\n') { ignore(); } }
Token tokenizeIdentifier() { int line = currentLine; int column = currentColumn; string result; while (isalnum(upcomingChar) || upcomingChar == '_' || upcomingChar == '-') { result.append(1, upcomingChar); accept(); } return Token(Token::IDENTIFIER, result, line, column); }
Token tokenizeSection() { expectAndAccept('['); Token sectionName = tokenizeSectionName(); expectAndAccept(']'); return sectionName; }
Token tokenizeSectionName() { int line = currentLine; int column = currentColumn; string result; //while (upcomingChar != ']' && upcomingChar != '[' && upcomingChar != '\n' && upcomingChar != EOF) { while (isalnum(upcomingChar) || upcomingChar == '_' || upcomingChar == '-') { result.append(1, upcomingChar); accept(); } return Token(Token::SECTION_NAME, result, line, column); }
Token tokenizeAssignment() { expectAndAccept('='); return Token(Token::ASSIGNMENT, "=", currentLine, currentColumn); }
Token tokenizeText() { int line = currentLine; int column = currentColumn; string result; while (upcomingChar != '\n' && upcomingChar != EOF) { result.append(1, upcomingChar); accept(); } return Token(Token::TEXT, result, line, column); }
Token tokenizeKey() { return tokenizeIdentifier(); }
Token tokenizeValue() { return tokenizeText(); }
Token tokenizeUnknown() { int line = currentLine; int column = currentColumn; string result; while (upcomingChar != EOF) { result.append(1, upcomingChar); accept(); } return Token(Token::UNKNOWN, result, line, column); }
public: IniFileLexer(const string &fileName) { currentLine = 1; currentColumn = 1; upcomingTokenPtrIsStale = true; iniFileStream.open(fileName.c_str()); if (iniFileStream.fail()) { int e = errno; throw FileSystemException("Cannot open file '" + fileName + "' for reading", e, fileName); } }
~IniFileLexer() { iniFileStream.close(); }
int getCurrentLine() { return currentLine; }
int getCurrentColumn() { return currentColumn; }
TokenPtr peekToken() { if (upcomingTokenPtrIsStale) { Token upcomingToken = getToken(); upcomingTokenPtr = boost::make_shared<Token>(upcomingToken); upcomingTokenPtrIsStale = false; } return upcomingTokenPtr; }
Token getToken() { if (!upcomingTokenPtrIsStale) { upcomingTokenPtrIsStale = true; return *upcomingTokenPtr; } while (iniFileStream.good()) { upcomingChar = iniFileStream.peek(); switch(upcomingChar) { case '[': return tokenizeSection(); case '\n': if (lastAcceptedChar != '\n') { accept(); return Token(Token::NEWLINE, "\n", currentLine, currentColumn); } else { ignore(); break; } case ';': // Comment encountered: accept all characters until newline (exclusive). ignoreWhileNotNewLine(); break; case '=': return tokenizeAssignment(); case EOF: return Token(Token::END_OF_FILE, "<END_OF_FILE>", currentLine, currentColumn); default: if (isblank(upcomingChar)) { ignore(); } else { switch(lastAcceptedChar) { case '\n': return tokenizeKey(); case '=': return tokenizeValue(); default: return tokenizeUnknown(); } } } } return Token(Token::END_OF_FILE, "<END_OF_FILE>", currentLine, currentColumn); } };
typedef boost::shared_ptr<IniFileSection> IniFileSectionPtr;
class IniFile { protected: typedef map<string, IniFileSectionPtr> SectionMap; string name; SectionMap sections; class IniFileParser { typedef IniFileLexer::Token Token;
protected: IniFileLexer lexer; IniFile *iniFile;
// The Start Symbol. void parseSections() { while ((lexer.peekToken())->kind == Token::SECTION_NAME) { parseSection(); } }
void parseSection() { Token token = acceptAndReturnif(Token::SECTION_NAME); acceptIfEOL();
string sectionName = token.value; IniFileSection *section = new IniFileSection(sectionName); iniFile->addSection(section);
parseSectionBody(section); }
void parseSectionBody(IniFileSection *currentSection) { while ((lexer.peekToken())->kind == Token::IDENTIFIER) { parseKeyValue(currentSection); } }
void parseKeyValue(IniFileSection *currentSection) { Token identifierToken = acceptAndReturnif (Token::IDENTIFIER); acceptif (Token::ASSIGNMENT); Token valueToken = acceptAndReturnif (Token::TEXT); acceptIfEOL(); currentSection->set(identifierToken.value, valueToken.value); }
void acceptif (Token::Kind expectedKind) { Token token = lexer.getToken();
if (token.kind != expectedKind) { throw Token::ExpectanceException(expectedKind, token); } }
void acceptIfEOL() { Token token = lexer.getToken();
if (token.kind != Token::NEWLINE && token.kind != Token::END_OF_FILE) { throw Token::ExpectanceException(Token::NEWLINE, token); } }
Token acceptAndReturnif (Token::Kind expectedKind) { Token token = lexer.getToken();
if (token.kind != expectedKind) { throw Token::ExpectanceException(expectedKind, token); }
return token; }
public: IniFileParser(IniFile *iniFile) : lexer(iniFile->getName()), iniFile(iniFile) { parseSections(); } }; public: IniFile(const string &iniFileName) : name(iniFileName) { IniFileParser parser(this); }
void addSection(IniFileSection *section) { sections.insert(make_pair(section->getSectionName(), IniFileSectionPtr(section))); }
IniFileSectionPtr section(const string §ionName) { SectionMap::iterator it = sections.find(sectionName); if (it != sections.end()) { return it->second; } else { return IniFileSectionPtr(); } }
bool hasSection(const string §ionName) const { return sections.find(sectionName) != sections.end(); }
string getName() const { return name; }
void inspect() const { SectionMap::const_iterator it = sections.begin(); while (it != sections.end()) { cout << "[" << (it->first) << "]" << endl; it->second->inspect();
it++; } } };
} // namespace Passenger
#endif /* _PASSENGER_INI_FILE_H_ */
|