aboutsummaryrefslogtreecommitdiff
path: root/src/ini.c
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2023-10-26 19:53:29 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2023-10-26 19:53:29 -0400
commit17178535cc9df5e834dfd43e3b2b919e02e5798d (patch)
tree5e55e8b2c2453ccf6271b190cf45e90d2c25179d /src/ini.c
downloadstasis-17178535cc9df5e834dfd43e3b2b919e02e5798d.tar.gz
Initial commit
Diffstat (limited to 'src/ini.c')
-rw-r--r--src/ini.c409
1 files changed, 409 insertions, 0 deletions
diff --git a/src/ini.c b/src/ini.c
new file mode 100644
index 0000000..b2df150
--- /dev/null
+++ b/src/ini.c
@@ -0,0 +1,409 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "ohmycal.h"
+#include "ini.h"
+/*
+char *strip(char *s) {
+ size_t len = strlen(s) + 1;
+ while (--len) {
+ if (isalnum(s[len])) {
+ break;
+ }
+ if (isblank(s[len])) {
+ s[len] = '\0';
+ }
+ }
+ return s;
+}
+ */
+
+/*
+char *lstrip(char *s) {
+ size_t i = 0;
+ char *end = NULL;
+ do {
+ end = &s[i];
+ if (!isblank(*end)) {
+ break;
+ }
+ i++;
+ } while (1);
+ if (i) {
+ size_t len = strlen(end);
+ memmove(s, end, len);
+ if (strlen(s)) {
+ s[len] = '\0';
+ }
+ }
+ return s;
+}
+ */
+
+/*
+int startswith(const char *s1, char *s2) {
+ size_t i;
+ for (i = 0; i < strlen(s2); i++) {
+ if (s1[i] != s2[i]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+*/
+
+/*
+int endswith(const char *s1, char *s2) {
+ size_t s2x, s1x;
+ for (s2x = strlen(s2), s1x = strlen(s1); s2x >= 0; s2x--, s1x--) {
+ char *s1p = &s1[s1x];
+ char *s2p = &s2[s2x];
+ if (s1[s1x] != s2[s2x]) {
+ return 0;
+ }
+ if (s2x == 0) {
+ break;
+ }
+ }
+ return 1;
+}
+ */
+
+struct INIFILE *ini_init() {
+ struct INIFILE *ini;
+ ini = calloc(1, sizeof(*ini));
+ ini->section_count = 0;
+ return ini;
+}
+
+void ini_section_init(struct INIFILE **ini) {
+ (*ini)->section = calloc((*ini)->section_count + 1, sizeof(**(*ini)->section));
+}
+
+struct INISection *ini_section_search(struct INIFILE **ini, char *value) {
+ struct INISection *result = NULL;
+ for (size_t i = 0; i < (*ini)->section_count; i++) {
+ if ((*ini)->section[i]->key != NULL) {
+ if (!strcmp((*ini)->section[i]->key, value)) {
+ result = (*ini)->section[i];
+ }
+ }
+ }
+ return result;
+}
+
+int ini_data_init(struct INIFILE **ini, char *section_name) {
+ struct INISection *section = ini_section_search(ini, section_name);
+ if (section == NULL) {
+ return 1;
+ }
+ section->data = calloc(section->data_count + 1, sizeof(**section->data));
+ return 0;
+}
+
+struct INIData *ini_data_get(struct INIFILE *ini, char *section_name, char *key) {
+ struct INISection *section = NULL;
+ section = ini_section_search(&ini, section_name);
+ for (size_t i = 0; i < section->data_count; i++) {
+ if (section->data[i]->key != NULL) {
+ if (!strcmp(section->data[i]->key, key)) {
+ return section->data[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+struct INIData *ini_getall(struct INIFILE *ini, char *section_name) {
+ struct INISection *section = NULL;
+ struct INIData *result = NULL;
+ static size_t i = 0;
+
+ section = ini_section_search(&ini, section_name);
+ if (section->data[i]) {
+ result = section->data[i];
+ i++;
+ } else {
+ result = NULL;
+ i = 0;
+ }
+
+ return result;
+}
+
+int ini_getval(struct INIFILE *ini, char *section_name, char *key, int type, union INIVal *result) {
+ char *token = NULL;
+ char tbuf[BUFSIZ];
+ char *tbufp = tbuf;
+ struct INIData *data;
+ data = ini_data_get(ini, section_name, key);
+ if (!data) {
+ result->as_char_p = NULL;
+ return -1;
+ }
+ switch (type) {
+ case INIVAL_TYPE_INT:
+ result->as_int = (int) strtol(data->value, NULL, 10);
+ break;
+ case INIVAL_TYPE_UINT:
+ result->as_uint = (unsigned int) strtoul(data->value, NULL, 10);
+ break;
+ case INIVAL_TYPE_LONG:
+ result->as_long = (long) strtol(data->value, NULL, 10);
+ break;
+ case INIVAL_TYPE_ULONG:
+ result->as_ulong = (unsigned long) strtoul(data->value, NULL, 10);
+ break;
+ case INIVAL_TYPE_LLONG:
+ result->as_llong = (long long) strtoll(data->value, NULL, 10);
+ break;
+ case INIVAL_TYPE_ULLONG:
+ result->as_ullong = (unsigned long long) strtoull(data->value, NULL, 10);
+ break;
+ case INIVAL_TYPE_DOUBLE:
+ result->as_double = (double) strtod(data->value, NULL);
+ break;
+ case INIVAL_TYPE_FLOAT:
+ result->as_float = (float) strtod(data->value, NULL);
+ break;
+ case INIVAL_TYPE_STR:
+ result->as_char_p = lstrip(data->value);
+ break;
+ case INIVAL_TYPE_STR_ARRAY:
+ strcpy(tbufp, data->value);
+ *data->value = '\0';
+ for (size_t i = 0; (token = strsep(&tbufp, "\n")) != NULL; i++) {
+ lstrip(token);
+ strcat(data->value, token);
+ strcat(data->value, "\n");
+ }
+ result->as_char_p = data->value;
+ break;
+ case INIVAL_TYPE_BOOL:
+ result->as_bool = false;
+ if ((!strcmp(data->value, "true") || !strcmp(data->value, "True")) ||
+ (!strcmp(data->value, "yes") || !strcmp(data->value, "Yes")) ||
+ strtol(data->value, NULL, 10)) {
+ result->as_bool = true;
+ }
+ break;
+ default:
+ memset(result, 0, sizeof(*result));
+ break;
+ }
+ return 0;
+}
+
+int ini_data_record(struct INIFILE **ini, char *section_name, char *key, char *value) {
+ struct INISection *section = ini_section_search(ini, section_name);
+ if (section == NULL) {
+ return 1;
+ }
+
+ struct INIData **tmp = realloc(section->data, (section->data_count + 1) * sizeof(**section->data));
+ if (!tmp) {
+ perror(__FUNCTION__);
+ exit(1);
+ }
+ section->data = tmp;
+ if (!ini_data_get((*ini), section_name, key)) {
+ section->data[section->data_count] = calloc(1, sizeof(*section->data[0]));
+ section->data[section->data_count]->key = key; //strdup(key);
+ section->data[section->data_count]->value = value; //strdup(value);
+ section->data_count++;
+ } else {
+ struct INIData *data = ini_data_get(*ini, section_name, key);
+ size_t value_len_old = strlen(data->value);
+ size_t value_len = strlen(value);
+ size_t value_len_new = value_len_old + value_len;
+ /*
+ char *value_tmp = NULL;
+ value_tmp = realloc(data->value, value_len_new + 2);
+ if (!value_tmp) {
+ perror(__FUNCTION__ );
+ exit(1);
+ }
+ data->value = value_tmp;
+ */
+ //strcat(data->value, " ");
+ strcat(data->value, value);
+ }
+ return 0;
+}
+
+void ini_section_record(struct INIFILE **ini, char *key) {
+ struct INISection **tmp = realloc((*ini)->section, ((*ini)->section_count + 1) * sizeof((*ini)->section));
+ if (!tmp) {
+ perror(__FUNCTION__);
+ exit(1);
+ }
+ (*ini)->section = tmp;
+ (*ini)->section[(*ini)->section_count] = calloc(1, sizeof(*(*ini)->section[0]));
+ (*ini)->section[(*ini)->section_count]->key = strdup(key);
+ (*ini)->section_count++;
+}
+
+void ini_show(struct INIFILE *ini) {
+ for (size_t x = 0; x < ini->section_count; x++) {
+ printf("[%s]\n", ini->section[x]->key);
+ for (size_t y = 0; y < ini->section[x]->data_count; y++) {
+ printf("%s='%s'\n", ini->section[x]->data[y]->key, ini->section[x]->data[y]->value);
+ }
+ printf("\n");
+ }
+}
+
+char *unquote(char *s) {
+ int found = 0;
+ if (startswith(s, "'") && endswith(s, "'")) {
+ found = 1;
+ } else if (startswith(s, "\"") && endswith(s, "\"")) {
+ found = 1;
+ }
+
+ if (found) {
+ memmove(s, s + 1, strlen(s));
+ s[strlen(s) - 1] = '\0';
+ }
+ return s;
+}
+
+char *collapse_whitespace(char **s) {
+ size_t len = strlen(*s);
+ size_t i;
+ for (i = 0; isblank((int)*s[i]); i++);
+ memmove(*s, *s + i, strlen(*s));
+ if (i) {
+ *s[len - i] = '\0';
+ }
+ return *s;
+}
+
+void ini_free(struct INIFILE **ini) {
+ for (size_t section = 0; section < (*ini)->section_count; section++) {
+ for (size_t data = 0; data < (*ini)->section[section]->data_count; data++) {
+ if ((*ini)->section[section]->data[data]) {
+ free((*ini)->section[section]->data[data]->key);
+ free((*ini)->section[section]->data[data]->value);
+ free((*ini)->section[section]->data[data]);
+ }
+ }
+ free((*ini)->section[section]->data);
+ free((*ini)->section[section]->key);
+ free((*ini)->section[section]);
+ }
+ free((*ini)->section);
+ free((*ini));
+}
+
+struct INIFILE *ini_open(const char *filename) {
+ FILE *fp;
+ char line[BUFSIZ] = {0};
+ char current_section[BUFSIZ] = {0};
+ char *key_last = NULL;
+ struct INIFILE *ini = ini_init();
+
+ ini_section_init(&ini);
+
+ // Create an implicit section. [default] does not need to be present in the INI config
+ ini_section_record(&ini, "default");
+ strcpy(current_section, "default");
+ //ini_data_init(&ini, "default");
+
+ // Open the configuration file for reading
+ fp = fopen(filename, "r");
+ if (!fp) {
+ perror(filename);
+ exit(1);
+ }
+
+ // Read file
+ for (size_t i = 0; fgets(line, sizeof(line), fp) != NULL; i++) {
+ // Find pointer to first comment character
+ char *comment = strpbrk(line, ";#");
+ if (comment) {
+ // Remove comment from line (standalone and inline comments)
+ if (!(comment - line > 0 && (*(comment - 1) == '\\') || (*comment - 1) == '#')) {
+ *comment = '\0';
+ } else {
+ // Handle escaped comment characters. Remove the escape character '\'
+ memmove(comment - 1, comment, strlen(comment));
+ comment[strlen(comment) - 1] = '\0';
+ }
+ }
+
+ // Removing comments could have reduced the line's length, so calculate it now
+ size_t len = strlen(line);
+
+ // Ignore empty lines
+ if (!len || line[0] == '\n') {
+ continue;
+ }
+
+ // Test for section header: [string]
+ if (startswith(line, "[")) {
+ // Ignore default section because we already have an implicit one
+ if (!strncmp(&line[1], "default", strlen("default"))) {
+ continue;
+ }
+
+ // Remove section ending: ']'
+ line[strlen(line) - 2] = '\0';
+
+ // Create new named section
+ ini_section_record(&ini, &line[1]);
+ //ini_data_init(&ini, &line[1]);
+
+ // Record the name of the section. This is used until another section is found.
+ strcpy(current_section, &line[1]);
+ continue;
+ }
+
+ char *key = NULL;
+ char *value = malloc(BUFSIZ);
+ char *operator = strchr(line, '=');
+
+ // continuation line
+ if (startswith(line, " ") || startswith(line, "\t")) {
+ operator = NULL;
+ }
+
+ if (operator) {
+ size_t key_len = operator - line;
+ key = strndup(line, key_len);
+ key_last = key;
+ strcpy(value, &operator[1]);
+ value[strlen(value) - 1] = '\0';
+ } else if (!key && !strlen(value) && ! (startswith(line, " ") || startswith(line, "\t"))) {
+ fprintf(stderr, "NO OPERATOR OR INDENT: %zu:'%s'\n", i, line);
+ struct INISection *section = ini_section_search(&ini, current_section);
+ struct INIData *data = NULL;
+ //key = key_last;
+ free(value);
+ value = NULL;
+ } else {
+ struct INISection *section = ini_section_search(&ini, current_section);
+ struct INIData *data = section->data[section->data_count - 1];
+ if (strlen(data->value)) {
+ data->value[strlen(data->value) - 1] = '\n';
+ }
+ key = key_last;
+ strcpy(value, line);
+ if (endswith(value, "\n")) {
+ value[strlen(value) - 1] = '\n';
+ }
+ }
+
+ // Store key value pair in section's data array
+ if (key) {
+ lstrip(key);
+ strip(key);
+ unquote(value);
+ lstrip(value);
+ ini_data_record(&ini, current_section, key, value);
+ }
+ }
+
+ return ini;
+} \ No newline at end of file