/* Get element */ void *ref_array_get(struct ref_array *ra, uint32_t idx, void *acptr) { TRACE_FLOW_ENTRY(); if (!ra) { TRACE_ERROR_STRING("Uninitialized argument.", ""); return NULL; } if (idx >= ra->len) { TRACE_INFO_NUMBER("Invalid idx.", idx); return NULL; } TRACE_INFO_NUMBER("Index: ", idx); if (acptr) { TRACE_INFO_STRING("Copying data.", ""); memcpy(acptr, (unsigned char *)(ra->storage) + idx * ra->elsize, ra->elsize); } TRACE_FLOW_EXIT(); return (unsigned char *)(ra->storage) + idx * ra->elsize; }
static int save_portion(struct ref_array *raw_lines, struct ref_array *raw_lengths, const char* buf, uint32_t len) { int error = EOK; char *copy = NULL; uint32_t adj = 0; TRACE_FLOW_ENTRY(); /* Add leading space only if there is * a) no space * b) it is not an empty line * c) it is now a first line */ if ((buf[0] != ' ') && (buf[0] != '\t') && (len != 0) && (ref_array_len(raw_lines) != 0)) adj = 1; copy = malloc(len + adj + 1); if (!copy) { TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM); return ENOMEM; } memcpy(copy + adj, buf, len); len += adj; copy[len] = 0; /* If the section being saved is not starting * with space add a space. */ if (adj) copy[0] = ' '; error = ref_array_append(raw_lines, (void *)(©)); if (error) { TRACE_ERROR_NUMBER("Failed to append line", error); free(copy); return error; } error = ref_array_append(raw_lengths, (void *)(&len)); if (error) { TRACE_ERROR_NUMBER("Failed to append length", error); return error; } TRACE_INFO_STRING("Added string:", (char *)copy); TRACE_INFO_NUMBER("Added number:", len); TRACE_FLOW_EXIT(); return EOK; }
/* Print the collection using iterator */ int col_print_collection2(struct collection_item *handle) { struct collection_iterator *iterator = NULL; int error = EOK; struct collection_item *item = NULL; int nest_level = 0; int dummy = 0; int line = 1; TRACE_FLOW_STRING("col_print_collection2", "Entry"); /* If we have something to print print it */ if (handle == NULL) { TRACE_ERROR_STRING("No error list", ""); return EINVAL; } /* Bind iterator */ error = col_bind_iterator(&iterator, handle, COL_TRAVERSE_DEFAULT | COL_TRAVERSE_END | COL_TRAVERSE_SHOWSUB); if (error) { TRACE_ERROR_NUMBER("Error (bind):", error); return error; } do { /* Loop through a collection */ error = col_iterate_collection(iterator, &item); if (error) { TRACE_ERROR_NUMBER("Error (iterate):", error); col_unbind_iterator(iterator); return error; } /* Are we done ? */ if (item == NULL) break; if (item->type != COL_TYPE_END) printf("%05d", line); col_debug_handle(item->property, item->property_len, item->type, item->data, item->length, (void *)(&nest_level), &dummy); line++; } while(1); /* Do not forget to unbind iterator - otherwise there will be a leak */ col_unbind_iterator(iterator); TRACE_INFO_STRING("col_print_collection2", "Exit"); return EOK; }
/* Inspect the line */ static int parser_inspect(struct parser_obj *po) { int error = EOK; uint32_t action = PARSE_DONE; TRACE_FLOW_ENTRY(); TRACE_INFO_STRING("Buffer:", po->last_read); TRACE_INFO_NUMBER("In comment:", po->inside_comment); if (check_for_comment(po->last_read, po->last_read_len, !(po->parse_flags & INI_PARSE_NO_C_COMMENTS), &(po->inside_comment))) { error = handle_comment(po, &action); if (error) { TRACE_ERROR_NUMBER("Failed to process comment", error); return error; } } else if (isspace(*(po->last_read))) { error = handle_space(po, &action); if (error) { TRACE_ERROR_NUMBER("Failed to process line wrapping", error); return error; } } else if (*(po->last_read) == '[') { error = handle_section(po, &action); if (error) { TRACE_ERROR_NUMBER("Failed to save section", error); return error; } } else { error = handle_kvp(po, &action); if (error) { TRACE_ERROR_NUMBER("Failed to save kvp", error); return error; } } /* Move to the next action */ error = col_enqueue_unsigned_property(po->queue, PARSE_ACTION, action); if (error) { TRACE_ERROR_NUMBER("Failed to schedule an action", error); return error; } TRACE_FLOW_EXIT(); return error; }
/* Create value from a referenced array */ int value_create_from_refarray(struct ref_array *raw_lines, struct ref_array *raw_lengths, uint32_t line, uint32_t origin, uint32_t key_len, uint32_t boundary, struct ini_comment *ic, struct value_obj **vo) { int error = EOK; struct value_obj *new_vo = NULL; TRACE_FLOW_ENTRY(); if ((!raw_lines) || (!raw_lengths) || (!vo)) { TRACE_ERROR_NUMBER("Invalid argument", EINVAL); return EINVAL; } new_vo = malloc(sizeof(struct value_obj)); if (!new_vo) { TRACE_ERROR_NUMBER("No memory", ENOMEM); return ENOMEM; } /* We are not using references here since * it will be inconsistent with the way * how comment is handled. * We could have added references here and make * comment keep references but it seems to be * and overhead in this case. */ new_vo->raw_lines = raw_lines; new_vo->raw_lengths = raw_lengths; new_vo->origin = origin; new_vo->line = line; new_vo->keylen = key_len; new_vo->boundary = boundary; new_vo->ic = ic; error = value_unfold(new_vo->raw_lines, new_vo->raw_lengths, &(new_vo->unfolded)); if (error) { TRACE_ERROR_NUMBER("Failed to unfold", error); value_destroy(new_vo); return error; } TRACE_INFO_STRING("Unfolded:", (const char *)simplebuffer_get_buf(new_vo->unfolded)); *vo = new_vo; TRACE_FLOW_EXIT(); return error; }
/* Unfold the value represented by the array */ static int value_unfold(struct ref_array *raw_lines, struct ref_array *raw_lengths, struct simplebuffer **unfolded) { int error; struct simplebuffer *oneline = NULL; uint32_t len = 0; char *ptr = NULL; uint32_t i = 0; char *part = NULL; TRACE_FLOW_ENTRY(); error = simplebuffer_alloc(&oneline); if (error) { TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error); return error; } for (;;) { /* Get line */ ptr = ref_array_get(raw_lines, i, NULL); if (ptr) { /* Get its length */ ref_array_get(raw_lengths, i, (void *)&len); part = *((char **)(ptr)); TRACE_INFO_STRING("Value:", part); TRACE_INFO_NUMBER("Lenght:", len); error = simplebuffer_add_raw(oneline, part, len, INI_VALUE_BLOCK); if (error) { TRACE_ERROR_NUMBER("Failed to add string", error); simplebuffer_free(oneline); return error; } i++; } else break; } *unfolded = oneline; 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; }
/* Cleanup callback for lines array */ void value_lines_cleanup_cb(void *elem, ref_array_del_enum type, void *data) { char *part; TRACE_FLOW_ENTRY(); part = *((char **)(elem)); TRACE_INFO_STRING("Freeing:", part); free(part); TRACE_FLOW_EXIT(); }
/* Function to check uid or gid */ static int check_id(struct collection_item *metadata, unsigned long id, const char *key) { int error = EOK; struct collection_item *item = NULL; unsigned long fid; TRACE_FLOW_STRING("check_id", "Entry"); TRACE_INFO_STRING("Key", key); error = get_config_item(INI_META_SEC_ACCESS, key, metadata, &item); if (error) { TRACE_ERROR_NUMBER("Internal collection error.", error); return error; } /* Entry is supposed to be there so it is an error * is the item is not found. */ if (item == NULL) { TRACE_ERROR_NUMBER("Expected item is not found.", ENOENT); return ENOENT; } fid = get_ulong_config_value(item, 1, -1, &error); if ((error) || (fid == -1)) { TRACE_ERROR_NUMBER("Conversion failed", EINVAL); return EINVAL; } if (id != fid) { TRACE_ERROR_NUMBER("File ID:", fid); TRACE_ERROR_NUMBER("ID passed in.", id); TRACE_ERROR_NUMBER("Access denied.", EACCES); return EACCES; } TRACE_FLOW_STRING("check_id", "Exit"); return EOK; }
static unsigned long get_checked_value(struct collection_item *metadata, const char *key, int *err) { int error = EOK; struct collection_item *item = NULL; unsigned long value; TRACE_FLOW_STRING("get_checked_value", "Entry"); TRACE_INFO_STRING("Key", key); error = get_config_item(INI_META_SEC_ACCESS, key, metadata, &item); if (error) { TRACE_ERROR_NUMBER("Internal collection error.", error); *err = error; return 0; } /* Entry is supposed to be there so it is an error * is the item is not found. */ if (item == NULL) { TRACE_ERROR_NUMBER("Expected item is not found.", ENOENT); *err = ENOENT; return 0; } value = get_ulong_config_value(item, 1, -1, &error); if ((error) || (value == -1)) { TRACE_ERROR_NUMBER("Conversion failed", EINVAL); *err = EINVAL; return 0; } *err = 0; TRACE_FLOW_NUMBER("get_checked_value Returning", value); return value; }
/* Delete the array */ void ref_array_destroy(struct ref_array *ra) { int idx; TRACE_FLOW_ENTRY(); /* Check if array is not NULL */ if (!ra) { TRACE_INFO_STRING("Uninitialized array.", "Might be Ok..."); return; } TRACE_INFO_NUMBER("Current reference count: ", ra->refcount); if (ra->refcount) { /* Decrease reference count */ ra->refcount--; if (ra->refcount == 0) { TRACE_INFO_NUMBER("It is time to delete array. Count:", ra->refcount); if (ra->cb) { for (idx = 0; idx < ra->len; idx++) { ra->cb((unsigned char *)(ra->storage) + idx * ra->elsize, REF_ARRAY_DESTROY, ra->cb_data); } } free(ra->storage); free(ra); } } else { /* Should never be here... * This can happen if the caller by mistake would try to * destroy the object from within the callback. Brrr.... */ TRACE_ERROR_STRING("Reference count is 0.", "Coding error???"); } TRACE_FLOW_EXIT(); }
/* Get items from the collection one by one following the tree */ int col_iterate_collection(struct collection_iterator *iterator, struct collection_item **item) { int error; struct collection_item *current; struct collection_item *other; TRACE_FLOW_STRING("col_iterate_collection", "Entry."); /* Check if we have storage for item */ if (item == NULL) { TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); return EINVAL; } while (1) { TRACE_INFO_NUMBER("Stack depth:", iterator->stack_depth); if (iterator->stack_depth == 0) { /* Re-init so if we continue looping we start over */ iterator->stack[0] = iterator->top; iterator->stack_depth++; iterator->item_level = 0; } /* Is current item available */ current = iterator->stack[iterator->stack_depth - 1]; iterator->item_level = iterator->stack_depth - 1; /* Are we done? */ if (((iterator->stack_depth - 1) == iterator->pin_level) && (iterator->pin == current)) { if (iterator->can_break) { TRACE_FLOW_STRING("We are done.", ""); *item = NULL; iterator->can_break = 0; return EOK; } else iterator->can_break = 1; } /* We are not done so check if we have an item */ if (current != NULL) { TRACE_INFO_STRING("Current item:", current->property); TRACE_INFO_NUMBER("Current item type:", current->type); /* Is this a collection reference */ if (current->type == COL_TYPE_COLLECTIONREF) { /* We do follow references? */ TRACE_INFO_STRING("Current item:", "collection reference"); if ((iterator->flags & COL_TRAVERSE_IGNORE) == 0) { /* We should not ignore - then move on */ TRACE_INFO_STRING("Collection references are not ignored", ""); error = col_grow_stack(iterator, iterator->stack_depth + 1); if (error) { TRACE_ERROR_NUMBER("Error growing stack.", error); return error; } /* Do we need to go deeper than one level ? */ if ((iterator->flags & COL_TRAVERSE_ONELEVEL) == 0) { TRACE_INFO_STRING("Need to go deeper", ""); /* We need to go deeper... */ /* Do we need to show headers but not reference? */ if ((iterator->flags & COL_TRAVERSE_ONLYSUB) != 0) { TRACE_INFO_STRING("Instructed to show header not reference", ""); other = *((struct collection_item **)current->data); iterator->stack[iterator->stack_depth] = other->next; iterator->item_level = iterator->stack_depth; *item = other; } /* Do we need to show both? */ else if ((iterator->flags & COL_TRAVERSE_SHOWSUB) != 0) { TRACE_INFO_STRING("Instructed to show header and reference",""); iterator->stack[iterator->stack_depth] = *((struct collection_item **)(current->data)); *item = current; /* Do not need to adjust level here */ } /* Do not show either */ else if ((iterator->flags & COL_TRAVERSE_FLAT) != 0) { TRACE_INFO_STRING("Instructed not to show header and reference",""); other = *((struct collection_item **)current->data); iterator->stack[iterator->stack_depth] = other->next; iterator->stack[iterator->stack_depth - 1] = current->next; iterator->stack_depth++; /* Do not need to adjust level here */ continue; } /* We need to show reference only */ else { TRACE_INFO_STRING("Instructed to show reference only", ""); other = *((struct collection_item **)current->data); TRACE_INFO_STRING("Sub collection:", other->property); TRACE_INFO_NUMBER("Sub collection type:", other->type); iterator->stack[iterator->stack_depth] = other->next; if (other->next != NULL) { TRACE_INFO_STRING("Will show this item next time:", other->next->property); TRACE_INFO_NUMBER("Will show this item next time type:", other->next->type); } *item = current; TRACE_INFO_NUMBER("Level of the reference:", iterator->item_level); /* Do not need to adjust level here */ } TRACE_INFO_STRING("We return item:", (*item)->property); TRACE_INFO_NUMBER("We return item type:", (*item)->type); TRACE_INFO_STRING("Moving to the next item on the previous item in stack", ""); iterator->stack[iterator->stack_depth - 1] = current->next; iterator->stack_depth++; } else { TRACE_INFO_STRING("Instructed to parse just one level", ""); /* On one level - just return current */ *item = current; TRACE_INFO_STRING("Moving to the next item on one level", ""); iterator->stack[iterator->stack_depth - 1] = current->next; } break; } else { /* We need to ignore references so move to the next item */ TRACE_INFO_STRING("Stepping over the reference", ""); iterator->stack[iterator->stack_depth - 1] = current->next; continue; } } else { /* Got a normal item - return it and move to the next one */ if ((current->type == COL_TYPE_COLLECTION) && ((iterator->flags & COL_TRAVERSE_FLAT) != 0) && (iterator->stack_depth > 1)) { TRACE_INFO_STRING("Header of the sub collection in flat case ", ""); iterator->stack[iterator->stack_depth - 1] = current->next; continue; } else { TRACE_INFO_STRING("Simple item", ""); *item = current; iterator->stack[iterator->stack_depth - 1] = current->next; } break; } } else { /* Item is NULL */ TRACE_INFO_STRING("Finished level", "moving to upper level"); iterator->stack_depth--; /* Remember that item_level is zero based while depth is size * so we decrease and then assign. */ TRACE_INFO_NUMBER("Stack depth at the end:", iterator->stack_depth); if ((iterator->flags & COL_TRAVERSE_END) != 0) { /* Show end element * a) If we are flattening but at the top * b) We are not flattening */ if ((((iterator->flags & COL_TRAVERSE_FLAT) != 0) && (iterator->stack_depth == 0)) || ((iterator->flags & COL_TRAVERSE_FLAT) == 0)) { /* Return dummy entry to indicate the end of the collection */ TRACE_INFO_STRING("Finished level", "told to return END"); *item = iterator->end_item; break; } } else { /* Move to next level */ continue; } } } TRACE_FLOW_STRING("col_iterate_collection", "Exit"); return EOK; }
/* Use JSON formal for serialization */ int col_json(const char *property_in, int property_len_in, int type, void *data_in, int length_in, void *custom_data, int *dummy) { int len; struct col_serial_data *buf_data; const char *property; const void *data; int property_len; int length; int error = EOK; int i; TRACE_FLOW_STRING("col_json","Entry point"); *dummy = 0; /* Check is there is buffer. If not allocate */ buf_data = (struct col_serial_data *)custom_data; if (buf_data == NULL) { TRACE_ERROR_STRING("Error.", "Storage data is not passed in!"); return EINVAL; } if (buf_data->buffer == NULL) { TRACE_INFO_STRING("First time use.", "Allocating buffer."); buf_data->buffer = malloc(BLOCK_SIZE); if (buf_data->buffer == NULL) { TRACE_ERROR_NUMBER("Error. Failed to allocate memory.", ENOMEM); return ENOMEM; } buf_data->buffer[0] = '\0'; buf_data->length = 0; buf_data->size = BLOCK_SIZE; } TRACE_INFO_NUMBER("Buffer len: ", buf_data->length); TRACE_INFO_NUMBER("Buffer size: ", buf_data->size); TRACE_INFO_STRING("Buffer: ", buf_data->buffer); /* Check the beginning of the collection */ if (type == COL_TYPE_COLLECTION) { TRACE_INFO_STRING("Serializing collection: ", property_in); TRACE_INFO_STRING("First header. ", ""); error = col_put_marker(buf_data, "{", 1); if (error != EOK) return error; property = TEXT_EVENT; property_len = TEXT_EVENTLEN; data = property_in; length = property_len_in + 1; type = COL_TYPE_STRING; buf_data->nest_level++; } /* Check for subcollections */ else if (type == COL_TYPE_COLLECTIONREF) { /* Skip */ TRACE_FLOW_STRING("col_serialize", "skip reference return"); return EOK; } /* Check for the end of the collection */ else if (type == COL_TYPE_END) { if ((buf_data->length > 0) && (buf_data->buffer[buf_data->length-1] == ',')) { buf_data->length--; buf_data->buffer[buf_data->length] = '\0'; } if (buf_data->nest_level > 0) { buf_data->nest_level--; error = col_put_marker(buf_data, "}", 1); if (error != EOK) return error; } TRACE_FLOW_STRING("col_serialize", "end collection item processed returning"); return EOK; } else { property = property_in; property_len = property_len_in; data = data_in; length = length_in; } TRACE_INFO_STRING("Property: ", property); TRACE_INFO_NUMBER("Property length: ", property_len); /* Start with property and ":" */ if ((error = col_put_marker(buf_data, "\"", 1)) || (error = col_put_marker(buf_data, property, property_len)) || (error = col_put_marker(buf_data, "\":", 2))) { TRACE_ERROR_NUMBER("put_marker returned error: ", error); return error; } /* Get projected length of the item */ len = col_get_data_len(type,length); TRACE_INFO_NUMBER("Expected data length: ",len); TRACE_INFO_STRING("Buffer so far: ", buf_data->buffer); /* Make sure we have enough space */ if ((error = col_grow_buffer(buf_data, len))) { TRACE_ERROR_NUMBER("grow_buffer returned error: ", error); return error; } /* Add the value */ switch (type) { case COL_TYPE_STRING: /* Escape double quotes */ len = col_copy_esc(&buf_data->buffer[buf_data->length], (const char *)(data), '"'); break; case COL_TYPE_BINARY: buf_data->buffer[buf_data->length] = '\''; for (i = 0; i < length; i++) sprintf(&buf_data->buffer[buf_data->length + i *2] + 1, "%02X", (unsigned int)(((const unsigned char *)(data))[i])); len = length * 2 + 1; buf_data->buffer[buf_data->length + len] = '\''; len++; break; case COL_TYPE_INTEGER: len = sprintf(&buf_data->buffer[buf_data->length], "%d", *((const int32_t *)(data))); break; case COL_TYPE_UNSIGNED: len = sprintf(&buf_data->buffer[buf_data->length], "%u", *((const uint32_t *)(data))); break; case COL_TYPE_LONG: len = sprintf(&buf_data->buffer[buf_data->length], "%lld", (long long int)(*((const int64_t *)(data)))); break; case COL_TYPE_ULONG: len = sprintf(&buf_data->buffer[buf_data->length], "%llu", (long long unsigned)(*((const uint64_t *)(data)))); break; case COL_TYPE_DOUBLE: len = sprintf(&buf_data->buffer[buf_data->length], "%.4f", *((const double *)(data))); break; case COL_TYPE_BOOL: len = sprintf(&buf_data->buffer[buf_data->length], "%s", (*((const unsigned char *)(data))) ? "true" : "false"); break; default: buf_data->buffer[buf_data->length] = '\0'; len = 0; break; } /* Adjust length */ buf_data->length += len; buf_data->buffer[buf_data->length] = '\0'; /* Always put a comma at the end */ error = col_put_marker(buf_data, ",", 1); if (error != EOK) { TRACE_ERROR_NUMBER("put_marker returned error: ", error); return error; } TRACE_INFO_STRING("Data: ", buf_data->buffer); TRACE_FLOW_STRING("col_json", "Exit point"); return EOK; }
/* Handle key-value pair */ static int handle_kvp(struct parser_obj *po, uint32_t *action) { int error = EOK; char *eq = NULL; uint32_t len = 0; char *dupval = NULL; char *str; uint32_t full_len; TRACE_FLOW_ENTRY(); str = po->last_read; full_len = po->last_read_len; TRACE_INFO_STRING("Last read:", str); /* Trim spaces at the beginning */ while ((full_len > 0) && (isspace(*(str)))) { str++; full_len--; } /* Check if we have the key */ if (*(str) == '=') { TRACE_ERROR_STRING("No key", str); po->last_error = ERR_NOKEY; *action = PARSE_ERROR; return EOK; } /* Find "=" */ eq = strchr(str, '='); if (eq == NULL) { TRACE_ERROR_STRING("No equal sign", str); po->last_error = ERR_NOEQUAL; *action = PARSE_ERROR; return EOK; } /* Strip spaces around "=" */ /* Since eq > str we can substract 1 */ len = eq - str - 1; while ((len > 0) && (isspace(*(str + len)))) len--; /* Adjust length properly */ len++; /* Check the key length */ if(len >= MAX_KEY) { TRACE_ERROR_STRING("Key name is too long", str); po->last_error = ERR_LONGKEY; *action = PARSE_ERROR; return EOK; } if (po->key) { /* Complete processing of the previous value */ error = complete_value_processing(po); if (error) { TRACE_ERROR_NUMBER("Failed to complete value processing", error); return error; } } /* Dup the key name */ po->key = malloc(len + 1); if (!(po->key)) { TRACE_ERROR_NUMBER("Failed to dup key", ENOMEM); return ENOMEM; } memcpy(po->key, str, len); *(po->key + len) = '\0'; po->key_len = len; TRACE_INFO_STRING("Key:", po->key); TRACE_INFO_NUMBER("Keylen:", po->key_len); len = full_len - (eq - str) - 1; /* Trim spaces after equal sign */ eq++; while (isspace(*eq)) { eq++; len--; } TRACE_INFO_STRING("VALUE:", eq); TRACE_INFO_NUMBER("LENGTH:", len); /* Dup the part of the value */ dupval = malloc(len + 1); if (!dupval) { TRACE_ERROR_NUMBER("Failed to dup value", ENOMEM); return ENOMEM; } memcpy(dupval, eq, len); *(dupval + len) = '\0'; /* Create new arrays */ error = value_create_arrays(&(po->raw_lines), &(po->raw_lengths)); if (error) { TRACE_ERROR_NUMBER("Failed to create arrays", error); free(dupval); return error; } /* Save a duplicated part in the value */ error = value_add_to_arrays(dupval, len, po->raw_lines, po->raw_lengths); if (error) { TRACE_ERROR_NUMBER("Failed to add value to arrays", error); free(dupval); return error; } /* Save the line number of the last found key */ po->keylinenum = po->linenum; /* Prepare for reading */ free(po->last_read); po->last_read = NULL; po->last_read_len = 0; *action = PARSE_READ; 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; }
/* Function to read next line from the file */ static int parser_save_section(struct parser_obj *po) { int error = EOK; uint32_t mergemode; int merge = 0; TRACE_FLOW_ENTRY(); if (po->sec) { TRACE_INFO_STRING("Section exists.", ""); /* First detect if we have collision */ error = check_section_collision(po); if (error) { TRACE_ERROR_NUMBER("Failed to check for collision", error); return error; } if (po->merge_sec) { TRACE_INFO_STRING("Merge collision detected", ""); mergemode = po->collision_flags & INI_MS_MASK; switch (mergemode) { case INI_MS_ERROR: /* Report error and return */ TRACE_INFO_STRING("Reporting error", "duplicate section"); error = save_error(po->el, po->seclinenum, ERR_DUPSECTION, ERROR_TXT); if (error) { TRACE_ERROR_NUMBER("Failed to " "save error", error); return error; } /* Return error */ TRACE_FLOW_RETURN(EEXIST); return EEXIST; case INI_MS_PRESERVE: /* Delete new section */ TRACE_INFO_STRING("Preserve mode", ""); col_destroy_collection_with_cb( po->sec, ini_cleanup_cb, NULL); po->sec = NULL; break; case INI_MS_OVERWRITE: /* Empty existing section */ TRACE_INFO_STRING("Ovewrite mode", ""); error = empty_section(po->merge_sec); if (error) { TRACE_ERROR_NUMBER("Failed to " "empty section", error); return error; } merge = 1; break; case INI_MS_DETECT: /* Detect mode */ TRACE_INFO_STRING("Detect mode", ""); po->merge_error = EEXIST; error = save_error(po->el, po->seclinenum, ERR_DUPSECTION, ERROR_TXT); if (error) { TRACE_ERROR_NUMBER("Failed to " "save error", error); return error; } merge = 1; break; case INI_MS_MERGE: /* Merge */ default: TRACE_INFO_STRING("Merge mode", ""); merge = 1; break; } if (merge) { error = merge_section(po); if (error) { TRACE_ERROR_NUMBER("Failed to merge section", error); return error; } } po->merge_sec = NULL; } else { /* Add section to configuration */ TRACE_INFO_STRING("Now adding collection", ""); error = col_add_collection_to_collection(po->top, NULL, NULL, po->sec, COL_ADD_MODE_EMBED); if (error) { TRACE_ERROR_NUMBER("Failed to embed section", error); return error; } po->sec = NULL; } } 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; }
/* Create a copy of the value */ int value_copy(struct value_obj *vo, struct value_obj **copy_vo) { int error = EOK; struct value_obj *new_vo = NULL; struct simplebuffer *oneline = NULL; TRACE_FLOW_ENTRY(); if ((!copy_vo) || (!vo)) { TRACE_ERROR_NUMBER("Invalid argument", EINVAL); return EINVAL; } /* Create buffer to hold the value */ error = simplebuffer_alloc(&oneline); if (error) { TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error); return error; } /* Put value into the buffer */ error = simplebuffer_add_str(oneline, (const char *)simplebuffer_get_buf(vo->unfolded), simplebuffer_get_len(vo->unfolded), INI_VALUE_BLOCK); if (error) { TRACE_ERROR_NUMBER("Failed to add string", error); simplebuffer_free(oneline); return error; } /* Acllocate new INI value structure */ new_vo = malloc(sizeof(struct value_obj)); if (!new_vo) { TRACE_ERROR_NUMBER("No memory", ENOMEM); simplebuffer_free(oneline); return ENOMEM; } new_vo->origin = vo->origin; new_vo->line = vo->line; new_vo->unfolded = oneline; new_vo->keylen = vo->keylen; new_vo->boundary = vo->boundary; new_vo->raw_lines = NULL; new_vo->raw_lengths = NULL; error = value_create_arrays(&(new_vo->raw_lines), &(new_vo->raw_lengths)); if (error) { TRACE_ERROR_NUMBER("Failed to fold", error); value_destroy(new_vo); return error; } /* Create arrays by folding the value */ error = value_fold(new_vo->unfolded, new_vo->keylen, new_vo->boundary, new_vo->raw_lines, new_vo->raw_lengths); if (error) { TRACE_ERROR_NUMBER("Failed to fold", error); value_destroy(new_vo); return error; } /* Copy comment */ if (vo->ic) { error = ini_comment_copy(vo->ic, &new_vo->ic); if (error) { TRACE_ERROR_NUMBER("Failed to copy comment", error); value_destroy(new_vo); return error; } } else new_vo->ic = NULL; *copy_vo = new_vo; TRACE_INFO_STRING("Orig value:", (const char *)simplebuffer_get_buf(vo->unfolded)); TRACE_INFO_STRING("Copy value:", (const char *)simplebuffer_get_buf(new_vo->unfolded)); TRACE_INFO_NUMBER("Orig value num lines:", ref_array_len(vo->raw_lengths)); TRACE_INFO_NUMBER("Copy value num lines:", ref_array_len(new_vo->raw_lengths)); TRACE_FLOW_EXIT(); return error; }
/* Function to read next line from the file */ static int parser_read(struct parser_obj *po) { int error = EOK; char *buffer = NULL; ssize_t res = 0; size_t len = 0; int32_t i = 0; uint32_t action; TRACE_FLOW_ENTRY(); /* Adjust line number */ (po->linenum)++; /* Get line from the file */ res = getline(&buffer, &len, po->file); if (res == -1) { if (feof(po->file)) { TRACE_FLOW_STRING("Read nothing", ""); if (po->inside_comment) { action = PARSE_ERROR; po->last_error = ERR_BADCOMMENT; } else action = PARSE_POST; } else { TRACE_ERROR_STRING("Error reading", ""); action = PARSE_ERROR; po->last_error = ERR_READ; } if(buffer) free(buffer); } else { /* Read Ok */ len = res; TRACE_INFO_STRING("Read line ok:", buffer); TRACE_INFO_NUMBER("Length:", len); TRACE_INFO_NUMBER("Strlen:", strlen(buffer)); if (buffer[0] == '\0') { /* Empty line - read again (should not ever happen) */ action = PARSE_READ; free(buffer); } else { /* Check length */ if (len >= BUFFER_SIZE) { TRACE_ERROR_STRING("Too long", ""); action = PARSE_ERROR; po->last_error = ERR_LONGDATA; free(buffer); } else { /* Trim end line */ i = len - 1; while ((i >= 0) && ((buffer[i] == '\r') || (buffer[i] == '\n'))) { TRACE_INFO_NUMBER("Offset:", i); TRACE_INFO_NUMBER("Code:", buffer[i]); buffer[i] = '\0'; i--; } po->last_read = buffer; po->last_read_len = i + 1; action = PARSE_INSPECT; TRACE_INFO_STRING("Line:", po->last_read); TRACE_INFO_NUMBER("Linelen:", po->last_read_len); } } } /* Move to the next action */ error = col_enqueue_unsigned_property(po->queue, PARSE_ACTION, action); if (error) { TRACE_ERROR_NUMBER("Failed to schedule an action", error); return error; } TRACE_FLOW_EXIT(); return EOK; }
/* Serialize value */ int value_serialize(struct value_obj *vo, const char *key, struct simplebuffer *sbobj) { int error = EOK; uint32_t num = 0; uint32_t i = 0; uint32_t len = 0; char *commentline = NULL; char *ptr = NULL; char *part = NULL; int sec = 0; uint32_t vln = 0; TRACE_FLOW_ENTRY(); TRACE_INFO_STRING("Serializing key:", key); if (!vo) { TRACE_ERROR_NUMBER("Invalid input parameter", EINVAL); return EINVAL; } /* Put comment first */ if (vo->ic) { /* Get number of lines in the comment */ error = ini_comment_get_numlines(vo->ic, &num); if (error) { TRACE_ERROR_NUMBER("Failed to get number of lines", errno); return error; } for (i = 0; i < num; i++) { len = 0; commentline = NULL; error = ini_comment_get_line(vo->ic, i, &commentline, &len); if (error) { TRACE_ERROR_NUMBER("Failed to get number of lines", errno); return error; } error = simplebuffer_add_raw(sbobj, commentline, len, INI_VALUE_BLOCK); if (error) { TRACE_ERROR_NUMBER("Failed to add comment", error); return error; } error = simplebuffer_add_cr(sbobj); if (error) { TRACE_ERROR_NUMBER("Failed to add CR", error); return error; } } } if (strncmp(key, INI_SPECIAL_KEY, sizeof(INI_SPECIAL_KEY)) == 0) { /* Special key carries only a comment */ TRACE_FLOW_EXIT(); return EOK; } /* Handle the case it is a section key */ if (strncmp(key, INI_SECTION_KEY, sizeof(INI_SECTION_KEY)) == 0) sec = 1; if (sec) { error = simplebuffer_add_str(sbobj, INI_OPEN_BR, sizeof(INI_OPEN_BR) - 1, INI_VALUE_BLOCK); if (error) { TRACE_ERROR_NUMBER("Failed to add opening section bracket", error); return error; } } else { error = simplebuffer_add_str(sbobj, key, vo->keylen, INI_VALUE_BLOCK); if (error) { TRACE_ERROR_NUMBER("Failed to add key", error); return error; } error = simplebuffer_add_str(sbobj, INI_EQUAL_SIGN, sizeof(INI_EQUAL_SIGN) - 1, INI_VALUE_BLOCK); if (error) { TRACE_ERROR_NUMBER("Failed to add equal sign", error); return error; } } if (vo->raw_lines) { vln = ref_array_len(vo->raw_lines); TRACE_INFO_NUMBER("Number of lines:", vln); #ifdef HAVE_TRACE extern void ref_array_debug(struct ref_array *ra, int num); ref_array_debug(vo->raw_lines, 0); ref_array_debug(vo->raw_lengths, 1); #endif for (i = 0; i < vln; i++) { /* Get line */ ptr = ref_array_get(vo->raw_lines, i, NULL); if (ptr) { /* Get its length */ len = 0; ref_array_get(vo->raw_lengths, i, (void *)&len); part = *((char **)(ptr)); TRACE_INFO_STRING("Value:", part); TRACE_INFO_NUMBER("Lenght:", len); error = simplebuffer_add_raw(sbobj, part, len, INI_VALUE_BLOCK); if (error) { TRACE_ERROR_NUMBER("Failed to add value", error); return error; } } if (!sec) { error = simplebuffer_add_cr(sbobj); if (error) { TRACE_ERROR_NUMBER("Failed to add CR", error); return error; } } } if ((!vln) && (!sec)) { error = simplebuffer_add_cr(sbobj); if (error) { TRACE_ERROR_NUMBER("Failed to add CR", error); return error; } } } if (sec) { error = simplebuffer_add_str(sbobj, INI_CLOSE_BR, sizeof(INI_CLOSE_BR) - 1, INI_VALUE_BLOCK); if (error) { TRACE_ERROR_NUMBER("Failed to add closing bracket", error); return error; } error = simplebuffer_add_cr(sbobj); if (error) { TRACE_ERROR_NUMBER("Failed to add CR", error); return error; } } TRACE_INFO_STRING("Buffer:", (const char *)simplebuffer_get_buf(sbobj)); TRACE_FLOW_EXIT(); return error; }
/* Function to create a folded value out of the unfolded string */ static int value_fold(struct simplebuffer *unfolded, uint32_t key_len, uint32_t fold_bound, struct ref_array *raw_lines, struct ref_array *raw_lengths) { int error = EOK; const char *buf; uint32_t len = 0; /* Full length of the buffer */ uint32_t fold_place = 0; /* Potential folding place */ uint32_t best_place = 0; /* Dynamic folding boundary */ uint32_t next_place = 0; /* Position of the found space */ uint32_t fold_len = 0; /* Determined length of the substring */ uint32_t idx = 0; /* Counter of lines */ uint32_t i = 0; /* Internal counter */ uint32_t resume_place = 0; /* Place we resume parsing */ uint32_t start_place = 0; /* Start of the string */ int done = 0; /* Are we done? */ TRACE_FLOW_ENTRY(); /* Reset arrays */ ref_array_reset(raw_lines); ref_array_reset(raw_lengths); /* Get the buffer info */ len = simplebuffer_get_len(unfolded); if (!len) { /* Nothing to fold */ TRACE_FLOW_EXIT(); return EOK; } buf = (const char *)simplebuffer_get_buf(unfolded); TRACE_INFO_STRING("Unfolded value:", buf); /* Make sure that we have at least one character to fold */ if (fold_bound == 0) fold_bound++; while (!done) { /* Determine the max length of the line */ if (idx == 0) { if (fold_bound > (key_len + INI_FOLDING_OVERHEAD)) { best_place = fold_bound - key_len - INI_FOLDING_OVERHEAD; } else best_place = 0; } else { best_place = fold_bound; /* Starting with the second line if we plan * to add space ourselves factor it into folding * boadary */ if ((buf[start_place] != ' ') && (buf[start_place] != '\t')) best_place--; } TRACE_INFO_NUMBER("Best place", best_place); fold_place = start_place; next_place = start_place; best_place += start_place; /* Parse the buffer from the right place */ for (i = resume_place; i <= len; i++) { /* Check for folding opportunity */ if (i == len) { next_place = i; done = 1; } /* * Fold if we found the separator or the first line * is too long right away */ else if (((buf[i] == ' ') || (buf[i] == '\t')) || ((best_place == 0) && (i == 0))) { next_place = i; TRACE_INFO_NUMBER("Next place:", next_place); } else continue; if ((next_place > best_place) || (next_place == 0)) { if ((fold_place == start_place) && (next_place != 0)) { /* Our first found folding place * is already after the preferred * folding place. Time to fold then... */ fold_len = next_place - start_place; } else { /* We will use the previous * folding place. */ fold_len = fold_place - start_place; } TRACE_INFO_NUMBER("Fold len:", fold_len); error = save_portion(raw_lines, raw_lengths, buf + start_place, fold_len); if (error) { TRACE_ERROR_NUMBER("Failed to save", error); return error; } start_place += fold_len; /* * This will force the re-processing * of the same space but it is * helpful in case the middle portion * of the value is beyond our folding limit. */ resume_place = next_place; if (fold_len == 0) resume_place++; idx++; break; } else { /* Case when next_place <= best_place */ fold_place = next_place; } } /* Save last portion */ if (done) { if (next_place - start_place) { error = save_portion(raw_lines, raw_lengths, buf + start_place, next_place - start_place); if (error) { TRACE_ERROR_NUMBER("Failed to save last chunk", error); return error; } idx++; } } } TRACE_FLOW_EXIT(); return error; }