#include "types.h" #include "configfile.h" #include "configmodel.h" #include "sectionstore.h" #include "containers/stringarray.h" #include "containers/sectionarray.h" #include "containers/linearray.h" #include #include #include // #include // #include // #include #define ZERO ((BPTR)0) STATIC LINEPTR configFileReadLine(BPTR file); // - STRUCTS ----------------------------------------------------------------------------- struct ConfigFile { CONST_STRPTR filename; SECTIONSTOREPTR sectionStore; }; // --------------------------------------------------------------------------------------- // - CONFIG FILE ------------------------------------------------------------------------- // --------------------------------------------------------------------------------------- VOID ConfigFileFree(CONFIGFILEPTR abstractConfigFile) { struct ConfigFile* configFile = (struct ConfigFile*)abstractConfigFile; if( configFile != NULL ) { if( configFile->sectionStore != NULL ) { SectionStoreFree(configFile->sectionStore); } FreeVec(configFile); } } CONFIGFILEPTR ConfigFileRead(CONST_STRPTR filename) { struct ConfigFile* result = NULL; BPTR configFile = Open(filename, MODE_OLDFILE); if( configFile != ZERO ) { LINEPTR line = NULL; result = AllocVec(sizeof(struct ConfigFile), MEMF_CLEAR); result->sectionStore = SectionStoreNew(); while( (line = configFileReadLine(configFile)) != NULL ) { SectionStoreAddLineToCurrentSection(result->sectionStore, line); //LineDump(line); } Close(configFile); } return 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 ConfigFileSplitKeyCompletely(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 ConfigFileSplitKeyForVar(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; } STRPTR ConfigFileGet(CONFIGFILEPTR abstractConfigFile, CONST_STRPTR compoundKey) { STRPTR result = NULL; struct ConfigFile* configFile = (struct ConfigFile*)abstractConfigFile; if( configFile != NULL ) { StringArray split = ConfigFileSplitKeyForVar(compoundKey); VARIABLEPTR var = SectionStoreGet(configFile->sectionStore, StringArrayValues(split)[0], StringArrayValues(split)[1]); if( var != NULL ) { CONST_STRPTR value = VariableGetRawValue(var); result = AllocVec(strlen(value)+1, MEMF_CLEAR); CopyMem(value, result, strlen(value)); } StringArrayFree(split, TRUE); } return result; } StringArray ConfigFileGetAll(CONFIGFILEPTR abstractConfigFile, CONST_STRPTR compoundKey) { StringArray result = StringArrayNew(); ULONG index = 0; struct ConfigFile* configFile = (struct ConfigFile*)abstractConfigFile; if( configFile != NULL ) { StringArray split = ConfigFileSplitKeyForVar(compoundKey); VariableArray vars = SectionStoreGetAll(configFile->sectionStore, StringArrayValues(split)[0], StringArrayValues(split)[1]); for( index = 0; index < SizeOfArray(vars); index++ ) { StringArrayAppendAndRetain(result, VariableGetRawValue(VariableArrayValues(vars)[index])); } VariableArrayFree(vars, FALSE); StringArrayFree(split, TRUE); } return result; } VOID ConfigFileAddVariable(CONFIGFILEPTR abstractConfigFile, CONST_STRPTR compoundKey, CONST_STRPTR stringValue) { struct ConfigFile* configFile = (struct ConfigFile*)abstractConfigFile; if( configFile != NULL ) { SECTIONPTR section = NULL; VARIABLEPTR var = NULL; CONST_STRPTR varLine = NULL; LINEPTR line = NULL; StringArray canonicalParts = ConfigFileSplitKeyForVar(compoundKey); var = VariableCreate(StringArrayValues(canonicalParts)[1], stringValue); varLine = VariableSerialize(var); line = LineNew(varLine); LineSetInitialVariable(line, var); FreeVec((STRPTR)varLine); StringArrayFree(canonicalParts, TRUE); // now get the section section = SectionStoreFindOrCreateSection(configFile->sectionStore, compoundKey); // add the line to the section SectionAddLine(section, line, TRUE); } } BOOL ConfigFileSet(CONFIGFILEPTR abstractConfigFile, CONST_STRPTR compoundKey, CONST_STRPTR stringValue) { BOOL result = FALSE; struct ConfigFile* configFile = (struct ConfigFile*)abstractConfigFile; if( configFile != NULL ) { StringArray parts = ConfigFileSplitKeyForVar(compoundKey); LineArray lines = SectionStoreFindLines(configFile->sectionStore, StringArrayValues(parts)[0], StringArrayValues(parts)[1]); if( SizeOfArray(lines) == 0 ) { ConfigFileAddVariable(abstractConfigFile, compoundKey, stringValue); result = TRUE; } else if( SizeOfArray(lines) == 1 ) { LINEPTR line = (LINEPTR)ArrayBackValue(LINEPTR, lines); if( line ) { VARIABLEPTR var = VariableCreate(StringArrayValues(parts)[1], stringValue); if( var ) { LineSetVariable(line, var); result = TRUE; } } } else // more than one line, we error. { result = FALSE; } StringArrayFree(parts, TRUE); LineArrayFree(lines, FALSE); } return result; } VOID ConfigFileReplaceAll(CONFIGFILEPTR abstractConfigFile, CONST_STRPTR compoundKey, CONST_STRPTR stringValue) { struct ConfigFile* configFile = (struct ConfigFile*)abstractConfigFile; if( configFile != NULL ) { StringArray parts = ConfigFileSplitKeyForVar(compoundKey); SectionStoreRemoveLines(configFile->sectionStore, StringArrayValues(parts)[0], StringArrayValues(parts)[1]); ConfigFileAddVariable( abstractConfigFile, compoundKey, stringValue); StringArrayFree(parts, TRUE); } } VOID ConfigFileUnset(CONFIGFILEPTR abstractConfigFile, CONST_STRPTR compoundKey, CONST_STRPTR stringValue) { struct ConfigFile* configFile = (struct ConfigFile*)abstractConfigFile; if( configFile != NULL ) { StringArray parts = ConfigFileSplitKeyCompletely(compoundKey); StringArrayFree(parts, TRUE); } } VOID ConfigFileUnsetAll(CONFIGFILEPTR abstractConfigFile, CONST_STRPTR compoundKey, CONST_STRPTR stringValue) { struct ConfigFile* configFile = (struct ConfigFile*)abstractConfigFile; if( configFile != NULL ) { StringArray parts = ConfigFileSplitKeyCompletely(compoundKey); StringArrayFree(parts, TRUE); } } VOID ConfigFileDump(CONFIGFILEPTR abstractConfigFile) { ULONG index = 0; ULONG count = 0; struct ConfigFile* configFile = (struct ConfigFile*)abstractConfigFile; if( configFile != NULL ) { count = SectionStoreSectionCount(configFile->sectionStore); for( index = 0; index < count; index++ ) { SECTIONPTR section = SectionStoreSectionAt(configFile->sectionStore, index); SectionDump(section); } } } STATIC LINEPTR configFileReadLine(BPTR file) { 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 ) { buffer[bytesReadTotal] = '\0'; result = LineNew(buffer); } FreeVec(buffer); return result; }