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.
509 lines
12 KiB
509 lines
12 KiB
#include "configmodel.h"
|
|
#include "sectionstore.h"
|
|
|
|
#include "cregex/cregex.h"
|
|
#include "containers/stringarray.h"
|
|
#include "containers/linearray.h"
|
|
|
|
#include <proto/exec.h>
|
|
#include <proto/dos.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#define ZERO ((BPTR)0)
|
|
|
|
cregex_program_t* InitialisePattern(CONST_STRPTR pattern);
|
|
Array RunPattern(CONST_STRPTR text, cregex_program_t* patternProgram);
|
|
|
|
|
|
SECTIONPTR LineGetSection(LINEPTR abstractLine);
|
|
VARIABLEPTR LineGetVariable(LINEPTR abstractLine);
|
|
VOID LineDump(LINEPTR abstractLine);
|
|
|
|
SECTIONPTR SectionCreateWithName(CONST_STRPTR primary);
|
|
SECTIONPTR SectionCreateWithNameAndSubname(CONST_STRPTR primary, CONST_STRPTR secondary);
|
|
VOID SectionFree(SECTIONPTR abstractSection);
|
|
|
|
VARIABLEPTR VariableCreate(CONST_STRPTR key, CONST_STRPTR rawValue);
|
|
VOID VariableFree(VARIABLEPTR abstractVariable);
|
|
|
|
|
|
#define WHITESPACE "[ \\t\\n\\r\\f\\v]"
|
|
#define RX_BLANK_LINE "^[ \t\n\r\f\v]*($|#|;)"
|
|
|
|
#define RX_SECTION_LINE "^[ \t\n\r\f\v]*\\[([a-z0-9]+)([ \t\n\r\f\v]*\"(.+)\")*\\][ \t\n\r\f\v]*($|#|;)"
|
|
#define RX_VARIABLE_LINE "^[ \t\n\r\f\v]*([a-z][a-z0-9]+)[ \t\n\r\f\v]*=[ \t\n\r\f\v]*(.+)[ \t\n\r\f\v]*($|#|;)"
|
|
#define RX_INTEGER "^-?[1-9][0-9]*$"
|
|
|
|
STATIC cregex_program_t* sectionPatternProgram = NULL;
|
|
STATIC cregex_program_t* variablePatternProgram = NULL;
|
|
STATIC cregex_program_t* blankPatternProgram = NULL;
|
|
STATIC cregex_program_t* integerPatternProgram = NULL;
|
|
|
|
struct ConfigFile
|
|
{
|
|
CONST_STRPTR filename;
|
|
LineArray lines;
|
|
SECTIONSTOREPTR sectionStore;
|
|
};
|
|
|
|
struct Section
|
|
{
|
|
CONST_STRPTR primary;
|
|
CONST_STRPTR secondary;
|
|
LineArray lines;
|
|
};
|
|
|
|
enum VariableType
|
|
{
|
|
TypeBool=0,
|
|
TypeInteger=1,
|
|
TypeString=2,
|
|
};
|
|
|
|
struct Variable
|
|
{
|
|
enum VariableType type;
|
|
CONST_STRPTR key;
|
|
union
|
|
{
|
|
CONST_STRPTR stringValue;
|
|
BOOL boolValue;
|
|
LONG longValue;
|
|
} value;
|
|
};
|
|
|
|
struct Line
|
|
{
|
|
STRPTR rawText;
|
|
struct Variable* variable;
|
|
struct Section* section;
|
|
};
|
|
|
|
|
|
VOID ConfigFileFree(CONFIGFILEPTR abstractConfigFile)
|
|
{
|
|
struct ConfigFile* configFile = (struct ConfigFile*)abstractConfigFile;
|
|
if( configFile != NULL )
|
|
{
|
|
if( configFile->lines != NULL )
|
|
{
|
|
LineArrayFree(configFile->lines, TRUE); //also frees lines
|
|
}
|
|
if( configFile->sectionStore != NULL )
|
|
{
|
|
SectionStoreFree(configFile->sectionStore);
|
|
}
|
|
FreeVec(configFile);
|
|
}
|
|
}
|
|
|
|
CONFIGFILEPTR ConfigFileRead(CONST_STRPTR filename)
|
|
{
|
|
struct ConfigFile* result = AllocVec(sizeof(struct ConfigFile), MEMF_CLEAR);
|
|
BPTR configFile = Open(filename, MODE_OLDFILE);
|
|
if( configFile != ZERO )
|
|
{
|
|
LINEPTR line = NULL;
|
|
SECTIONPTR currentSection = NULL;//SectionCreateWithName(""); // initial empty section
|
|
result->sectionStore = SectionStoreNew();
|
|
result->lines = LineArrayNew();
|
|
InitialisePatterns();
|
|
while( (line = LineReadIncludingContinuation(configFile, currentSection)) != NULL )
|
|
{
|
|
// add it to the flat list of lines
|
|
LineArrayAppend(result->lines, line);
|
|
LineDump(line);
|
|
|
|
if( LineGetSection(line) != NULL )
|
|
{
|
|
currentSection = LineGetSection(line);
|
|
}
|
|
}
|
|
ReleasePatterns();
|
|
Close(configFile);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
VOID ConfigFileSave(CONFIGFILEPTR config)
|
|
{
|
|
}
|
|
|
|
LINEPTR LineCreate(CONST_STRPTR buffer, ULONG size)
|
|
{
|
|
struct Line* result = NULL;
|
|
StringArray matches = NULL;
|
|
|
|
if( size > 0 )
|
|
{
|
|
result = AllocVec(sizeof(struct Line), MEMF_CLEAR);
|
|
result->rawText = AllocVec(size, MEMF_CLEAR);
|
|
CopyMem(buffer, result->rawText, size-1); // drop the \\n
|
|
|
|
if( matches = RunPattern(result->rawText, sectionPatternProgram) )
|
|
{
|
|
if( SizeOfArray(matches) == 3 )
|
|
{
|
|
result->section = SectionCreateWithName(StringArrayValues(matches)[1]);
|
|
}
|
|
else if( SizeOfArray(matches) == 5 )
|
|
{
|
|
result->section = SectionCreateWithNameAndSubname(StringArrayValues(matches)[1], StringArrayValues(matches)[3]);
|
|
}
|
|
StringArrayFree(matches);
|
|
}
|
|
else
|
|
{
|
|
|
|
if( matches = RunPattern(result->rawText, variablePatternProgram) )
|
|
{
|
|
result->variable = VariableCreate(StringArrayValues(matches)[1], StringArrayValues(matches)[2]);
|
|
StringArrayFree(matches);
|
|
}
|
|
else
|
|
{
|
|
if( matches = RunPattern(result->rawText, blankPatternProgram) )
|
|
{
|
|
StringArrayFree(matches);
|
|
}
|
|
else
|
|
{
|
|
Printf("\nDIDNT MATCH ANYTHING %s\n", result->rawText);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
return result;
|
|
}
|
|
|
|
VOID LineDump(LINEPTR abstractLine)
|
|
{
|
|
struct Line* line = (struct Line*)abstractLine;
|
|
if( line != NULL )
|
|
{
|
|
Printf("> %s\n", line->rawText);
|
|
if( line->variable == NULL && line->section == NULL )
|
|
{
|
|
Printf("(blank)\n");
|
|
}
|
|
else if( line->variable == NULL && line->section != NULL )
|
|
{
|
|
struct Section* section = (struct Section*)LineGetSection(abstractLine);
|
|
CONST_STRPTR canonical = NULL;
|
|
Printf("(new section %s", section->primary);
|
|
if( section->secondary )
|
|
{
|
|
Printf("[%s]", section->secondary);
|
|
}
|
|
Printf(")\n");
|
|
canonical = SectionCanonicalName(section);
|
|
Printf("...((%s))...\n", canonical);
|
|
FreeVec((STRPTR)canonical);
|
|
}
|
|
else
|
|
{
|
|
struct Section* section = (struct Section*)LineGetSection(abstractLine);
|
|
struct Variable* var = (struct Variable*)LineGetVariable(abstractLine);
|
|
Printf("(variable in section %s", section->primary);
|
|
if( section->secondary )
|
|
{
|
|
Printf("[%s]\n", section->secondary);
|
|
}
|
|
Printf(") %s = %s\n", var->key, var->value.stringValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
LINEPTR LineReadIncludingContinuation(BPTR file, SECTIONPTR currentSection)
|
|
{
|
|
UBYTE* buffer = AllocVec(512, MEMF_CLEAR);
|
|
ULONG bufLength = 512;
|
|
ULONG bytesReadTotal = 0;
|
|
UBYTE* read = NULL;
|
|
struct Line* result = NULL;
|
|
|
|
// read the whole line including continuation
|
|
do
|
|
{
|
|
read = FGets(file, &(buffer[bytesReadTotal]), bufLength-bytesReadTotal);
|
|
bytesReadTotal = strlen(buffer);
|
|
}
|
|
while( read != NULL && bytesReadTotal >= 2 && bytesReadTotal < bufLength && buffer[bytesReadTotal-1] == '\n' && buffer[bytesReadTotal-2] == '\\' );
|
|
|
|
// make a line
|
|
if( bytesReadTotal > 0 )
|
|
{
|
|
result = LineCreate(buffer, bytesReadTotal);
|
|
if( result->variable != NULL && result->section == NULL )
|
|
{
|
|
result->section = currentSection;
|
|
}
|
|
}
|
|
|
|
FreeVec(buffer);
|
|
return result;
|
|
}
|
|
|
|
SECTIONPTR LineGetSection(LINEPTR abstractLine)
|
|
{
|
|
struct Line* line = (struct Line*)abstractLine;
|
|
if( line != NULL )
|
|
{
|
|
return line->section;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
VARIABLEPTR LineGetVariable(LINEPTR abstractLine)
|
|
{
|
|
struct Line* line = (struct Line*)abstractLine;
|
|
if( line != NULL )
|
|
{
|
|
return line->variable;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
VOID LineFree(LINEPTR abstractLine)
|
|
{
|
|
struct Line* line = (struct Line*)abstractLine;
|
|
if( line != NULL )
|
|
{
|
|
if( line->rawText != NULL )
|
|
{
|
|
FreeVec(line->rawText);
|
|
}
|
|
if( line->section != NULL && line->variable == NULL )
|
|
{
|
|
//only free the section in a section line,
|
|
//not a variable line as thats just a weak ref
|
|
SectionFree(line->section);
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
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->primary = AllocVec(length+1, MEMF_CLEAR);
|
|
CopyMem(primary, (STRPTR)result->primary, length);
|
|
|
|
if( secondary != NULL )
|
|
{
|
|
ULONG length = strlen(secondary);
|
|
result->secondary = AllocVec(length+1, MEMF_CLEAR);
|
|
CopyMem(secondary, (STRPTR)result->secondary, length);
|
|
}
|
|
|
|
result->lines = LineArrayNew();
|
|
}
|
|
|
|
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, FALSE); // dont free the lines they are only weak
|
|
}
|
|
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;
|
|
if(section->primary != NULL)
|
|
{
|
|
primaryLength = strlen(section->primary);
|
|
}
|
|
if(section->secondary != NULL)
|
|
{
|
|
secondaryLength = strlen(section->secondary);
|
|
}
|
|
result = AllocVec(primaryLength+1+secondaryLength+1, MEMF_CLEAR);
|
|
CopyMem(section->primary, result, primaryLength);
|
|
result[primaryLength] = '.';
|
|
CopyMem(section->secondary, result+primaryLength+1, secondaryLength);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
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->type = TypeString;
|
|
if( rawValue != NULL )
|
|
{
|
|
ULONG length = strlen(rawValue);
|
|
result->value.stringValue = AllocVec(length+1, MEMF_CLEAR);
|
|
CopyMem(rawValue, (STRPTR)result->value.stringValue, length);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
VOID VariableFree(VARIABLEPTR abstractVariable)
|
|
{
|
|
struct Variable* variable = (struct Variable*)abstractVariable;
|
|
if( variable != NULL )
|
|
{
|
|
if( variable->key != NULL )
|
|
{
|
|
FreeVec((STRPTR)variable->key);
|
|
}
|
|
if( variable->type == TypeString && variable->value.stringValue != NULL )
|
|
{
|
|
FreeVec((STRPTR)variable->value.stringValue);
|
|
}
|
|
FreeVec(variable);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------
|
|
// ----------------------------------------------------
|
|
// ----------------------------------------------------
|
|
// ----------------------------------------------------
|
|
// ----------------------------------------------------
|
|
VOID InitialisePatterns(VOID)
|
|
{
|
|
sectionPatternProgram = InitialisePattern(RX_SECTION_LINE);
|
|
variablePatternProgram = InitialisePattern(RX_VARIABLE_LINE);
|
|
blankPatternProgram = InitialisePattern(RX_BLANK_LINE);
|
|
integerPatternProgram = InitialisePattern(RX_INTEGER);
|
|
}
|
|
|
|
VOID ReleasePatterns(VOID)
|
|
{
|
|
if( sectionPatternProgram != NULL ) cregex_compile_free( sectionPatternProgram );
|
|
if( variablePatternProgram != NULL ) cregex_compile_free( variablePatternProgram );
|
|
if( blankPatternProgram != NULL ) cregex_compile_free( blankPatternProgram );
|
|
if( integerPatternProgram != NULL ) cregex_compile_free( integerPatternProgram );
|
|
}
|
|
|
|
cregex_program_t* InitialisePattern(CONST_STRPTR pattern)
|
|
{
|
|
cregex_program_t* result = NULL;
|
|
cregex_node_t* patternNode = cregex_parse(pattern);
|
|
if( patternNode )
|
|
{
|
|
result = cregex_compile_node( patternNode );
|
|
if( result != NULL )
|
|
{
|
|
//Printf("successfully compiled %s\n", pattern);
|
|
}
|
|
else
|
|
{
|
|
Printf("failed to compile %s\n", pattern);
|
|
}
|
|
cregex_parse_free( patternNode );
|
|
}
|
|
else
|
|
{
|
|
Printf("could not parse %s\n", pattern);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Array RunPattern(CONST_STRPTR text, cregex_program_t* patternProgram)
|
|
{
|
|
Array result = NULL;
|
|
char* localMatches[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
|
if (cregex_program_run(patternProgram, text, localMatches, 20) > 0) {
|
|
int j = 0;
|
|
int nmatches = 0;
|
|
|
|
// count the matches
|
|
for (j = 0; j < 20; ++j)
|
|
if (localMatches[j])
|
|
nmatches = j;
|
|
|
|
if( nmatches > 0 )
|
|
{
|
|
result = StringArrayNew();
|
|
// loop the matches
|
|
for (j = 0; j <= nmatches; j += 2) {
|
|
if (localMatches[j] && localMatches[j + 1]) {
|
|
int len = (int)(localMatches[j + 1] - localMatches[j]);
|
|
STRPTR buffer = AllocVec(len+1, MEMF_CLEAR); // freed in the array
|
|
sprintf(buffer, "%.*s", len, localMatches[j]);
|
|
if( buffer[len-1] == '\n' )
|
|
{
|
|
buffer[len-1] = '\0';
|
|
}
|
|
StringArrayAppend(result, buffer);
|
|
} else {
|
|
//Printf("(NULL,NULL)\n");
|
|
}
|
|
}
|
|
}
|
|
// end
|
|
} else {
|
|
//Printf("\"%s\": no match\n", text);
|
|
}
|
|
return result;
|
|
}
|
|
|