#include "configmodel.h" #include "containers/linearray.h" #include "containers/sectionarray.h" #include "containers/stringarray.h" #include #include #include #include #include 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); } }