/* Run parser */ static int parser_run(struct parser_obj *po) { int error = EOK; struct collection_item *item = NULL; uint32_t action = 0; action_fn operations[] = { parser_read, parser_inspect, parser_post, parser_error, NULL }; TRACE_FLOW_ENTRY(); while(1) { /* Get next action */ item = NULL; error = col_dequeue_item(po->queue, &item); if (error) { TRACE_ERROR_NUMBER("Failed to get action", error); return error; } /* Get action, run operation */ action = *((uint32_t *)(col_get_item_data(item))); col_delete_item(item); if (action == PARSE_DONE) { TRACE_INFO_NUMBER("We are done", error); /* Report merge error in detect mode * if no other error was detected. */ if ((po->ret == 0) && (po->merge_error != 0) && ((po->collision_flags & INI_MV1S_DETECT) || (po->collision_flags & INI_MV2S_DETECT) || (po->collision_flags & INI_MS_DETECT))) po->ret = po->merge_error; error = po->ret; break; } error = operations[action](po); if (error) { TRACE_ERROR_NUMBER("Failed to perform an action", error); return error; } } TRACE_FLOW_EXIT(); return error; }
/* Configuration copy callback */ static int ini_copy_cb(struct collection_item *item, void *ext_data, int *skip) { int error = EOK; struct value_obj *vo = NULL; struct value_obj *new_vo = NULL; TRACE_FLOW_ENTRY(); ext_data = NULL; *skip = 0; /* Banary items are the values */ if(col_get_item_type(item) == COL_TYPE_BINARY) { vo = *((struct value_obj **)(col_get_item_data(item))); error = value_copy(vo, &new_vo); if (error) { TRACE_ERROR_NUMBER("Failed to copy value", error); return error; } error = col_modify_binary_item(item, NULL, &new_vo, sizeof(struct value_obj *)); if (error) { TRACE_ERROR_NUMBER("Failed to copy value", error); value_destroy(new_vo); return error; } } TRACE_FLOW_EXIT(); return error; }
/* Find if there is a collistion */ static int check_section_collision(struct parser_obj *po) { int error = EOK; struct collection_item *item = NULL; TRACE_FLOW_ENTRY(); TRACE_INFO_STRING("Searching for:", col_get_item_property(po->sec, NULL)); error = col_get_item(po->top, col_get_item_property(po->sec, NULL), COL_TYPE_ANY, COL_TRAVERSE_DEFAULT, &item); if (error) { TRACE_ERROR_NUMBER("Failed searching for dup", error); return error; } /* Check if there is a dup */ if (item) { TRACE_INFO_STRING("Collision found:", col_get_item_property(item, NULL)); /* Get the actual section collection instead of reference */ po->merge_sec = *((struct collection_item **) (col_get_item_data(item))); } else { TRACE_INFO_STRING("Collision not found.", ""); po->merge_sec = NULL; } TRACE_FLOW_EXIT(); return EOK; }
/* Complete value processing */ static int complete_value_processing(struct parser_obj *po) { int error = EOK; int error2 = EOK; struct value_obj *vo = NULL; struct value_obj *vo_old = NULL; unsigned insertmode; uint32_t mergemode; int suppress = 0; int doinsert = 0; struct collection_item *item = NULL; struct collection_item *section = NULL; int merging = 0; TRACE_FLOW_ENTRY(); if (po->merge_sec) { TRACE_INFO_STRING("Processing value in merge mode", ""); section = po->merge_sec; merging = 1; } else if(!(po->sec)) { TRACE_INFO_STRING("Creating default section", ""); /* If there is not open section create a default one */ error = col_create_collection(&po->sec, INI_DEFAULT_SECTION, COL_CLASS_INI_SECTION); if (error) { TRACE_ERROR_NUMBER("Failed to create default section", error); return error; } section = po->sec; } else { TRACE_INFO_STRING("Processing value in normal mode", ""); section = po->sec; } if (merging) { TRACE_INFO_STRING("Using merge key:", po->merge_key); vo = po->merge_vo; /* We are adding to the merge section so use MV2S flags. * But flags are done in such a way that deviding MV2S by MV1S mask * will translate MV2S flags into MV1S so we can use * MV1S constants. */ TRACE_INFO_NUMBER("Collisions flags:", po->collision_flags); mergemode = (po->collision_flags & INI_MV2S_MASK) / INI_MV1S_MASK; } else { /* Construct value object from what we have */ error = value_create_from_refarray(po->raw_lines, po->raw_lengths, po->keylinenum, INI_VALUE_READ, po->key_len, po->boundary, po->ic, &vo); if (error) { TRACE_ERROR_NUMBER("Failed to create value object", error); return error; } /* Forget about the arrays. They are now owned by the value object */ po->ic = NULL; po->raw_lines = NULL; po->raw_lengths = NULL; mergemode = po->collision_flags & INI_MV1S_MASK; } switch (mergemode) { case INI_MV1S_ERROR: insertmode = COL_INSERT_DUPERROR; doinsert = 1; break; case INI_MV1S_PRESERVE: insertmode = COL_INSERT_DUPERROR; doinsert = 1; suppress = 1; break; case INI_MV1S_ALLOW: insertmode = COL_INSERT_NOCHECK; doinsert = 1; break; case INI_MV1S_OVERWRITE: /* Special handling */ case INI_MV1S_DETECT: default: break; } /* Do not insert but search for dups first */ if (!doinsert) { TRACE_INFO_STRING("Overwrite mode. Looking for:", (char *)(merging ? po->merge_key : po->key)); error = col_get_item(section, merging ? po->merge_key : po->key, COL_TYPE_BINARY, COL_TRAVERSE_DEFAULT, &item); if (error) { TRACE_ERROR_NUMBER("Failed searching for dup", error); value_destroy(vo); return error; } /* Check if there is a dup */ if (item) { /* Check if we are in the detect mode */ if (mergemode == INI_MV1S_DETECT) { po->merge_error = EEXIST; /* There is a dup - inform user about it and continue */ error = save_error(po->el, merging ? po->seclinenum : po->keylinenum, merging ? ERR_DUPKEYSEC : ERR_DUPKEY, ERROR_TXT); if (error) { TRACE_ERROR_NUMBER("Failed to save error", error); value_destroy(vo); return error; } doinsert = 1; insertmode = COL_INSERT_NOCHECK; } else { /* Dup exists - update it */ vo_old = *((struct value_obj **)(col_get_item_data(item))); error = col_modify_binary_item(item, NULL, &vo, sizeof(struct value_obj *)); if (error) { TRACE_ERROR_NUMBER("Failed updating the value", error); value_destroy(vo); return error; } /* If we failed to update it is better to leak then crash, * so destroy original value only on the successful update. */ value_destroy(vo_old); } } else { /* No dup found so we can insert with no check */ doinsert = 1; insertmode = COL_INSERT_NOCHECK; } } if (doinsert) { /* Add value to collection */ error = col_insert_binary_property(section, NULL, COL_DSP_END, NULL, 0, insertmode, merging ? po->merge_key : po->key, &vo, sizeof(struct value_obj *)); if (error) { value_destroy(vo); if ((suppress) && (error == EEXIST)) { TRACE_INFO_STRING("Preseved exisitng value", (char *)(merging ? po->merge_key : po->key)); } else { /* Check if this is a critical error or not */ if ((mergemode == INI_MV1S_ERROR) && (error == EEXIST)) { TRACE_ERROR_NUMBER("Failed to add value object " "to the section", error); error2 = save_error(po->el, merging ? po->seclinenum : po->keylinenum, merging ? ERR_DUPKEYSEC : ERR_DUPKEY, ERROR_TXT); if (error2) { TRACE_ERROR_NUMBER("Failed to save error", error2); return error2; } return error; } else { TRACE_ERROR_NUMBER("Failed to add value object" " to the section", error); return error; } } } } if (!merging) { free(po->key); po->key = NULL; po->key_len = 0; } TRACE_FLOW_EXIT(); return EOK; }
/* Merge contents of the section */ static int merge_section(struct parser_obj *po) { int error = EOK; struct collection_item *item = NULL; struct value_obj *vo = NULL; int work_to_do = 1; const char *key; TRACE_FLOW_ENTRY(); do { TRACE_INFO_STRING("Top of the merge loop", ""); item = NULL; error = col_extract_item_from_current(po->sec, COL_DSP_FRONT, NULL, 0, COL_TYPE_ANY, &item); if ((error) && (error != ENOENT)) { TRACE_ERROR_NUMBER("Failed to extract item.", error); return error; } if (item) { TRACE_INFO_STRING("Item found:", col_get_item_property(item, NULL)); if (strncmp(col_get_item_property(item, NULL), INI_SECTION_KEY, 1) == 0) { /* Just ignore the first item */ vo = *((struct value_obj **)(col_get_item_data(item))); value_destroy(vo); col_delete_item(item); continue; } po->merge_vo = *((struct value_obj **)(col_get_item_data(item))); key = col_get_item_property(item, NULL); /* To be able to use po->merge_key in the loop * we have to overcome constraints imposed by * the "const" declaration. */ memcpy(&(po->merge_key), &key, sizeof(char *)); /* Use the value processing function to inser the value */ error = complete_value_processing(po); /* In case of error value is already cleaned */ po->merge_vo = NULL; po->merge_key = NULL; col_delete_item(item); /* Now we can check the error */ if (error) { TRACE_ERROR_NUMBER("Failed to merge item.", error); return error; } } else { TRACE_INFO_STRING("No more items:", ""); work_to_do = 0; } } while (work_to_do); /* If we reached this place the incoming section is empty. * but just to be safe clean with callback. */ col_destroy_collection_with_cb(po->sec, ini_cleanup_cb, NULL); po->sec = NULL; TRACE_FLOW_EXIT(); return EOK; }
/* Clean all items in the section */ int empty_section(struct collection_item *sec) { int error = EOK; struct collection_item *item = NULL; struct collection_item *save_item = NULL; struct value_obj *vo = NULL; int work_to_do = 1; TRACE_FLOW_ENTRY(); do { item = NULL; error = col_extract_item_from_current(sec, COL_DSP_FRONT, NULL, 0, COL_TYPE_ANY, &item); if ((error) && (error != ENOENT)) { TRACE_ERROR_NUMBER("Failed to extract item.", error); return error; } if (item) { TRACE_INFO_STRING("Item found:", col_get_item_property(item, NULL)); if (strncmp(col_get_item_property(item, NULL), INI_SECTION_KEY, 1) == 0) { /* Just ignore the first item */ save_item = item; continue; } vo = *((struct value_obj **)(col_get_item_data(item))); value_destroy(vo); col_delete_item(item); } else { TRACE_INFO_STRING("No more items:", ""); /* Restore saved item */ error = col_insert_item(sec, NULL, save_item, COL_DSP_END, NULL, 0, COL_INSERT_NOCHECK); if (error) { TRACE_ERROR_NUMBER("Failed to restore item.", error); return error; } work_to_do = 0; } } while (work_to_do); TRACE_FLOW_EXIT(); return EOK; }
/* Internal function that prints errors */ static void print_error_list(FILE *file, struct collection_item *error_list, int cclass, char *wrong_col_error, char *failed_to_process, char *error_header, char *line_format, int family) { struct collection_iterator *iterator; int error; struct collection_item *item = NULL; struct parse_error *pe; unsigned int count; TRACE_FLOW_STRING("print_error_list", "Entry"); /* If we have something to print print it */ if (error_list == NULL) { TRACE_ERROR_STRING("No error list",""); return; } /* Make sure we go the right collection */ if (!col_is_of_class(error_list, cclass)) { TRACE_ERROR_STRING("Wrong collection class:", wrong_col_error); fprintf(file,"%s\n", wrong_col_error); return; } /* Bind iterator */ error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT); if (error) { TRACE_ERROR_STRING("Error (bind):", failed_to_process); fprintf(file, "%s\n", failed_to_process); return; } while(1) { /* Loop through a collection */ error = col_iterate_collection(iterator, &item); if (error) { TRACE_ERROR_STRING("Error (iterate):", failed_to_process); fprintf(file, "%s\n", failed_to_process); col_unbind_iterator(iterator); return; } /* Are we done ? */ if (item == NULL) break; /* Process collection header */ if (col_get_item_type(item) == COL_TYPE_COLLECTION) { col_get_collection_count(item, &count); if (count <= 2) break; } else if (col_get_item_type(item) == COL_TYPE_STRING) { fprintf(file, error_header, (char *)col_get_item_data(item)); } else { /* Put error into provided format */ pe = (struct parse_error *)(col_get_item_data(item)); fprintf(file, line_format, col_get_item_property(item, NULL), /* Error or warning */ pe->error, /* Error */ pe->line, /* Line */ ini_get_error_str(pe->error, family)); /* Error str */ } } /* Do not forget to unbind iterator - otherwise there will be a leak */ col_unbind_iterator(iterator); TRACE_FLOW_STRING("print_error_list", "Exit"); }