/*- * Copyright (c) 2015 Rozhuk Ivan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Author: Rozhuk Ivan * */ /* Example: [section] name=value a=a b=b */ #include #ifndef BSD #define _GNU_SOURCE /* See feature_test_macros(7) */ #define __USE_GNU 1 #endif #include #include #include /* bcopy, bzero, memcpy, memmove, memset, strerror... */ #include /* malloc, exit */ #include /* snprintf, fprintf */ #include "mem_helpers.h" #include "StrToNum.h" #include "utils.h" #include "ini_cfg.h" #define INI_CFG_LINE_ALLOC_PADDING 16 typedef struct settings_line_s { uint8_t *data; size_t data_size; size_t data_allocated_size; uint32_t type; uint8_t *name; size_t name_size; uint8_t *val; size_t val_size; } settings_line_t, *settings_line_p; #define SETTING_LINE_TYPE_EMPTY_LINE 0 #define SETTING_LINE_TYPE_INVALID 1 #define SETTING_LINE_TYPE_COMMENT 2 #define SETTING_LINE_TYPE_SECTION 3 #define SETTING_LINE_TYPE_VALUE 4 typedef struct ini_cfg_s { settings_line_p *lines; size_t lines_count; } ini_cfg_t; settings_line_p ini_cfg_sline_alloc__int(size_t size); settings_line_p ini_cfg_sline_alloc__int(size_t size) { size_t tm; settings_line_p sline; /* Alloc and store data from line. */ tm = (sizeof(settings_line_t) + size + INI_CFG_LINE_ALLOC_PADDING); sline = mem_zalloc(tm); if (NULL == sline) return (NULL); sline->data = (uint8_t*)(sline + 1); sline->data_size = size; sline->data_allocated_size = (tm - sizeof(settings_line_t)); return (sline); } int ini_cfg_create(ini_cfg_p *ini_cfg_ret) { ini_cfg_p ini_cfg; if (NULL == ini_cfg_ret) return (EINVAL); ini_cfg = mem_zalloc(sizeof(ini_cfg_t)); if (NULL == ini_cfg) { (*ini_cfg_ret) = NULL; return (errno); } (*ini_cfg_ret) = ini_cfg; return (0); } void ini_cfg_destroy(ini_cfg_p ini_cfg) { size_t i; if (NULL == ini_cfg) return; if (NULL != ini_cfg->lines) { for (i = 0; i < ini_cfg->lines_count; i ++) { if (NULL == ini_cfg->lines[i]) continue; free(ini_cfg->lines[i]); } free(ini_cfg->lines); } free(ini_cfg); } int ini_cfg_buf_parse(ini_cfg_p ini_cfg, uint8_t *buf, size_t buf_size) { int error = 0; uint8_t *line, *ptr; size_t line_size; settings_line_p sline, *lines_new; if (NULL == ini_cfg || NULL == buf) return (EINVAL); if (0 == buf_size) return (0); line = NULL; line_size = 0; while (0 == buf_get_next_line(buf, buf_size, line, line_size, &line, &line_size)) { /* Alloc and store data from line. */ sline = ini_cfg_sline_alloc__int(line_size); if (NULL == sline) { error = errno; goto err_out; } memcpy(sline->data, line, line_size); if (0 == line_size) { sline->type = SETTING_LINE_TYPE_EMPTY_LINE; } else { switch (sline->data[0]) { case ';': case '#': sline->type = SETTING_LINE_TYPE_COMMENT; break; case '[': ptr = mem_rchr(sline->data, sline->data_size, ']'); if (NULL == ptr) { /* Bad format. */ sline->type = SETTING_LINE_TYPE_INVALID; break; } sline->type = SETTING_LINE_TYPE_SECTION; sline->name = (sline->data + 1); sline->name_size = (ptr - sline->name); break; default: ptr = mem_chr(sline->data, sline->data_size, '='); if (NULL == ptr) { /* Bad format. */ sline->type = SETTING_LINE_TYPE_INVALID; break; } sline->type = SETTING_LINE_TYPE_VALUE; sline->name = sline->data; sline->name_size = (ptr - sline->name); sline->val = (ptr + 1); sline->val_size = (sline->data_size - (sline->val - sline->data)); break; } } /* Add line to array. */ lines_new = realloc(ini_cfg->lines, (sizeof(settings_line_p) * (ini_cfg->lines_count + 1))); if (NULL == lines_new) { error = errno; free(sline); goto err_out; } ini_cfg->lines = lines_new; ini_cfg->lines[ini_cfg->lines_count] = sline; ini_cfg->lines_count ++; } err_out: return (error); } int ini_cfg_buf_calc_size(ini_cfg_p ini_cfg, size_t *file_size) { size_t i, tm; if (NULL == ini_cfg || NULL == file_size) return (EINVAL); /* Summ lines. */ for (i = 0, tm = 0; i < ini_cfg->lines_count; i ++) { if (NULL == ini_cfg->lines[i]) continue; tm += (ini_cfg->lines[i]->data_size + 2); } (*file_size) = tm; return (0); } int ini_cfg_buf_gen(ini_cfg_p ini_cfg, uint8_t *buf, size_t buf_size, size_t *buf_size_ret) { int error = 0; size_t i, off, tm; if (NULL == ini_cfg || NULL == buf || 0 == buf_size || NULL == buf_size_ret) return (EINVAL); /* Write lines. */ for (i = 0, off = 0; i < ini_cfg->lines_count; i ++) { if (NULL == ini_cfg->lines[i]) continue; tm = (ini_cfg->lines[i]->data_size + 2); if ((off + tm) > buf_size) { error = -1; break; } memcpy((buf + off), ini_cfg->lines[i]->data, ini_cfg->lines[i]->data_size); memcpy((buf + off + ini_cfg->lines[i]->data_size), "\r\n", 2); off += tm; } (*buf_size_ret) = off; return (error); } size_t ini_cfg_find_section(ini_cfg_p ini_cfg, uint8_t *sect_name, size_t sect_name_size) { size_t i; if (NULL == ini_cfg || (NULL != sect_name && 0 == sect_name_size)) return ((size_t)~0); if (NULL == ini_cfg->lines) return ((size_t)~0); /* Look for section. */ for (i = 0; i < ini_cfg->lines_count; i ++) { if (NULL == ini_cfg->lines[i]) continue; if (SETTING_LINE_TYPE_SECTION != ini_cfg->lines[i]->type) continue; if (0 == mem_cmpn(ini_cfg->lines[i]->name, ini_cfg->lines[i]->name_size, sect_name, sect_name_size)) { return (i); /* Found! */ } } return ((size_t)~0); } size_t ini_cfg_find_sectioni(ini_cfg_p ini_cfg, uint8_t *sect_name, size_t sect_name_size) { size_t i; if (NULL == ini_cfg || (NULL != sect_name && 0 == sect_name_size)) return ((size_t)~0); if (NULL == ini_cfg->lines) return ((size_t)~0); /* Look for section. */ for (i = 0; i < ini_cfg->lines_count; i ++) { if (NULL == ini_cfg->lines[i]) continue; if (SETTING_LINE_TYPE_SECTION != ini_cfg->lines[i]->type) continue; if (0 == mem_cmpin(ini_cfg->lines[i]->name, ini_cfg->lines[i]->name_size, sect_name, sect_name_size)) { return (i); /* Found! */ } } return ((size_t)~0); } size_t ini_cfg_find_value(ini_cfg_p ini_cfg, uint8_t *val_name, size_t val_name_size, size_t sect_off) { size_t i; if (NULL == ini_cfg || NULL == val_name || 0 == val_name_size || (size_t)~0 == sect_off) return ((size_t)~0); if (NULL == ini_cfg->lines) return ((size_t)~0); /* Look for value. */ for (i = (sect_off + 1); i < ini_cfg->lines_count; i ++) { if (NULL == ini_cfg->lines[i]) continue; if (SETTING_LINE_TYPE_SECTION == ini_cfg->lines[i]->type) break; /* No value in section. */ if (SETTING_LINE_TYPE_VALUE != ini_cfg->lines[i]->type) continue; if (0 == mem_cmpin(ini_cfg->lines[i]->name, ini_cfg->lines[i]->name_size, val_name, val_name_size)) { return (i); /* Found! */ } } return ((size_t)~0); } size_t ini_cfg_find_valuei(ini_cfg_p ini_cfg, uint8_t *val_name, size_t val_name_size, size_t sect_off) { size_t i; if (NULL == ini_cfg || NULL == val_name || 0 == val_name_size || (size_t)~0 == sect_off) return ((size_t)~0); if (NULL == ini_cfg->lines) return ((size_t)~0); /* Look for value. */ for (i = (sect_off + 1); i < ini_cfg->lines_count; i ++) { if (NULL == ini_cfg->lines[i]) continue; if (SETTING_LINE_TYPE_SECTION == ini_cfg->lines[i]->type) break; /* No value in section. */ if (SETTING_LINE_TYPE_VALUE != ini_cfg->lines[i]->type) continue; if (0 == mem_cmpin(ini_cfg->lines[i]->name, ini_cfg->lines[i]->name_size, val_name, val_name_size)) { return (i); /* Found! */ } } return ((size_t)~0); } int ini_cfg_val_get(ini_cfg_p ini_cfg, uint8_t *sect_name, size_t sect_name_size, uint8_t *val_name, size_t val_name_size, uint8_t **val, size_t *val_size) { size_t sect_off, val_off; if (NULL == ini_cfg || NULL == val || NULL == val_size) return (EINVAL); if (0 == sect_name_size && NULL != sect_name) sect_name_size = strlen((const char*)sect_name); if (0 == val_name_size && NULL != val_name) val_name_size = strlen((const char*)val_name); /* Look for section. */ sect_off = ini_cfg_find_section(ini_cfg, sect_name, sect_name_size); if ((size_t)~0 == sect_off) return (ENOENT); /* No section. */ /* Look for value. */ val_off = ini_cfg_find_value(ini_cfg, val_name, val_name_size, sect_off); if ((size_t)~0 == val_off) return (ENOENT); /* No value in section. */ (*val) = ini_cfg->lines[val_off]->val; (*val_size) = ini_cfg->lines[val_off]->val_size; return (0); } int ini_cfg_val_get_int(ini_cfg_p ini_cfg, uint8_t *sect_name, size_t sect_name_size, uint8_t *val_name, size_t val_name_size, ssize_t *val) { int error; uint8_t *tm_val; size_t val_size; if (NULL == val) return (EINVAL); error = ini_cfg_val_get(ini_cfg, sect_name, sect_name_size, val_name, val_name_size, &tm_val, &val_size); if (0 != error) return (error); (*val) = UStr8ToNum(tm_val, val_size); return (0); } int ini_cfg_val_get_uint(ini_cfg_p ini_cfg, uint8_t *sect_name, size_t sect_name_size, uint8_t *val_name, size_t val_name_size, size_t *val) { int error; uint8_t *tm_val; size_t val_size; if (NULL == val) return (EINVAL); error = ini_cfg_val_get(ini_cfg, sect_name, sect_name_size, val_name, val_name_size, &tm_val, &val_size); if (0 != error) return (error); (*val) = UStr8ToUNum(tm_val, val_size); return (0); } int ini_cfg_vali_get(ini_cfg_p ini_cfg, uint8_t *sect_name, size_t sect_name_size, uint8_t *val_name, size_t val_name_size, uint8_t **val, size_t *val_size) { size_t sect_off, val_off; if (NULL == ini_cfg || NULL == val || NULL == val_size) return (EINVAL); if (0 == sect_name_size && NULL != sect_name) sect_name_size = strlen((const char*)sect_name); if (0 == val_name_size && NULL != val_name) val_name_size = strlen((const char*)val_name); /* Look for section. */ sect_off = ini_cfg_find_sectioni(ini_cfg, sect_name, sect_name_size); if ((size_t)~0 == sect_off) return (ENOENT); /* No section. */ /* Look for value. */ val_off = ini_cfg_find_valuei(ini_cfg, val_name, val_name_size, sect_off); if ((size_t)~0 == val_off) return (ENOENT); /* No value in section. */ (*val) = ini_cfg->lines[val_off]->val; (*val_size) = ini_cfg->lines[val_off]->val_size; return (0); } int ini_cfg_vali_get_int(ini_cfg_p ini_cfg, uint8_t *sect_name, size_t sect_name_size, uint8_t *val_name, size_t val_name_size, ssize_t *val) { int error; uint8_t *tm_val; size_t val_size; if (NULL == val) return (EINVAL); error = ini_cfg_vali_get(ini_cfg, sect_name, sect_name_size, val_name, val_name_size, &tm_val, &val_size); if (0 != error) return (error); (*val) = UStr8ToNum(tm_val, val_size); return (0); } int ini_cfg_vali_get_uint(ini_cfg_p ini_cfg, uint8_t *sect_name, size_t sect_name_size, uint8_t *val_name, size_t val_name_size, size_t *val) { int error; uint8_t *tm_val; size_t val_size; if (NULL == val) return (EINVAL); error = ini_cfg_vali_get(ini_cfg, sect_name, sect_name_size, val_name, val_name_size, &tm_val, &val_size); if (0 != error) return (error); (*val) = UStr8ToUNum(tm_val, val_size); return (0); } int ini_cfg_val_set(ini_cfg_p ini_cfg, uint8_t *sect_name, size_t sect_name_size, uint8_t *val_name, size_t val_name_size, uint8_t *val, size_t val_size) { size_t sect_off, val_off, data_size; settings_line_p sline, *lines_new; if (NULL == ini_cfg || (NULL == val && 0 != val_size)) return (EINVAL); if (0 == sect_name_size && NULL != sect_name) sect_name_size = strlen((const char*)sect_name); if (0 == val_name_size && NULL != val_name) val_name_size = strlen((const char*)val_name); /* Look for section. */ sect_off = ini_cfg_find_section(ini_cfg, sect_name, sect_name_size); if ((size_t)~0 == sect_off) { /* No section, add. */ /* Alloc line for section. */ sline = ini_cfg_sline_alloc__int((sect_name_size + 2)); if (NULL == sline) return (errno); sline->type = SETTING_LINE_TYPE_SECTION; sline->name = (sline->data + 1); sline->name_size = sect_name_size; sline->data[0] = '['; memcpy(sline->name, sect_name, sect_name_size); sline->name[sline->name_size] = ']'; /* Add line to array. */ lines_new = realloc(ini_cfg->lines, (sizeof(settings_line_p) * (ini_cfg->lines_count + 1))); if (NULL == lines_new) { free(sline); return (errno); } ini_cfg->lines = lines_new; ini_cfg->lines[ini_cfg->lines_count] = sline; sect_off = ini_cfg->lines_count; ini_cfg->lines_count ++; } data_size = (val_name_size + 1 + val_size); /* Look for value. */ val_off = ini_cfg_find_value(ini_cfg, val_name, val_name_size, sect_off); if ((size_t)~0 == val_off) { /* No value in section, add. */ /* Add line to array. */ lines_new = realloc(ini_cfg->lines, (sizeof(settings_line_p) * (ini_cfg->lines_count + 1))); if (NULL == lines_new) { free(sline); return (errno); } ini_cfg->lines = lines_new; /* Add to section end. */ for (val_off = (sect_off + 1); val_off < ini_cfg->lines_count; val_off ++) { if (NULL == ini_cfg->lines[val_off]) continue; if (SETTING_LINE_TYPE_SECTION == ini_cfg->lines[val_off]->type) break; } /* Before empty lines.*/ for (; 0 < val_off && val_off <= ini_cfg->lines_count; val_off --) { if (NULL == ini_cfg->lines[(val_off - 1)]) continue; if (SETTING_LINE_TYPE_EMPTY_LINE != ini_cfg->lines[(val_off - 1)]->type) break; } memmove(&ini_cfg->lines[(val_off + 1)], &ini_cfg->lines[val_off], (sizeof(settings_line_p) * (ini_cfg->lines_count - val_off))); ini_cfg->lines[val_off] = NULL; ini_cfg->lines_count ++; alloc_new_line: /* Alloc. */ sline = ini_cfg_sline_alloc__int(data_size); if (NULL == sline) return (errno); ini_cfg->lines[val_off] = sline; sline->type = SETTING_LINE_TYPE_VALUE; sline->name = sline->data; sline->name_size = val_name_size; memcpy(sline->name, val_name, val_name_size); sline->name[sline->name_size] = '='; sline->val = (sline->name + sline->name_size + 1); } else { /* Check existing item free size. */ sline = ini_cfg->lines[val_off]; if (NULL == sline) goto alloc_new_line; if (sline->data_allocated_size > data_size) goto update_value; /* Realloc. */ sline = realloc(ini_cfg->lines[val_off], (sizeof(settings_line_t) + data_size + INI_CFG_LINE_ALLOC_PADDING)); if (NULL == sline) return (errno); if (ini_cfg->lines[val_off] == sline) goto update_value; /* Update pointers. */ ini_cfg->lines[val_off] = sline; sline->data = (uint8_t*)(sline + 1); sline->data_allocated_size = (data_size + INI_CFG_LINE_ALLOC_PADDING); sline->name = sline->data; sline->val = (sline->name + sline->name_size + 1); } update_value: /* Update. */ sline->data_size = data_size; sline->val_size = val_size; memcpy(sline->val, val, val_size); return (0); } int ini_cfg_val_set_int(ini_cfg_p ini_cfg, uint8_t *sect_name, size_t sect_name_size, uint8_t *val_name, size_t val_name_size, ssize_t val) { char buf[64]; size_t buf_size; buf_size = snprintf(buf, sizeof(buf), "%zi", val); return (ini_cfg_val_set(ini_cfg, sect_name, sect_name_size, val_name, val_name_size, (uint8_t*)buf, buf_size)); } int ini_cfg_val_set_uint(ini_cfg_p ini_cfg, uint8_t *sect_name, size_t sect_name_size, uint8_t *val_name, size_t val_name_size, size_t val) { char buf[64]; size_t buf_size; buf_size = snprintf(buf, sizeof(buf), "%zu", val); return (ini_cfg_val_set(ini_cfg, sect_name, sect_name_size, val_name, val_name_size, (uint8_t*)buf, buf_size)); }