/* Asterisk INI to Property List File Conversion Utility -- Version 1.10 * * intermediate.c * aini2plist * * Intermediate phase, builds IR tree for the currently parsed section * * Author: Benjamin Kowarsch * * (C) 2006 Sunrise Telephone Systems Ltd. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * In countries and territories where the above no-warranty disclaimer is * not permissible by applicable law, the following terms apply: * * NO PERMISSION TO USE THE SOFTWARE IS GRANTED AND THE SOFTWARE MUST NOT BE * USED AT ALL IN SUCH COUNTRIES AND TERRITORIES WHERE THE ABOVE NO-WARRANTY * DISCLAIMER IS NOT PERMISSIBLE AND INVALIDATED BY APPLICABLE LAW. HOWEVER, * THE COPYRIGHT HOLDERS HEREBY WAIVE THEIR RIGHT TO PURSUE OFFENDERS AS LONG * AS THEY OTHERWISE ABIDE BY THE TERMS OF THE LICENSE AS APPLICABLE FOR USE * OF THE SOFTWARE IN COUNTRIES AND TERRITORIES WHERE THE ABOVE NO-WARRANTY * DISCLAIMER IS PERMITTED BY APPLICABLE LAW. THIS WAIVER DOES NOT CONSTITUTE * A LICENSE TO USE THE SOFTWARE IN COUNTRIES AND TERRITORIES WHERE THE ABOVE * NO-WARRANTY DISCLAIMER IS NOT PERMISSIBLE AND INVALIDATED BY APPLICABLE * LAW. ANY LIABILITY OF ANY KIND IS CATEGORICALLY RULED OUT AT ALL TIMES. */ #include #include #include #include "hash.h" #include "ASCII.h" #include "globaldefs.h" #include "keywords.h" #include "intermediate.h" #define MAX_STR_LEN MAXIMUM_LENGTH_FOR_STRINGS // hash value for keyword "yes" #define YES_VALUE 0x3BFA6D47 // hash value for keyword "no" #define NO_VALUE 0x006E1B81 // hash value for keyword "true" #define TRUE_VALUE 0x4DAE9B2E // hash value for keyword "false" #define FALSE_VALUE 0x66E499E3 // -------------------------------------------------------------------------- // Comment table entry type // -------------------------------------------------------------------------- struct _comment_entry { char *string; struct _comment_entry *next; }; // this is what we really wanted to declare typedef struct _comment_entry CommentEntry; // -------------------------------------------------------------------------- // Comment table // -------------------------------------------------------------------------- static CARDINAL commentCount = 0; static CommentEntry *firstComment = NULL, *lastComment = NULL; // -------------------------------------------------------------------------- // Comment iteration cache // -------------------------------------------------------------------------- static CARDINAL lastIterationCommentIndex = 0; static CommentEntry *lastIterationComment = NULL; // -------------------------------------------------------------------------- // Value list entry type // -------------------------------------------------------------------------- struct _value_list_entry { // hash value CARDINAL hash; // value in plaintext char *string; // value's inline comment char *comment; // pointer to value next in list struct _value_list_entry *next; }; // this is what we really wanted to declare typedef struct _value_list_entry ValueListEntry; // -------------------------------------------------------------------------- // Key table entry type // -------------------------------------------------------------------------- struct _key_table_entry { // hash value CARDINAL hash; // key name char *name; // key's attribute KeywordAttribute attribute; // number of assigned comments CARDINAL commentCount; // list of assigned comments CommentEntry *firstComment, *lastComment; // index of comment last retrieved CARDINAL lastIterationCommentIndex; // pointer to comment last retrieved CommentEntry *lastIterationComment; // number of values stored in key CARDINAL valueCount; // list of values stored in key ValueListEntry *firstValue, *lastValue; // index of value last retrieved CARDINAL lastIterationValueIndex; // pointer to value last retrieved ValueListEntry *lastIterationValue; // pointer to key next in list struct _key_table_entry *next; }; // this is what we really wanted to declare typedef struct _key_table_entry KeyTableEntry; // -------------------------------------------------------------------------- // Section table entry type // -------------------------------------------------------------------------- struct _section_table_entry { // hash value CARDINAL hash; // section name char *name; // section's attribute KeywordAttribute attribute; // number of assigned comments CARDINAL commentCount; // list of assigned comments CommentEntry *firstComment, *lastComment; // index of comment last retrieved CARDINAL lastIterationCommentIndex; // pointer to comment last retrieved CommentEntry *lastIterationComment; // number of keys in section CARDINAL keyCount; // list of keys in section KeyTableEntry *firstKey, *lastKey; // hash value of key last looked up CARDINAL lastLookupHash; // pointer to key last looked up KeyTableEntry * lastLookupKey; // index of key last retrieved CARDINAL lastIterationKeyIndex; // pointer to key last retrieved KeyTableEntry *lastIterationKey; // pointer to section next in list struct _section_table_entry *next; }; // this is what we really wanted to declare typedef struct _section_table_entry SectionTableEntry; // -------------------------------------------------------------------------- // Section table // -------------------------------------------------------------------------- static CARDINAL sectionCount = 0; static SectionTableEntry *firstSection = NULL, *lastSection = NULL; // -------------------------------------------------------------------------- // Section lookup cache // -------------------------------------------------------------------------- static CARDINAL lastLookupHash = 0; static SectionTableEntry *lastLookupSection = NULL; // -------------------------------------------------------------------------- // Section iteration cache // -------------------------------------------------------------------------- static CARDINAL lastIterationSectionIndex = 0; static SectionTableEntry *lastIterationSection = NULL; // -------------------------------------------------------------------------- // Relocation table entry type // -------------------------------------------------------------------------- struct _relocation_table_entry { // key to be relocated KeyTableEntry *key; // section where key is located SectionTableEntry *currentLocation; // section where key is to be relocated to SectionTableEntry *targetLocation; // pointer to next in list struct _relocation_table_entry *next; }; // this is what we really wanted to declare typedef struct _relocation_table_entry RelocationTableEntry; // -------------------------------------------------------------------------- // Relocation table // -------------------------------------------------------------------------- static CARDINAL relocatableKeyCount = 0; static RelocationTableEntry *firstRelocation = NULL, *lastRelocation = NULL; // ========================================================================== // Private functions // ========================================================================== // -------------------------------------------------------------------------- // private function: eval_boolstr(boolstr) // -------------------------------------------------------------------------- // // Evaluates string 'boolstr' and returns: // 0 - if boolstr is any of "0", "n", "f", "no", "false" // 1 - if boolstr is any of "1", "y", "t", "yes", "true" // -1 - in any other case int eval_boolstr(const char *boolstr) { CARDINAL len, hash; char ch = boolstr[0]; len = strlen(boolstr); if (len == 0) { return -1; } else if (len == 1) { if ((ch == DIGIT_ZERO) || (ch == LOWERCASE_N) || (ch == LOWERCASE_F)) { // boolean value "false" return 0; } else if ((ch == DIGIT_ONE) || (ch == LOWERCASE_Y) || (ch == LOWERCASE_T)) { // boolean value "true" return 1; } else { // not a boolean value return -1; } // end if } else { hash = calc_hash(boolstr); if ((hash == NO_VALUE) || (hash == FALSE_VALUE)) { // boolean value "false" return 0; } else if ((hash == YES_VALUE) || (hash == TRUE_VALUE)) { // boolean value "true" return 1; } else { // not a boolean value return -1; } // end if } // end if } // end eval_boolstr // -------------------------------------------------------------------------- // private function: new_string(string) // -------------------------------------------------------------------------- // // Returns a newly allocated C string with 'string' as its contents. static char *new_string(const char *string) { char *new = NULL; CARDINAL len; if (string == NULL) { // no string to assign return NULL; } // end if len = (CARDINAL) strlen(string); new = malloc(len + 1); if (new == NULL) { // out of memory return NULL; } // end if if (len > 0) { strncpy(new, string, len); } // end if new[len] = CSTRING_TERMINATOR; return new; } // end new_string // -------------------------------------------------------------------------- // private function: new_comment(comment) // -------------------------------------------------------------------------- // // Returns a newly allocated comment entry with 'comment' as its contents and // its successor field 'next' pointing to NULL. static CommentEntry *new_comment(const char *comment) { CommentEntry *new = NULL; if (comment == NULL) { // no comment to assign return NULL; } // end if new = malloc(sizeof(CommentEntry)); if (new == NULL) { // out of memory return NULL; } // end if new->string = new_string(comment); if (new->string == NULL) { // out of memory free(new); return NULL; } // end if new->next = NULL; return new; } // end new_comment // -------------------------------------------------------------------------- // private function new_value(value) // -------------------------------------------------------------------------- // static ValueListEntry *new_value(const char *value) { ValueListEntry *new = NULL; if (value == NULL) { // no value to assign return NULL; } // end if new = malloc(sizeof(ValueListEntry)); if (new == NULL) { // out of memory return NULL; } // end if new->string = new_string(value); if (new->string == NULL) { // out of memory free(new); return NULL; } // end if new->hash = 0; new->comment = NULL; new->next = NULL; return new; } // end new_value // -------------------------------------------------------------------------- // private function new_key(hash) // -------------------------------------------------------------------------- // static KeyTableEntry *new_key(CARDINAL hash) { KeyTableEntry *new = NULL; if (hash == 0) { // invalid key return NULL; } // end if new = malloc(sizeof(KeyTableEntry)); if (new == NULL) { // out of memory return NULL; } // end if // initialise members new->hash = hash; new->name = NULL; new->attribute = NO_ATTRIBUTE; new->commentCount = 0; new->firstComment = NULL; new->lastComment = NULL; new->lastIterationCommentIndex = 0; new->lastIterationComment = NULL; new->valueCount = 0; new->firstValue = NULL; new->lastValue = NULL; new->lastIterationValueIndex = 0; new->lastIterationValue = NULL; new->next = NULL; return new; } // end new_key // -------------------------------------------------------------------------- // private function insert_into_relocation_table(key, currentLoc, targetLoc) // -------------------------------------------------------------------------- // static int insert_into_relocation_table(KeyTableEntry *key, SectionTableEntry *currentSection, SectionTableEntry *targetSection) { RelocationTableEntry *new = NULL; if ((key == NULL) || (currentSection == NULL) || (targetSection == NULL)) { return 0; } // end if new = malloc(sizeof(RelocationTableEntry)); if (new == NULL) { // out of memory return 0; } // end if // initialise members new->key = key; new->currentLocation = currentSection; new->targetLocation = targetSection; new->next = NULL; // append to list if (firstRelocation == NULL) { firstRelocation = new; } else { lastRelocation->next = new; } // end if lastRelocation = new; relocatableKeyCount++; return 1; } // end insert_into_relocation_table // -------------------------------------------------------------------------- // private function remove_key_from_section(section, key) // -------------------------------------------------------------------------- // static int remove_key_from_section(SectionTableEntry *section, KeyTableEntry *key) { KeyTableEntry *prevKey = NULL, *thisKey = NULL; if ((section == NULL) || (key == NULL)) { return 0; } // end if thisKey = section->firstKey; if /* first key */ (thisKey == key) { // remove the key section->firstKey = thisKey->next; } else /* not first key */ { // find the key to be removed while (thisKey != key) { prevKey = thisKey; thisKey = thisKey->next; if (thisKey == NULL) { // key not found return 0; } // end if } // end while // remove the key prevKey->next = thisKey->next; } // end if // adjust key count section->keyCount--; // adjust lastKey if removed key was last key if (thisKey == section->lastKey) { section->lastKey = prevKey; } // end if // reset lookup cache if it contained removed key if (section->keyCount == 0) { section->lastLookupHash = 0; } else { section->lastLookupHash = section->firstKey->hash; } // end if section->lastLookupKey = section->firstKey; // adjust key iteration cache if (section->keyCount == 0) { section->lastIterationKeyIndex = 1; } else { section->lastIterationKeyIndex = 0; } // end if section->lastIterationKey = section->firstKey; return 1; } // end remove_key_from_section // -------------------------------------------------------------------------- // private function append_key_to_section(section, key) // -------------------------------------------------------------------------- // static int append_key_to_section(SectionTableEntry *section, KeyTableEntry *key) { if ((section == NULL) || (key == NULL)) { return 0; } // end if if (section->firstKey == NULL) { section->firstKey = key; section->lastKey = key; section->keyCount = 1; } else { section->lastKey->next = key; section->lastKey = key; section->lastKey->next = NULL; section->keyCount++; } // end if // reset lookup cache section->lastLookupHash = section->firstKey->hash; section->lastLookupKey = section->firstKey; // reset key iteration cache section->lastIterationKeyIndex = 1; section->lastIterationKey = section->firstKey; return 0; } // end append_key_to_section // -------------------------------------------------------------------------- // private function is_unique_value_for_key(key, value) // -------------------------------------------------------------------------- // static bool is_unique_value_for_key(KeyTableEntry *key, CARDINAL value) { ValueListEntry *thisValue = NULL; if ((value == 0) || (key == NULL)) { // invalid value return false; } // end if thisValue = key->firstValue; while (thisValue != NULL) { if (thisValue->hash == value) { // value found -- not unique return false; } // end if thisValue = thisValue->next; } // end while return true; } // end is_unique_value_for_key // -------------------------------------------------------------------------- // private function lookup_key(section, hash) // -------------------------------------------------------------------------- // static KeyTableEntry *lookup_key(SectionTableEntry *section, CARDINAL hash) { KeyTableEntry *thisKey = NULL; if ((hash == 0) || (section == NULL) || (section->firstKey == NULL)) { return NULL; } // end if if (hash == section->lastLookupHash) { return section->lastLookupKey; } // end if thisKey = section->firstKey; // for now lets do a linear search // the number of keys in any given section should be very low anyway while (thisKey != NULL) { if (thisKey->hash == hash) { // key found section->lastLookupHash = hash; section->lastLookupKey = thisKey; break; } // end if // move to next entry thisKey = thisKey->next; } // end if return thisKey; } // end lookup_key // -------------------------------------------------------------------------- // private function new_section(hash) // -------------------------------------------------------------------------- // static SectionTableEntry *new_section(CARDINAL hash) { SectionTableEntry *new = NULL; if (hash == 0) { // invalid section return NULL; } // end if new = malloc(sizeof(SectionTableEntry)); if (new == NULL) { // out of memory return NULL; } // end if // initialise members new->hash = hash; new->name = NULL; new->attribute = NO_ATTRIBUTE; new->commentCount = 0; new->firstComment = NULL; new->lastComment = NULL; new->lastIterationCommentIndex = 0; new->lastIterationComment = NULL; new->keyCount = 0; new->firstKey = NULL; new->lastKey = NULL; new->lastLookupHash = 0; new->lastLookupKey = NULL; new->lastIterationKeyIndex = 0; new->lastIterationKey = NULL; new->next = NULL; return new; } // end new_section // -------------------------------------------------------------------------- // private function lookup_section(hash) // -------------------------------------------------------------------------- // static SectionTableEntry *lookup_section(CARDINAL hash) { SectionTableEntry *thisSection = NULL; if ((hash == 0) || (firstSection == NULL)) { return NULL; } // end if if (hash == lastLookupHash) { return lastLookupSection; } // end if thisSection = firstSection; // for now lets do a linear search // the number of sections in any given file should be very low anyway while (thisSection != NULL) { if (thisSection->hash == hash) { // section found lastLookupHash = hash; lastLookupSection = thisSection; break; } // end if // move to next entry thisSection = thisSection->next; } // end if return thisSection; } // end lookup_section // -------------------------------------------------------------------------- // private function remove_all_comments_for_key(key) // -------------------------------------------------------------------------- // static int remove_all_comments_for_key(KeyTableEntry *key) { CommentEntry *prevComment = NULL, *thisComment = NULL; if (key == NULL) { // no key to remove comments from return 0; } // end if thisComment = key->firstComment; // remove the key's comments while (thisComment != NULL) { free(thisComment->string); prevComment = thisComment; thisComment = thisComment->next; free(prevComment); } // end while key->firstComment = NULL; key->lastComment = NULL; key->commentCount = 0; key->lastIterationCommentIndex = 0; key->lastIterationComment = NULL; return 1; } // end remove_all_comments_for_key // -------------------------------------------------------------------------- // private function remove_all_values_for_key(key) // -------------------------------------------------------------------------- // static int remove_all_values_for_key(KeyTableEntry *key) { ValueListEntry *prevValue = NULL, *thisValue = NULL; if (key == NULL) { // no key to remove values from return 0; } // end if thisValue = key->firstValue; if (thisValue == NULL) { // no values to remove return 0; } // end if while (thisValue != NULL) { free(thisValue->string); if (thisValue->comment != NULL) { free(thisValue->comment); } // end if prevValue = thisValue; thisValue = thisValue->next; free(prevValue); } // end while key->firstValue = NULL; key->lastValue = NULL; key->valueCount = 0; key->lastIterationValueIndex = 0; key->lastIterationValue = NULL; return 1; } // end remove_all_values_for_key // -------------------------------------------------------------------------- // private function remove_all_comments_for_section(section) // -------------------------------------------------------------------------- // static int remove_all_comments_for_section(SectionTableEntry *section) { CommentEntry *prevComment = NULL, *thisComment = NULL; if (section == NULL) { // no section to remove comments from return 0; } // end if thisComment = section->firstComment; // remove the key's comments while (thisComment != NULL) { free(thisComment->string); prevComment = thisComment; thisComment = thisComment->next; free(prevComment); } // end while section->firstComment = NULL; section->lastComment = NULL; section->commentCount = 0; section->lastIterationCommentIndex = 0; section->lastIterationComment = NULL; return 1; } // end remove_all_comments_for_section // -------------------------------------------------------------------------- // function remove_all_keys_for_section(section) // -------------------------------------------------------------------------- // static int remove_all_keys_for_section(SectionTableEntry *section) { KeyTableEntry *prevKey = NULL, *thisKey = NULL; if (section->firstKey == NULL) { // no keys to remove return 0; } // end if thisKey = section->firstKey; while (thisKey != NULL) { // remove key's name if (thisKey->name != NULL) { free(thisKey->name); } // end if // remove key's comments remove_all_comments_for_key(thisKey); // remove key's value list remove_all_values_for_key(thisKey); // remove key itself prevKey = thisKey; thisKey = thisKey->next; free(prevKey); } // end while section->firstKey = NULL; section->lastKey = NULL; section->keyCount = 0; section->lastLookupHash = 0; section->lastLookupKey = NULL; section->lastIterationKeyIndex = 0; section->lastIterationKey = NULL; return 1; } // end remove_all_keys_for_section // ========================================================================== // Public functions for intermediate comment-line storage // ========================================================================== // -------------------------------------------------------------------------- // function new_unassigned_comment(comment) // -------------------------------------------------------------------------- // int new_unassigned_comment(const char *comment) { CommentEntry *new = NULL; if (comment == NULL) { // no comment to assign return 0; } // end if new = new_comment(comment); if (new == NULL) { // out of memory return 0; } // end if if (firstComment == NULL) { firstComment = new; lastComment = new; lastIterationCommentIndex = 1; lastIterationComment = firstComment; } else { lastComment->next = new; lastComment = new; } // end if commentCount++; return 1; } // end new_unassigned_comment // -------------------------------------------------------------------------- // function assign_pending_comments_to_key(key) // -------------------------------------------------------------------------- // int assign_pending_comments_to_key(CARDINAL section, CARDINAL key) { SectionTableEntry *thisSection = NULL; KeyTableEntry *thisKey = NULL; if (key == 0) { // invalid key return 0; } // end if thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return 0; } // end if thisKey = lookup_key(thisSection, key); if (thisKey == NULL) { // key not found return 0; } // end if if (thisKey->firstComment == NULL) { thisKey->firstComment = firstComment; thisKey->lastIterationCommentIndex = 1; thisKey->lastIterationComment = firstComment; } else /* key already has comments assigned */ { thisKey->lastComment->next = firstComment; } // end if thisKey->lastComment = lastComment; thisKey->commentCount = thisKey->commentCount + commentCount; firstComment = NULL; lastComment = NULL; commentCount = 0; return 1; } // end assign_pending_comments_to_key // -------------------------------------------------------------------------- // function assign_pending_comments_to_section(section) // -------------------------------------------------------------------------- // int assign_pending_comments_to_section(CARDINAL section) { SectionTableEntry *thisSection = NULL; if (section == 0) { // invalid section return 0; } // end if thisSection = lookup_section(section); if (thisSection == NULL) { // section not found return 0; } // end if if (thisSection->firstComment == NULL) { thisSection->firstComment = firstComment; thisSection->lastIterationCommentIndex = 1; thisSection->lastIterationComment = firstComment; } else /* section already has comments assigned */ { thisSection->lastComment->next = firstComment; } // end if thisSection->lastComment = lastComment; thisSection->commentCount = thisSection->commentCount + commentCount; firstComment = NULL; lastComment = NULL; commentCount = 0; return 1; } // end assign_pending_comments_to_section // -------------------------------------------------------------------------- // function get_pending_comment_count() // -------------------------------------------------------------------------- // CARDINAL get_pending_comment_count() { return commentCount; } // end get_pending_comment_count // -------------------------------------------------------------------------- // function get_pending_comment_at_index(index) // -------------------------------------------------------------------------- // char *get_pending_comment_at_index(CARDINAL index) { CommentEntry *thisComment = NULL; CARDINAL current = 1; if ((index == 0) || (index > commentCount)) { // index out of range return NULL; } // end if if (lastIterationCommentIndex <= index) { current = lastIterationCommentIndex; thisComment = lastIterationComment; } else { thisComment = firstComment; } // end if while (current < index) { thisComment = thisComment->next; current++; } // end while lastIterationCommentIndex = index; lastIterationComment = thisComment; return thisComment->string; } // end get_pending_comment_at_index // -------------------------------------------------------------------------- // function remove_all_pending_comments() // -------------------------------------------------------------------------- // int remove_all_pending_comments() { CommentEntry *prevComment = NULL, *thisComment = NULL; thisComment = firstComment; while (thisComment != NULL) { free(thisComment->string); prevComment = thisComment; thisComment = thisComment->next; free(prevComment); } // end while firstComment = NULL; lastComment = NULL; commentCount = 0; return 1; } // end remove_all_pending_comments // ========================================================================== // Public functions for intermediate key-value storage // ========================================================================== // -------------------------------------------------------------------------- // function new_key_with_name(section, key, name) // -------------------------------------------------------------------------- // int new_key_with_name(CARDINAL section, CARDINAL key, const char *name) { SectionTableEntry *thisSection = NULL, *targetSection = NULL; KeyTableEntry *new = NULL; char *translation = NULL; if ((key == 0) || (name == NULL)) { // invalid key return 0; } // end if thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return 0; } // end if new = lookup_key(thisSection, key); if (new != NULL) { // key already present return 0; } // end if new = new_key(key); if (new == NULL) { // out of memory return 0; } // end if translation = (char *) get_translation_for_keyword(key); if (translation == NULL) { new->name = new_string(name); } else /* translation available */ { // could use the pointer directly // but this will make deallocation more complex // will eventually use a hash dict for all key names new->name = new_string(translation); } // end if if (new->name == NULL) { // out of memory free(new); return 0; } // end if new->attribute = get_attribute_for_keyword(key); // check if this key is to be relocated to another section ... targetSection = lookup_section(get_relocation_for_keyword(key)); // ... if so, enter its coordinates into the relocation table if (targetSection != NULL) { insert_into_relocation_table(new, thisSection, targetSection); } // end if if (thisSection->firstKey == NULL) { thisSection->firstKey = new; thisSection->lastKey = new; thisSection->lastIterationKeyIndex = 1; thisSection->lastIterationKey = thisSection->firstKey; } else { thisSection->lastKey->next = new; thisSection->lastKey = new; } // end if thisSection->keyCount++; return 1; } // end new_key_with_name // -------------------------------------------------------------------------- // function get_name_of_key(section, key) // -------------------------------------------------------------------------- // const char *get_name_of_key(CARDINAL section, CARDINAL key) { SectionTableEntry *thisSection = NULL; KeyTableEntry *thisKey = NULL; if (key == 0) { // invalid key return NULL; } // end if thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return NULL; } // end if thisKey = lookup_key(thisSection, key); if (thisKey == NULL) { // not found return NULL; } // end if return thisKey->name; } // end get_name_of_key // -------------------------------------------------------------------------- // function key_is_present(section, key) // -------------------------------------------------------------------------- // bool key_is_present(CARDINAL section, CARDINAL key) { return (lookup_key(lookup_section(section), key) != NULL); } // end key_is_present // -------------------------------------------------------------------------- // function get_comment_count_for_key(section, key) // -------------------------------------------------------------------------- // CARDINAL get_comment_count_for_key(CARDINAL section, CARDINAL key) { SectionTableEntry *thisSection = NULL; KeyTableEntry *thisKey = NULL; if (key == 0) { // invalid key return 0; } // end if thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return 0; } // end if thisKey = lookup_key(thisSection, key); if (thisKey == NULL) { // not found return 0; } // end if return thisKey->commentCount; } // end get_comment_count_for_key // -------------------------------------------------------------------------- // function get_comment_for_key_at_index(section, key, index) // -------------------------------------------------------------------------- // char *get_comment_for_key_at_index(CARDINAL section, CARDINAL key, CARDINAL index) { SectionTableEntry *thisSection = NULL; KeyTableEntry *thisKey = NULL; CommentEntry *thisComment = NULL; CARDINAL current = 1; if (key == 0) { // invalid key return NULL; } // end if thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return NULL; } // end if thisKey = lookup_key(thisSection, key); if (thisKey == NULL) { // key not found return NULL; } // end if if (index > thisKey->commentCount) { // index out of range return 0; } // end if thisComment = thisKey->firstComment; while (current < index) { thisComment = thisComment->next; current++; } // end while return thisComment->string; } // end get_comment_for_key_at_index // -------------------------------------------------------------------------- // function is_multi_value_key(section, key) // -------------------------------------------------------------------------- // bool is_multi_value_key(CARDINAL section, CARDINAL key) { SectionTableEntry *thisSection = NULL; KeyTableEntry *thisKey = NULL; if (key == 0) { // invalid key return false; } // end if thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return false; } // end if thisKey = lookup_key(thisSection, key); if (thisKey == NULL) { // not found return false; } // end if return ((thisKey->attribute == MULTIPLE_ASSIGNMENTS) || (thisKey->attribute == MULTIPLE_ASSIGNMENTS_UNIQUE_VALUES)); } // end is_multi_value_key // -------------------------------------------------------------------------- // function assign_value_to_key(section, key, value) // -------------------------------------------------------------------------- // int assign_value_to_key(CARDINAL section, CARDINAL key, const char *value) { SectionTableEntry *thisSection = NULL; KeyTableEntry *thisKey = NULL; CARDINAL hash; bool boolval; if (key == 0) { // invalid key return 0; } // end if thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return 0; } // end if thisKey = lookup_key(thisSection, key); if (thisKey == NULL) { // not found return 0; } // end if if (thisKey->attribute == MULTIPLE_ASSIGNMENTS) { if (thisKey->firstValue == NULL) { // add the new value as first value thisKey->firstValue = new_value(value); thisKey->lastValue = thisKey->firstValue; thisKey->lastIterationValueIndex = 1; thisKey->lastIterationValue = thisKey->firstValue; } else { // append the new value to the value list thisKey->lastValue->next = new_value(value); thisKey->lastValue = thisKey->lastValue->next; } // end if thisKey->valueCount++; } else if (thisKey->attribute == MULTIPLE_ASSIGNMENTS_UNIQUE_VALUES) { hash = calc_hash_with_limit(value, MAXIMUM_LENGTH_FOR_IDENTIFIERS); // assign value if unique, ignore if duplicate if (is_unique_value_for_key(thisKey, hash) == true) { if (thisKey->firstValue == NULL) { thisKey->firstValue = new_value(value); thisKey->lastValue = thisKey->firstValue; thisKey->lastIterationValueIndex = 1; thisKey->lastIterationValue = thisKey->firstValue; } else /* not first in list */ { thisKey->lastValue->next = new_value(value); thisKey->lastValue = thisKey->lastValue->next; } // end if thisKey->lastValue->hash = hash; thisKey->valueCount++; } // end if } else /* key takes a single value only */ { if (thisKey->firstValue != NULL) { // dispose of existing value(s) remove_all_values_for_key(thisKey); } // end if if (thisKey->attribute != TOGGLE_BOOLEAN_VALUE) { // store value as it is thisKey->firstValue = new_value(value); } else /* boolean value to invert */ { // store value inverted boolval = eval_boolstr(value); if (boolval == 0) { // no becomes yes thisKey->firstValue = new_value("yes"); } else if (boolval == 1) { // yes becomes no thisKey->firstValue = new_value("no"); } else /* not a boolean value */ { /// TO DO: error handling, remove entire key } // end if } // end if thisKey->lastValue = thisKey->firstValue; thisKey->valueCount = 1; thisKey->lastIterationValueIndex = 1; thisKey->lastIterationValue = thisKey->firstValue; } // end if return 1; } // end assign_value_to_key // -------------------------------------------------------------------------- // function get_value_count_for_key(section, key) // -------------------------------------------------------------------------- // CARDINAL get_value_count_for_key(CARDINAL section, CARDINAL key) { SectionTableEntry *thisSection = NULL; KeyTableEntry *thisKey = NULL; if (key == 0) { // invalid key return 0; } // end if thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return 0; } // end if thisKey = lookup_key(thisSection, key); if (thisKey == NULL) { // not found return 0; } // end if return thisKey->valueCount; } // end get_value_count_for_key // -------------------------------------------------------------------------- // function get_value_for_key_at_index(section, key, index) // -------------------------------------------------------------------------- // char *get_value_for_key_at_index(CARDINAL section, CARDINAL key, CARDINAL index) { SectionTableEntry *thisSection = NULL; KeyTableEntry *thisKey = NULL; ValueListEntry *thisValue = NULL; CARDINAL current = 1; if ((key == 0) || (index == 0)) { // invalid key or index return NULL; } // end if thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return NULL; } // end if thisKey = lookup_key(thisSection, key); if (thisKey == NULL) { // key not found return NULL; } // end if if (index > thisKey->valueCount) { // index out of range return NULL; } // end if if (thisKey->lastIterationValueIndex <= index) { current = thisKey->lastIterationValueIndex; thisValue = thisKey->lastIterationValue; } else { thisValue = thisKey->firstValue; } // end if while (current < index) { thisValue = thisValue->next; current++; } // end while thisKey->lastIterationValueIndex = index; thisKey->lastIterationValue = thisValue; return thisValue->string; } // end get_value_for_key_at_index // -------------------------------------------------------------------------- // function set_comment_of_last_value_for_key(section, key, comment) // -------------------------------------------------------------------------- // int set_comment_of_last_value_for_key(CARDINAL section, CARDINAL key, const char *comment) { /// TO DO: return 0; } // end set_comment_of_last_value_for_key // -------------------------------------------------------------------------- // function key_has_inline_comments(section, key) // -------------------------------------------------------------------------- // bool key_has_inline_comments(CARDINAL section, CARDINAL key) { /// TO DO return false; } // end key_has_inline_comments // -------------------------------------------------------------------------- // function get_comment_of_value_for_key_at_index(section, key, index) // -------------------------------------------------------------------------- // const char *get_comment_of_value_for_key_at_index(CARDINAL section, CARDINAL key, CARDINAL index) { /// TO DO: return NULL; } // end get_comment_of_value_for_key_at_index // ========================================================================== // Intermediate section storage // ========================================================================== // -------------------------------------------------------------------------- // function new_section_with_name(section, name) // -------------------------------------------------------------------------- // int new_section_with_name(CARDINAL section, const char *name) { SectionTableEntry *new = NULL; char *translation = NULL; if ((section == 0) || (name == NULL)) { // invalid section return 0; } // end if new = lookup_section(section); if (new != NULL) { // section already present return 0; } // end if new = new_section(section); if (new == NULL) { // out of memory return 0; } // end if translation = (char *) get_translation_for_keyword(section); if (translation == NULL) { new->name = new_string(name); } else /* translation available */ { // could use the pointer directly // but this will make deallocation more complex // will eventually use a hash dict for all key names new->name = new_string(translation); } // end if if (new->name == NULL) { // out of memory free(new); return 0; } // end if new->attribute = get_attribute_for_keyword(section); if (firstSection == NULL) { firstSection = new; lastSection = new; lastIterationSectionIndex = 1; lastIterationSection = firstSection; } else { lastSection->next = new; lastSection = new; } // end if sectionCount++; return 1; } // end new_section_with_name // -------------------------------------------------------------------------- // function get_name_of_section(section) // -------------------------------------------------------------------------- // const char *get_name_of_section(CARDINAL section) { SectionTableEntry *thisSection = NULL; thisSection = lookup_section(section); if (thisSection == NULL) { // not found return NULL; } // end if return thisSection->name; } // end get_name_of_section // -------------------------------------------------------------------------- // function section_is_present(section) // -------------------------------------------------------------------------- // bool section_is_present(CARDINAL section) { return (lookup_section(section) != NULL); } // end section_is_present // -------------------------------------------------------------------------- // function get_comment_count_for_section(section) // -------------------------------------------------------------------------- // CARDINAL get_comment_count_for_section(CARDINAL section) { SectionTableEntry *thisSection = NULL; thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return 0; } // end if return thisSection->commentCount; } // end get_comment_count_for_key // -------------------------------------------------------------------------- // function get_comment_for_section_at_index(section, index) // -------------------------------------------------------------------------- // char *get_comment_for_section_at_index(CARDINAL section, CARDINAL index) { SectionTableEntry *thisSection = NULL; CommentEntry *thisComment = NULL; CARDINAL current = 1; thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return NULL; } // end if if (index > thisSection->commentCount) { // index out of range return 0; } // end if thisComment = thisSection->firstComment; while (current < index) { thisComment = thisComment->next; current++; } // end while return thisComment->string; } // end get_comment_for_key_at_index // -------------------------------------------------------------------------- // function get_key_count_for_section(section) // -------------------------------------------------------------------------- // CARDINAL get_key_count_for_section(CARDINAL section) { SectionTableEntry *thisSection = NULL; thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return 0; } // end if return thisSection->keyCount; } // end get_comment_count_for_key // -------------------------------------------------------------------------- // function get_key_for_section_at_index(section, index) // -------------------------------------------------------------------------- // CARDINAL get_key_for_section_at_index(CARDINAL section, CARDINAL index) { SectionTableEntry *thisSection = NULL; KeyTableEntry *thisKey = NULL; CARDINAL current = 1; thisSection = lookup_section(section); if (thisSection == NULL) { // invalid section return 0; } // end if if (index > thisSection->keyCount) { // index out of range return 0; } // end if if (thisSection->lastIterationKeyIndex <= index) { current = thisSection->lastIterationKeyIndex; thisKey = thisSection->lastIterationKey; } else { thisKey = thisSection->firstKey; } // end if while (current < index) { thisKey = thisKey->next; current++; } // end while thisSection->lastIterationKeyIndex = index; thisSection->lastIterationKey = thisKey; return thisKey->hash; } // end get_key_for_section_at_index // -------------------------------------------------------------------------- // function get_section_count() // -------------------------------------------------------------------------- // CARDINAL get_section_count() { return sectionCount; } // end get_section_count // -------------------------------------------------------------------------- // function get_section_at_index(index) // -------------------------------------------------------------------------- // CARDINAL get_section_at_index(CARDINAL index) { SectionTableEntry *thisSection = NULL; CARDINAL current = 1; if (index == 0) { // index out of range return 0; } // end if if (lastIterationSectionIndex <= index) { current = lastIterationSectionIndex; thisSection = lastIterationSection; } else { thisSection = firstSection; } // end if if (index > sectionCount) { // index out of range return 0; } // end if while (current < index) { thisSection = thisSection->next; current++; } // end while lastIterationSectionIndex = index; lastIterationSection = thisSection; return thisSection->hash; } // end get_section_at_index // -------------------------------------------------------------------------- // function move_all_relocatable_keys() // -------------------------------------------------------------------------- // int move_all_relocatable_keys() { RelocationTableEntry *previous = NULL, *this = NULL; if (relocatableKeyCount < 1) { // no keys to relocate return 0; } // end if this = firstRelocation; while (this != NULL) { // move the key to its target section remove_key_from_section(this->currentLocation, this->key); append_key_to_section(this->targetLocation, this->key); // remove entry in relocation table previous = this; this = this->next; free(previous); } // end while relocatableKeyCount = 0; firstRelocation = NULL; lastRelocation = NULL; return 1; } // end move_all_relocatable_keys // -------------------------------------------------------------------------- // function remove_all_sections() // -------------------------------------------------------------------------- // int remove_all_sections() { SectionTableEntry *prevSection = NULL, *thisSection = NULL; if (firstSection == NULL) { // no sections to remove return 0; } // end if thisSection = firstSection; while (thisSection != NULL) { // remove section's name if (thisSection->name != NULL) { free(thisSection->name); } // end if // remove section's comments remove_all_comments_for_section(thisSection); // remove section's keys remove_all_keys_for_section(thisSection); // remove section itself prevSection = thisSection; thisSection = thisSection->next; free(prevSection); } // end while firstSection = NULL; lastSection = NULL; sectionCount = 0; lastLookupHash = 0; lastLookupSection = NULL; lastIterationSectionIndex = 0; lastIterationSection = NULL; return 1; } // end remove_all_sections // END OF FILE