#include "sectionstore.h" #include "containers/sectionmap.h" #include "containers/sectionarray.h" #include "containers/stringarray.h" #include "containers/linearray.h" #include "cregex/pattern.h" #include "configfile.h" #include #include #include #include #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]*$" #define RX_BLANK_LINE "^[ \t\n\r\f\v]*($|#|;)" STATIC SECTIONPTR parseSection(LINEPTR line, PATTERNPTR sectionPatternProgram); STATIC VARIABLEPTR parseVariable(LINEPTR line, PATTERNPTR variablePatternProgram); STATIC BOOL parseBlank(LINEPTR line, PATTERNPTR blankPatternProgram); //STATIC BOOL parseBoolValue(CONST_STRPTR value, BOOL* outBool); //STATIC BOOL parseIntegerValue(CONST_STRPTR value, LONG* outInt, PATTERNPTR integerProgram); struct SectionStore { SectionMap map; SectionArray array; // here so we only create them once not each line PATTERNPTR sectionPatternProgram; PATTERNPTR variablePatternProgram; PATTERNPTR blankPatternProgram; PATTERNPTR integerPatternProgram; }; SECTIONSTOREPTR SectionStoreNew(VOID) { struct SectionStore* result = AllocVec(sizeof(struct SectionStore), MEMF_CLEAR); result->map = SectionMapNew(); result->array = SectionArrayNew(); result->sectionPatternProgram = PatternNew(RX_SECTION_LINE); result->variablePatternProgram = PatternNew(RX_VARIABLE_LINE); result->blankPatternProgram = PatternNew(RX_BLANK_LINE); result->integerPatternProgram = PatternNew(RX_INTEGER); return result; } VOID SectionStoreFree(SECTIONSTOREPTR abstractSectionStore) { if( abstractSectionStore != NULL ) { struct SectionStore* store = (struct SectionStore*)abstractSectionStore; if( store->map != NULL ) SectionMapFree( store->map ); if( store->array != NULL ) SectionArrayFree( store->array, TRUE );//free teh sections if( store->sectionPatternProgram != NULL ) PatternFree(store->sectionPatternProgram); if( store->variablePatternProgram != NULL ) PatternFree(store->variablePatternProgram); if( store->blankPatternProgram != NULL ) PatternFree(store->blankPatternProgram); if( store->integerPatternProgram != NULL ) PatternFree(store->integerPatternProgram); FreeVec(store); } } VOID SectionStoreAddSection(SECTIONSTOREPTR abstractSectionStore, SECTIONPTR section) { struct SectionStore* store = (struct SectionStore*)abstractSectionStore; if( store != NULL && section != NULL ) { CONST_STRPTR canonicalName = SectionCanonicalName(section); if( canonicalName != NULL ) { // the array acts as storage as it can free its contents SectionArrayAppend(store->array, section); // the map will copy the string key and free it, but not the section SectionMapSet(store->map, canonicalName, section); //free the canonical name as the map has copied it. FreeVec((STRPTR)canonicalName); } } } SECTIONPTR SectionStoreGetSection(SECTIONSTOREPTR abstractSectionStore, CONST_STRPTR canonicalName) { struct SectionStore* store = (struct SectionStore*)abstractSectionStore; if( store != NULL && canonicalName != NULL ) { return SectionMapGet(store->map, canonicalName); } } SECTIONPTR SectionStoreCurrentSection(SECTIONSTOREPTR abstractSectionStore) { struct SectionStore* store = (struct SectionStore*)abstractSectionStore; SECTIONPTR result = NULL; if( store != NULL && SizeOfArray(store->array) > 0 ) { result = ArrayBackValue(SECTIONPTR, store->array); } return result; } VOID SectionStoreRemoveLines(SECTIONSTOREPTR sectionStore, CONST_STRPTR canonicalSectionName, CONST_STRPTR varKey) { SECTIONPTR section = SectionStoreGetSection(sectionStore, canonicalSectionName); if( section != NULL ) { SectionRemoveLinesForVariable(section, varKey); } } LineArray SectionStoreFindLines(SECTIONSTOREPTR sectionStore, CONST_STRPTR canonicalSectionName, CONST_STRPTR varKey) { LineArray result = LineArrayNew(); SECTIONPTR section = SectionStoreGetSection(sectionStore, canonicalSectionName); if( section != NULL ) { SectionCollectLinesForVariable(section, varKey, result); } return result; } VariableArray SectionStoreGetAll(SECTIONSTOREPTR sectionStore, CONST_STRPTR canonicalSectionName, CONST_STRPTR varKey) { VariableArray result = VariableArrayNew(); LineArray lines = SectionStoreFindLines(sectionStore, canonicalSectionName, varKey); ULONG index = 0; for( index = 0; index < SizeOfArray(lines); index++) { LINEPTR line = LineArrayValues(lines)[index]; VariableArrayAppend(result, LineGetVariable(line)); } LineArrayFree(lines, FALSE); return result; } VARIABLEPTR SectionStoreGet(SECTIONSTOREPTR sectionStore, CONST_STRPTR canonicalSectionName, CONST_STRPTR varKey) { VARIABLEPTR result = NULL; LineArray lines = SectionStoreFindLines(sectionStore, canonicalSectionName, varKey); result = LineGetVariable((LINEPTR)ArrayBackValue(LINEPTR, lines)); LineArrayFree(lines, FALSE); return result; } VOID SectionStoreAddLineToCurrentSection(SECTIONSTOREPTR abstractSectionStore, LINEPTR line) { struct SectionStore* store = (struct SectionStore*)abstractSectionStore; if( store != NULL ) { // if its a new section, just add it and move on SECTIONPTR section = parseSection(line, store->sectionPatternProgram); if( section ) { SectionStoreAddSection(store, section); SectionAddSectionLine(section, line); } else { // if its a variable or blank, parse it and add to current section VARIABLEPTR variable = NULL; section = SectionStoreCurrentSection(store); // if no current section, we're adding variables at the state so create a blank section if( section == NULL ) { section = SectionCreateWithName(""); SectionStoreAddSection(store, section); } if( variable = parseVariable(line, store->variablePatternProgram) ) { LineSetInitialVariable(line, variable); SectionAddLine(section, line, FALSE); } else { if( parseBlank(line, store->blankPatternProgram) ) { SectionAddLine(section, line, FALSE); } else { // wasnt a section, a variable or a comment so ignore it or complain } } } } } ULONG SectionStoreSectionCount(SECTIONSTOREPTR abstractSectionStore) { ULONG result = 0; struct SectionStore* store = (struct SectionStore*)abstractSectionStore; if( store != NULL ) { return SizeOfArray(store->array); } return result; } SECTIONPTR SectionStoreSectionAt(SECTIONSTOREPTR abstractSectionStore, ULONG index) { SECTIONPTR result = NULL; struct SectionStore* store = (struct SectionStore*)abstractSectionStore; if( store != NULL ) { if( index < SizeOfArray(store->array) ) { result = SectionArrayValues(store->array)[index]; } } return result; } StringArray SectionStoreSubsectionsForSection(SECTIONSTOREPTR abstractSectionStore, CONST_STRPTR primarySection) { struct SectionStore* store = (struct SectionStore*)abstractSectionStore; StringArray result = StringArrayNew(); ULONG index = 0; for( index = 0; index < SizeOfArray(store->array); index++) { SECTIONPTR section = SectionArrayValues(store->array)[index]; if( SectionHasName(section, primarySection) ) { CONST_STRPTR secondary = SectionGetRawSubsectionName(section); if( secondary != NULL && strlen(secondary) > 0 ) { StringArrayAppendAndRetain(result, secondary); } } } return result; } SECTIONPTR SectionStoreFindOrCreateSection(SECTIONSTOREPTR sectionStore, CONST_STRPTR compoundKey) { // now get the section StringArray canonicalParts = CompoundKeySplitKeyForVar(compoundKey); SECTIONPTR section = SectionStoreGetSection(sectionStore, StringArrayValues(canonicalParts)[0]); if( section == NULL ) { StringArray separateParts = CompoundKeySplitKeyCompletely(compoundKey); CONST_STRPTR sectionLineText = NULL; LINEPTR sectionLine = NULL; section = SectionCreateWithNameAndSubname(StringArrayValues(separateParts)[0], StringArrayValues(separateParts)[1]); sectionLineText = SectionSerialize(section); sectionLine = LineNew(sectionLineText); SectionAddSectionLine(section, sectionLine); FreeVec((STRPTR)sectionLineText); SectionStoreAddSection(sectionStore, section); //section store will free it for us StringArrayFree(separateParts, TRUE); } StringArrayFree(canonicalParts, TRUE); return section; } // --------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------- // PRIVATE // --------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------- STATIC VARIABLEPTR parseVariable(LINEPTR line, PATTERNPTR variablePatternProgram) { VARIABLEPTR result = NULL; StringArray matches = NULL; if( matches = PatternRun(LineGetRawText(line), variablePatternProgram) ) { result = VariableCreate(StringArrayValues(matches)[1], StringArrayValues(matches)[2]); StringArrayFree(matches, TRUE); } return result; } STATIC SECTIONPTR parseSection(LINEPTR line, PATTERNPTR sectionPatternProgram) { SECTIONPTR result = NULL; StringArray matches = NULL; if( matches = PatternRun(LineGetRawText(line), sectionPatternProgram) ) { if( SizeOfArray(matches) == 3 ) { result = SectionCreateWithName(StringArrayValues(matches)[1]); } else if( SizeOfArray(matches) == 5 ) { result = SectionCreateWithNameAndSubname(StringArrayValues(matches)[1], StringArrayValues(matches)[3]); } StringArrayFree(matches, TRUE); } return result; } // TODO I ended up not using these and just having all variable be strings. // The calling code can decide how to parse the strings into ints or bools or whatever // It just seemed like a step too far in complexity to have to decide for each // variable what type it was and then keep track of that when we set, or collect values // STATIC BOOL parseBoolValue(CONST_STRPTR value, BOOL* outBool) // { // BOOL result = FALSE; // if( strcmp(value, "yes") == 0 || strcmp(value, "true") == 0 || strcmp(value, "on") == 0 ) { // *outBool = TRUE; // result = TRUE; // } // if( strcmp(value, "no") == 0 || strcmp(value, "false") == 0 || strcmp(value, "off") == 0 ) { // *outBool = FALSE; // result = TRUE; // } // return result; // } // // // STATIC BOOL parseIntegerValue(CONST_STRPTR value, LONG* outInt, PATTERNPTR integerProgram) // { // BOOL result = FALSE; // StringArray matches = NULL; // // if( matches = PatternRun(value, integerProgram) ) // { // StringArrayFree(matches, TRUE); // *outInt = atol(value); // result = TRUE; // } // return result; // } STATIC BOOL parseBlank(LINEPTR line, PATTERNPTR blankPatternProgram) { BOOL result = FALSE; StringArray matches = NULL; if( matches = PatternRun(LineGetRawText(line), blankPatternProgram) ) { result = TRUE; StringArrayFree(matches, TRUE); } return result; }