#include #include #include #include #include #include "ini.h" #include "compat.h" static inline int stracmp(const char* zstr, const char* lstr, size_t len) { int diff = 0; while (0 == diff && *zstr && len > 0) { --len; diff = (*zstr - *lstr); ++zstr; ++lstr; } if (0 == diff) { if (0 == len && !*zstr) diff = *zstr; else if (0 != len && *zstr) diff = *lstr; } return diff; } static const ini_datum* find_name(const ini_datum* schema, const char* name, int len, const ini_data_type type) { if (0 == stracmp(schema->name, name, len)) { return schema; } for (int i = 0; i < schema->count; ++i) { if ( ( ini_none == type || type == schema->data[i].type) && 0 == stracmp(schema->data[i].name, name, len)) { return &schema->data[i]; } } return NULL; } static inline int needs_quotes(const char* str) { while (*str && !isspace(*str) && ',' != *str) ++str; return !!(*str); } static int _write_ini_file(FILE* file, const ini_datum* schema, const void* data, int level) { int status = 0; const void* ptr = (data + schema->offset); if (ini_section == schema->type) { fprintf(file, "[%s]\n", schema->name); for (int i = 0; i < schema->count; ++i) { _write_ini_file(file, &schema->data[i], ptr, level + 1); if (schema->count - 1 != i) fputc('\n', file); } if (0 != level) fputc('\n', file); } else if (ini_comment == schema->type) { fprintf(file, "; %s", *(char**)ptr); } else { fprintf(file, "%s = ", schema->name); if (ini_string == schema->type) { const char* str = *(char**)ptr; if (!needs_quotes(str)) fputs(str, file); else fprintf(file, "\"%s\"", str); } else if (ini_integer == schema->type) { fprintf(file, "%d", *(uint32_t*)ptr); } else if (ini_flag == schema->type) { fprintf(file, "%d", !!( *(uint32_t*)ptr & (1 << schema->shift))); } } return status; } int write_ini_file(FILE* file, const ini_datum* schema, const void* data) { return _write_ini_file(file, schema, data, 0); } static inline const char* first_char(const char* str) { while (*str && isspace(*str)) ++str; return str; } static inline const char* first_space(const char* str) { while (*str && !isspace(*str)) ++str; return str; } static inline const char* end_key(const char* str) { while (*str && '=' != *str && !isspace(*str)) ++str; return str; } static inline const char* last_char(const char* str) { int len = strlen(str); const char* end = str + len - 1; while (end > str && isspace(*end)) --end; return end; } static inline char* parse_string(const char* str) { const char* last = last_char(str); if ('"' == str[0] && '"' == *last) { ++str; --last; } return strndup(str, last - str + 1); } static inline int parse_key_value(const char* key_start, const ini_datum* section, void* data) { const char* key_end = end_key(key_start + 1); if (NULL == key_end) return -1; const ini_datum* def = find_name(section, key_start, (key_end - key_start), ini_none); if (NULL == def || def->type <= ini_section) return -1; const char* equal = first_char(key_end); if (NULL == equal || '=' != *equal) return -1; const char* val = first_char(equal + 1); if (NULL == val) return -1; const char* ptr = data + def->offset; if (ini_string == def->type) { *(char**)ptr = parse_string(val); } else if ( ini_integer == def->type || ini_flag == def->type) { int intval = 0; if (0 >= sscanf(val, "%d", &intval)) return -1; if (ini_integer == def->type) { *(int32_t*)ptr = intval; } else { int32_t mask = (1 << def->shift); if (intval) *(uint32_t*)ptr |= mask; else *(uint32_t*)ptr &= ~mask; } } return 0; } int read_ini_file(FILE* file, const ini_datum* schema, void* data) { int status = 0; const ini_datum* section = NULL; const ini_datum* subsection = NULL; void* ptr = data; char* line = NULL; size_t sz_line = 0; while (0 == status && 0 <= getline(&line, &sz_line, file)) { const char* str = first_char(line); if ('[' == str[0]) { const char* start = &str[1]; const char* end = strchr(start, ']'); if (NULL != end) { ptr = data; int len = (end - start); if ('.' == start[0]) { if (NULL != section) { subsection = find_name( section, start, len, ini_section ); ptr += section->offset; if (NULL != subsection) { ptr += subsection->offset; } } } else { section = find_name( schema, start, len, ini_section ); subsection = section; if (NULL != section) ptr += section->offset; } } } else if (';' == str[0]) { // Ignore comments } else if (isalnum(str[0])) { // Key-value // Ignoring return value: // - Unknown sections or keys are ignored // - Just ignore malformed files, I guess parse_key_value(str, subsection, ptr); } } free(line); return status; }