You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
configreader/configmodel.c

682 lines
17 KiB

#include "configmodel.h"
#include "containers/linearray.h"
#include "containers/sectionarray.h"
#include "containers/stringarray.h"
#include <proto/exec.h>
#include <proto/dos.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
STATIC VOID downcaseString(STRPTR string);
struct Section
{
CONST_STRPTR primary;
CONST_STRPTR secondary;
LineArray lines;
ULONG insertPos; // we want to add new entries at end, but before any blanks
};
struct Variable
{
CONST_STRPTR key;
CONST_STRPTR normalizedKey;
CONST_STRPTR stringValue;
};
struct Line
{
STRPTR rawText;
struct Variable* variable;
struct Section* section;
};
// ---------------------------------------------------------------------------------------
// - LINE --------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
LINEPTR LineNew(CONST_STRPTR buffer)
{
struct Line* result = NULL;
if( buffer != 0 )
{
result = AllocVec(sizeof(struct Line), MEMF_CLEAR);
result->rawText = AllocVec(strlen(buffer)+1, MEMF_CLEAR);
CopyMem(buffer, result->rawText, strlen(buffer));
}
return result;
}
STATIC VOID lineDumpRecreate(LINEPTR abstractLine);
VOID LineWrite(LINEPTR abstractLine, BPTR file)
{
struct Line* line = (struct Line*)abstractLine;
if( line != NULL )
{
if( line->rawText != NULL )
{
Write(file, line->rawText, strlen(line->rawText));
}
}
}
VOID LineDump(LINEPTR abstractLine)
{
struct Line* line = (struct Line*)abstractLine;
if( line != NULL )
{
if( line->rawText != NULL )
{
Printf("%s", line->rawText);
}
}
}
STATIC VOID lineDumpRecreate(LINEPTR abstractLine)
{
struct Line* line = (struct Line*)abstractLine;
if( line != NULL )
{
if( line->variable == NULL && line->section == NULL )
{
// blank
Printf("# blank line\n");
}
else if( line->variable == NULL && line->section != NULL )
{
// section
struct Section* section = (struct Section*)LineGetSection(abstractLine);
CONST_STRPTR sectionText = SectionSerialize(section);
Printf(sectionText);
FreeVec((STRPTR)sectionText);
}
else
{
struct Variable* var = (struct Variable*)LineGetVariable(abstractLine);
CONST_STRPTR varText = VariableSerialize(var);
Printf(varText);
FreeVec((STRPTR)varText);
}
}
}
VOID LineSetSection(LINEPTR abstractLine, SECTIONPTR abstractSection)
{
struct Line* line = (struct Line*)abstractLine;
if( line != NULL )
{
// we dont own the section
line->section = abstractSection;
}
}
SECTIONPTR LineGetSection(LINEPTR abstractLine)
{
struct Line* line = (struct Line*)abstractLine;
if( line != NULL )
{
return line->section;
}
return NULL;
}
// first time through we need to preserve rawText
VOID LineSetInitialVariable(LINEPTR abstractLine, VARIABLEPTR abstractVariable)
{
struct Line* line = (struct Line*)abstractLine;
if( line != NULL )
{
// we take ownership of the variable
if( line->variable != NULL )
{
VariableFree(line->variable);
}
line->variable = abstractVariable;
}
}
// any further change to the variable means we wipe the raw text
VOID LineSetVariable(LINEPTR abstractLine, VARIABLEPTR abstractVariable)
{
struct Line* line = (struct Line*)abstractLine;
if( line != NULL )
{
// we take ownership of the variable
if( line->variable != NULL )
{
VariableFree(line->variable);
}
line->variable = abstractVariable;
if( line->rawText != NULL )
{
// if we change the variable we remove any original text
FreeVec(line->rawText);
}
line->rawText = (STRPTR)VariableSerialize(line->variable);
}
}
VARIABLEPTR LineGetVariable(LINEPTR abstractLine)
{
struct Line* line = (struct Line*)abstractLine;
if( line != NULL )
{
return line->variable;
}
return NULL;
}
BOOL LineHasVariable(LINEPTR line, CONST_STRPTR varKey)
{
VARIABLEPTR var = LineGetVariable(line);
return (BOOL)(var != NULL && VariableHasKey(var, varKey));
}
VOID LineFree(LINEPTR abstractLine)
{
struct Line* line = (struct Line*)abstractLine;
if( line != NULL )
{
if( line->rawText != NULL )
{
FreeVec(line->rawText);
}
if( line->variable != NULL )
{
VariableFree(line->variable);
}
FreeVec(line);
}
}
CONST_STRPTR LineGetRawText(LINEPTR abstractLine)
{
struct Line* line = (struct Line*)abstractLine;
if( line != NULL )
{
return (CONST_STRPTR)line->rawText;
}
else
{
return NULL;
}
}
BOOL LineIsEmpty(LINEPTR abstractLine)
{
struct Line* line = (struct Line*)abstractLine;
if( line != NULL )
{
STRPTR start = line->rawText;
STRPTR end = start + strlen(line->rawText);
while( start != end )
{
if(!isspace(*start))
{
return FALSE;
}
start++;
}
}
return TRUE;
}
// ---------------------------------------------------------------------------------------
// - SECTION -----------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
SECTIONPTR SectionCreateWithName(CONST_STRPTR primary)
{
return SectionCreateWithNameAndSubname(primary, NULL);
}
SECTIONPTR SectionCreateWithNameAndSubname(CONST_STRPTR primary, CONST_STRPTR secondary)
{
struct Section* result = NULL;
if( primary != NULL )
{
ULONG length = strlen(primary);
result = AllocVec(sizeof(struct Section), MEMF_CLEAR);
result->insertPos = 0;
result->primary = AllocVec(length+1, MEMF_CLEAR);
CopyMem(primary, (STRPTR)result->primary, length);
if( secondary != NULL && strlen(secondary) > 0 )
{
ULONG length = strlen(secondary);
result->secondary = AllocVec(length+1, MEMF_CLEAR);
CopyMem(secondary, (STRPTR)result->secondary, length);
}
result->lines = LineArrayNew();
}
return result;
}
BOOL SectionHasName(SECTIONPTR abstractSection, CONST_STRPTR primary)
{
struct Section* section = (struct Section*)abstractSection;
return (BOOL)(strcmp(primary, section->primary) == 0);
}
CONST_STRPTR SectionGetRawSubsectionName(SECTIONPTR abstractSection)
{
struct Section* section = (struct Section*)abstractSection;
return section->secondary;
}
VOID SectionAddSectionLine(SECTIONPTR abstractSection, LINEPTR abstractLine)
{
// sets self onto line and then adds line to the collection
LineSetSection(abstractLine, abstractSection);
SectionAddLine(abstractSection, abstractLine, FALSE);
}
// we need to know if there are blank lines at the end and if this is a variable,
//insert this before them
//
// otherwise we go from
// [foo]
// bar = 1
//
// [baz]
// etc
// to
//
// [foo]
// bar = 1
//
// fubar = 1
// [baz]
// etc
VOID SectionAddLine(SECTIONPTR abstractSection, LINEPTR abstractLine, BOOL addBeforeBlanks)
{
struct Section* section = (struct Section*)abstractSection;
if( section != NULL )
{
if( addBeforeBlanks==FALSE || section->insertPos == SizeOfArray(section->lines) )
{
LineArrayAppend(section->lines, abstractLine);
if(!LineIsEmpty(abstractLine) )
{
section->insertPos = SizeOfArray(section->lines);
}
}
else
{
LineArrayInsert(section->lines, abstractLine, section->insertPos);
section->insertPos += 1;
}
}
}
VOID SectionCollectLinesForVariable(SECTIONPTR abstractSection, CONST_STRPTR varKey, LineArray collecting)
{
struct Section* section = (struct Section*)abstractSection;
if( section != NULL )
{
ULONG lineCount = SizeOfArray(section->lines);
ULONG index = 0;
STRPTR normalizedKey = AllocVec(strlen(varKey)+1, MEMF_CLEAR);
CopyMem(varKey, normalizedKey, strlen(varKey));
downcaseString(normalizedKey);
for( index = 0; index < lineCount; index++ )
{
LINEPTR line = LineArrayValues(section->lines)[index];
if( LineHasVariable(line, normalizedKey) )
{
LineArrayAppend(collecting, line);
}
}
FreeVec(normalizedKey);
}
else
{
Printf("null section\n");
}
}
VOID SectionRemoveLinesForVariable(SECTIONPTR abstractSection, CONST_STRPTR varKey)
{
struct Section* section = (struct Section*)abstractSection;
if( section != NULL )
{
ULONG lineCount = SizeOfArray(section->lines);
ULONG index = 0;
ULONG newInsertPos = 0;
LineArray newLineArray = LineArrayNew();
STRPTR normalizedKey = AllocVec(strlen(varKey)+1, MEMF_CLEAR);
CopyMem(varKey, normalizedKey, strlen(varKey));
downcaseString(normalizedKey);
for( index = 0; index < lineCount; index++ )
{
// instead of compacting, we save lines that dont match, and free lines that do
LINEPTR line = LineArrayValues(section->lines)[index];
if( LineHasVariable(line, normalizedKey) )
{
LineFree(line);
LineArrayValues(section->lines)[index] = NULL; // dont crash when we call LineArrayFree
}
else
{
LineArrayAppend(newLineArray, line);
if( !LineIsEmpty(line) )
{
// keep track of where we can insert before blanks
newInsertPos++;
}
}
}
LineArrayFree(section->lines, FALSE);//dont free the lines, the newArray will do that later
section->lines = newLineArray;
section->insertPos = newInsertPos;
FreeVec(normalizedKey);
}
}
VOID SectionWrite(SECTIONPTR abstractSection, BPTR file)
{
struct Section* section = (struct Section*)abstractSection;
if( section != NULL && section->lines != NULL )
{
ArrayForEach(LINEPTR, aLine, section->lines, LineWrite(aLine,file););
}
}
VOID SectionDump(SECTIONPTR abstractSection)
{
struct Section* section = (struct Section*)abstractSection;
if( section != NULL && section->lines != NULL )
{
ArrayForEach(LINEPTR, aLine, section->lines, LineDump(aLine););
}
}
CONST_STRPTR SectionSerialize(SECTIONPTR abstractSection)
{
struct Section* section = (struct Section*)abstractSection;
STRPTR result = NULL;
ULONG size = 0;
if( section != NULL && section->primary != NULL )
{
if( strlen(section->primary) > 0 )
{
size += 1; // [
size += strlen(section->primary);
if( section->secondary != NULL )
{
size += 2; // ' \"'
size += strlen(section->secondary);
size += 1; // \"
}
size += 2; // ]\n
result = AllocVec(size+1, MEMF_CLEAR);
if( section->secondary == NULL )
{
sprintf(result, "[%s]\n", section->primary);
}
else
{
sprintf(result, "[%s \"%s\"]\n", section->primary, section->secondary);
}
}
}
return result;
}
VOID SectionFree(SECTIONPTR abstractSection)
{
struct Section* section = (struct Section*)abstractSection;
if( section != NULL )
{
if( section->primary != NULL )
{
FreeVec((STRPTR)section->primary);
}
if( section->secondary != NULL )
{
FreeVec((STRPTR)section->secondary);
}
if( section->lines != NULL )
{
LineArrayFree(section->lines, TRUE); // free the lines when we free the section
}
FreeVec(section);
}
}
CONST_STRPTR SectionCanonicalName(SECTIONPTR abstractSection)
{
struct Section* section = (struct Section*)abstractSection;
STRPTR result = NULL;
if(section != NULL)
{
ULONG primaryLength = 0;
ULONG secondaryLength = 0;
ULONG totalLength = 0;
if(section->primary != NULL)
{
primaryLength = strlen(section->primary);
}
if(section->secondary != NULL && strlen(section->secondary)>0)
{
secondaryLength = strlen(section->secondary);
}
totalLength += primaryLength;
if( secondaryLength > 0 )
{
totalLength += 1; // '.'
totalLength += secondaryLength;
}
result = AllocVec(totalLength+1, MEMF_CLEAR);
CopyMem(section->primary, result, primaryLength);
if( secondaryLength > 0 )
{
result[primaryLength] = '.';
CopyMem(section->secondary, result+primaryLength+1, secondaryLength);
}
result[totalLength] = '\0';
}
return result;
}
// ---------------------------------------------------------------------------------------
// - VARIABLE ----------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
VARIABLEPTR VariableCreate(CONST_STRPTR key, CONST_STRPTR rawValue)
{
struct Variable* result = NULL;
if( key != NULL )
{
ULONG length = strlen(key);
result = AllocVec(sizeof(struct Variable), MEMF_CLEAR);
result->key = AllocVec(length+1, MEMF_CLEAR);
CopyMem(key, (STRPTR)result->key, length);
result->normalizedKey = AllocVec(length+1, MEMF_CLEAR);
CopyMem(key, (STRPTR)result->normalizedKey, length);
downcaseString((STRPTR)result->normalizedKey);
}
if( rawValue != NULL )
{
ULONG length = strlen(rawValue);
result->stringValue = AllocVec(length+1, MEMF_CLEAR);
CopyMem(rawValue, (STRPTR)result->stringValue, length);
}
return result;
}
CONST_STRPTR VariableGetRawValue(VARIABLEPTR abstractVariable)
{
struct Variable* variable = (struct Variable*)abstractVariable;
if( variable != NULL )
{
return variable->stringValue;
}
return NULL;
}
BOOL VariableHasKey(VARIABLEPTR abstractVariable, CONST_STRPTR varKey)
{
struct Variable* variable = (struct Variable*)abstractVariable;
if( variable != NULL )
{
if( strcmp(varKey, variable->normalizedKey) == 0 )
{
return TRUE;
}
}
return FALSE;
}
VOID VariableFree(VARIABLEPTR abstractVariable)
{
struct Variable* variable = (struct Variable*)abstractVariable;
if( variable != NULL )
{
if( variable->key != NULL )
{
FreeVec((STRPTR)variable->key);
}
if( variable->normalizedKey != NULL )
{
FreeVec((STRPTR)variable->normalizedKey);
}
if( variable->stringValue != NULL )
{
FreeVec((STRPTR)variable->stringValue);
}
FreeVec(variable);
}
}
CONST_STRPTR VariableSerialize(VARIABLEPTR abstractVariable)
{
STRPTR result = NULL;
struct Variable* variable = (struct Variable*)abstractVariable;
if( variable != NULL && variable->normalizedKey != NULL && variable->stringValue != NULL )
{
ULONG size = 1 + strlen(variable->normalizedKey) + 3 + strlen(variable->stringValue) + 1 + 1;
result = AllocVec(size, MEMF_CLEAR);
sprintf(result, "\t%s = %s\n", variable->normalizedKey, variable->stringValue);
}
return (CONST_STRPTR)result;
}
// a key is "foo.bar.baz.blah" where "foo" is the primary section name
// "bar.baz" is the secondary section name and "blah" is the variable name.
// this splits foo.bar.baz.blah into foo, bar.baz amd blah.
// section, subsection and variable.
// we'll get empty strings for the parts that arent present
StringArray CompoundKeySplitKeyCompletely(CONST_STRPTR key)
{
StringArray result = StringArrayNew();
StringArray parts = StringArrayNew();
ULONG numberOfParts = 0;
STRPTR token = NULL;
// we need to make a copy of the key because strtok modifies it.
STRPTR keyCopy = AllocVec(strlen(key)+1, MEMF_CLEAR);
CopyMem(key, keyCopy, strlen(key));
token = strtok(keyCopy, ".");
while (token != NULL)
{
StringArrayAppendAndRetain(parts, token);
token = strtok(NULL, ".");
}
FreeVec(keyCopy);
// now we join all but the first and last part
numberOfParts = SizeOfArray(parts);
if( numberOfParts == 1 ) //just a variable ["","","var"]
{
StringArrayAppendAndRetain(result, "");
StringArrayAppendAndRetain(result, "");
StringArrayAppendAndRetain(result, (STRPTR)ArrayBackValue(STRPTR, parts));
}
else if( numberOfParts == 2 ) // section and variable ["section","","var"]
{
StringArrayAppendAndRetain(result, StringArrayValues(parts)[0]);
StringArrayAppendAndRetain(result, "");
StringArrayAppendAndRetain(result, StringArrayValues(parts)[1]);
}
else if( numberOfParts == 3 ) // section and subsection and variable ["section","subsec","var"]
{
StringArrayAppendAndRetain(result, StringArrayValues(parts)[0]);
StringArrayAppendAndRetain(result, StringArrayValues(parts)[1]);
StringArrayAppendAndRetain(result, StringArrayValues(parts)[2]);
}
else if( numberOfParts > 3 ) // subsections needs dotted ["section", "subsec1.subsec2", "var""]
{
StringArrayAppendAndRetain(result, StringArrayValues(parts)[0]); //section
// start at index 1, and add (size -2 (start+end)) parts
StringArrayAppend(result, StringArrayJoinedParts(parts, '.', 1, SizeOfArray(parts)-2));
StringArrayAppendAndRetain(result, (STRPTR)ArrayBackValue(STRPTR, parts)); // variable
}
StringArrayFree(parts, TRUE);
return result;
}
StringArray CompoundKeySplitKeyForVar(CONST_STRPTR key)
{
// a key is "foo.bar.baz.blah" where "foo" is the primary section name
// "bar.baz" is the secondary section name and "blah" is the variable name.
// In this case we'll just break it down into the canonical section name,
// foo.bar.baz and the variable name blah.
StringArray result = StringArrayNew();
StringArray parts = StringArrayNew();
STRPTR sectionPart = NULL;
STRPTR varPart = NULL;
STRPTR token = NULL;
// we need to make a copy of the key because strtok modifies it.
STRPTR keyCopy = AllocVec(strlen(key)+1, MEMF_CLEAR);
CopyMem(key, keyCopy, strlen(key));
token = strtok(keyCopy, ".");
while (token != NULL)
{
StringArrayAppendAndRetain(parts, token);
token = strtok(NULL, ".");
}
FreeVec(keyCopy);
// now we join all but the last part
sectionPart = StringArrayJoinedParts(parts, '.', 0, SizeOfArray(parts)-1);
StringArrayAppend(result, sectionPart); // its been alloced so dont copy
varPart = (STRPTR)ArrayBackValue(STRPTR, parts);
StringArrayAppendAndRetain(result, varPart); // its a reference so copy
StringArrayFree(parts, TRUE);
return result;
}
STATIC VOID downcaseString(STRPTR string)
{
BYTE* p = NULL;
for(p=string; *p; p++)
{
*p=tolower(*p);
}
}