Initial commit
This commit is contained in:
256
ZUtil/ZIniReader.cpp
Normal file
256
ZUtil/ZIniReader.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
#include <ZUtil/ZIniReader.hpp>
|
||||
|
||||
#include <ZSTL/ZBasicStringAlgo.hpp>
|
||||
|
||||
bool ZIniReader::Read(const char* data, size_t length)
|
||||
{
|
||||
/*
|
||||
Algorithm:
|
||||
1) Read a line, ignore blank lines and comment lines
|
||||
2) If line is KVP
|
||||
2.1) No section? -> Error
|
||||
2.2) Section exists? -> Parse KVP
|
||||
2.2.1) Takes form of K = V? -> Add KVP
|
||||
2.2.2) Doesn't? -> Error
|
||||
3) Else if section
|
||||
3.1) If already seen section -> Error
|
||||
3.2) Create section hashtable, insert it
|
||||
4) Next line
|
||||
*/
|
||||
|
||||
ClearData();
|
||||
ErrorMessage.Clear();
|
||||
|
||||
ZHashMap<ZString, ZString>* currentSection = NULL;
|
||||
|
||||
size_t i = 0; //Current index into file data
|
||||
size_t lineStart = 0; //Start of line
|
||||
size_t lineLength;
|
||||
uint32_t lineNumber = 1;
|
||||
|
||||
//Parse loop
|
||||
while(i < length)
|
||||
{
|
||||
char c = data[i];
|
||||
i++;
|
||||
|
||||
//Treat final character as EOL too
|
||||
if(i < length)
|
||||
{
|
||||
//Split on \n or \r
|
||||
if(c != 0x0A && c != 0x0D)
|
||||
continue;
|
||||
else //Found EOL character
|
||||
{
|
||||
//Line length does NOT include EOL character
|
||||
lineLength = i - 1 - lineStart;
|
||||
}
|
||||
}
|
||||
else //Final line, don't remove extra character
|
||||
lineLength = i - lineStart;
|
||||
|
||||
//Line must be non-blank
|
||||
if(lineLength > 0 && data[lineStart] != '\n')
|
||||
{
|
||||
//Get pointer to start of line
|
||||
const char* line = &data[lineStart];
|
||||
|
||||
//Ignore comments
|
||||
if(line[0] != ';' && line [0] != '#')
|
||||
{
|
||||
//Section name
|
||||
if(line[0] == '[')
|
||||
{
|
||||
size_t j=1; //Start after '['
|
||||
//Read until closing '] or EOL
|
||||
while(j<lineLength && line[j] != ']')
|
||||
{
|
||||
if(!isalnum(line[j]) && line[j] != '_' && line[j] != ' ')
|
||||
{
|
||||
SetError("Header must contain alphanumeric, space, or _ only", lineNumber);
|
||||
return false;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
//Reached end of line but no terminator
|
||||
if(j == lineLength)
|
||||
{
|
||||
SetError("Missing closing \']\'", lineNumber);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Copy section name to ZString
|
||||
ZString sectionName(line+1, j-1);
|
||||
|
||||
//Check for existence
|
||||
if(StringSectionMap.ContainsKey(sectionName))
|
||||
{
|
||||
SetError("Section already exists", lineNumber);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Construct new table
|
||||
ZHashMap<ZString, ZString>* table = new(std::nothrow) ZHashMap<ZString, ZString>();
|
||||
if(table != NULL)
|
||||
{
|
||||
//Add mapping
|
||||
StringSectionMap.Put(sectionName, table);
|
||||
Sections.PushBack(table);
|
||||
SectionNames.PushBack(sectionName);
|
||||
|
||||
//Save this as the current section
|
||||
currentSection = table;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetError("Out of memory", lineNumber);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(!isspace(line[0])) //Not a space, not comment, not section -> KVP
|
||||
{
|
||||
//Did we find a section first?
|
||||
if(currentSection != NULL)
|
||||
{
|
||||
size_t j=0; //length of key
|
||||
|
||||
//Scan until space, equals sign, or EOL
|
||||
while(j<lineLength && line[j] != '=' && !isspace(line[j]))
|
||||
{
|
||||
if(!isalnum(line[j]) && line[j] != '_')
|
||||
{
|
||||
SetError("Key must contain alphanumeric or '_' only", lineNumber);
|
||||
return false;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
//Didn't find either a space or '='
|
||||
if(j == lineLength)
|
||||
{
|
||||
SetError("Missing '=' after key name", lineNumber);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Copy key to ZString
|
||||
ZString key(line, j);
|
||||
|
||||
if(line[j] != '=')
|
||||
{
|
||||
//Skip remaining whitespace
|
||||
while(j<lineLength && isspace(line[j]))
|
||||
j++;
|
||||
|
||||
if(j == lineLength || line[j] != '=')
|
||||
{
|
||||
SetError("Missing '=' after key name", lineNumber);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Now we definitely point to a '=' character. Ensure there is data after it
|
||||
if(j+1 == lineLength)
|
||||
{
|
||||
SetError("Expected value after '='", lineNumber);
|
||||
return false;
|
||||
}
|
||||
j++;
|
||||
|
||||
//Skip any whitespace after '='
|
||||
while(j<lineLength && isspace(line[j]))
|
||||
j++;
|
||||
|
||||
//Special case: if we hit EOL, then we have an empty value
|
||||
if(j == lineLength)
|
||||
{
|
||||
currentSection->Put(key, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
ZString val(&line[j], lineLength-j);
|
||||
currentSection->Put(key, val);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetError("Values must be in a section", lineNumber);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
SetError("Invalid line. Must be a section, comment, or value", lineNumber);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} //if(non-blank line)
|
||||
|
||||
//Check for \r \n pattern
|
||||
if(c == '\r') //we expect a \n, but verify
|
||||
{
|
||||
if(i < length && data[i] == '\n')
|
||||
i++;
|
||||
}
|
||||
|
||||
//Start on next line
|
||||
lineStart = i;
|
||||
lineNumber++;
|
||||
}
|
||||
|
||||
return !Sections.Empty();
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
const ZHashMap<ZString, ZString>* ZIniReader::GetSection(const ZString& sectionName)
|
||||
{
|
||||
ZHashMap<ZString, ZHashMap<ZString, ZString>* >::Iterator it = StringSectionMap.Find(sectionName);
|
||||
|
||||
if(it.HasCurrent())
|
||||
return it.GetValue();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZIniReader::SetError(const char* message, uint32_t line)
|
||||
{
|
||||
ZStringAlgo::BuildPrintf(ErrorMessage, "Line %u: %s", line, message);
|
||||
|
||||
//Clear all data
|
||||
ClearData();
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZIniReader::ClearData()
|
||||
{
|
||||
for(size_t i = 0; i < Sections.Size(); i++)
|
||||
delete Sections[i];
|
||||
|
||||
Sections.Clear();
|
||||
SectionNames.Clear();
|
||||
StringSectionMap.Clear();
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void ZIniReader::GetKVTree(ZKVTree& _kvtree)
|
||||
{
|
||||
for(size_t i = 0; i < Sections.Size(); i++)
|
||||
{
|
||||
ZHashMap<ZString, ZString>* hmap = Sections[i];
|
||||
ZHashMap<ZString, ZString>::Iterator it = hmap->Begin();
|
||||
|
||||
for(; it != hmap->End(); ++it)
|
||||
{
|
||||
ZString key = SectionNames[i]+"."+it.GetKey();
|
||||
ZString& value = it.GetValue();
|
||||
|
||||
_kvtree.Put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user