diff options
author | pommicket <pommicket@gmail.com> | 2025-09-11 16:40:04 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2025-09-11 16:40:47 -0400 |
commit | ec9cf4ef9b4ff2a16172e72adf5fca2a94ba3009 (patch) | |
tree | 0dfcd4d1bb36b26804d773b6a4c053e5ac4969d9 /pom.c | |
parent | 06b3634741718a264bef03ee7b38455a2482bb24 (diff) |
Start libpom implementation
Diffstat (limited to 'pom.c')
-rw-r--r-- | pom.c | 172 |
1 files changed, 172 insertions, 0 deletions
@@ -1,5 +1,24 @@ #include "pom.h" +#include <stdio.h> // still needed for sprintf, even if POM_NO_STDIO is defined. +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <inttypes.h> + +#if __GNUC__ >= 6 +#define ATTRIBUTE_PRINTF(fmt, args) __attribute__ ((format(printf, fmt, args))) +#else +#define ATTRIBUTE_PRINTF(fmt, args) +#endif +#if _MSC_VER >= 1600 +#define PRINTF_FORMAT_STRING _Printf_format_string_ +#else +#define PRINTF_FORMAT_STRING +#endif + struct pom_error { const pom_error *next; const char *file; @@ -7,22 +26,175 @@ struct pom_error { const char *message; }; +struct main_conf; + +struct pom_conf { + struct main_conf *main; + size_t prefix_len; + const struct conf_item *items; + size_t items_count; +}; + +struct conf_items { + const char *key; + const char *value; + const pom_conf *section; +}; + +struct main_conf { + struct conf_item *items; + size_t items_count; +}; + +#ifdef POM_NO_STDIO +#define fatal_error(...) abort() +#else +// fatal_error should only be called when the API is misused +// (e.g. `NULL` argument that shouldn't be `NULL`). +static void fatal_error(PRINTF_FORMAT_STRING const char *fmt, ...) ATTRIBUTE_PRINTF(1, 2); +static void +fatal_error(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + abort(); +} +#endif + +// Make an error with no next-error. +static pom_error *make_error(PRINTF_FORMAT_STRING const char *file, uint64_t line, const char *fmt, ...) ATTRIBUTE_PRINTF(3, 4); +static pom_error * +make_error(const char *file, uint64_t line, const char *fmt, ...) { + va_list args, args_copy; + va_start(args, fmt); + va_copy(args_copy, args); + bool bad_fmt = false; + int len = vsnprintf(NULL, 0, fmt, args); + if (len < 0 || len > INT_MAX - sizeof(pom_error) - 1) { + // Should probably never happen? Who knows though. + // In this case, we just use fmt as the error message. + bad_fmt = true; + len = strlen(fmt); + } + pom_error *err = malloc(sizeof(pom_error) + len + 1); + if (err) { + char *message = (char *)(err + 1); + if (bad_fmt) { + strcpy(message, fmt); + } else { + vsnprintf(message, len + 1, fmt, args); + } + err->file = file; + err->line = line; + err->message = message; + err->next = NULL; + } + return err; +} + const pom_error * pom_error_next(const pom_error *error) { + if (!error) return NULL; return error->next; } const char * pom_error_file(const pom_error *error) { + if (!error) + fatal_error("%s called with NULL argument", __func__); return error->file; } uint64_t pom_error_line(const pom_error *error) { + if (!error) + fatal_error("%s called with NULL argument", __func__); return error->line; } const char * pom_error_message(const pom_error *error) { + if (!error) + fatal_error("%s called with NULL argument", __func__); return error->message; } + +#ifndef POM_NO_STDIO +void +pom_error_print(const pom_error *error) { + if (!error) { + fprintf(stderr, "No error.\n"); + return; + } + fprintf(stderr, "Error:\n"); + for (; error; error = pom_error_next(error)) { + fprintf(stderr, "%s:%" PRIu64 ": %s\n", error->file, error->line, error->message); + } +} +#endif + +pom_conf * +pom_load(const char *filename, + size_t (*read_func)(void *userdata, char *buf, size_t len), + void *userdata, pom_error **error) { + if (!filename) + fatal_error("%s called with NULL file name", __func__); + if (!read_func) + fatal_error("%s called with NULL read function", __func__); + // TODO + if (error) *error = NULL; + return NULL; +} + +static size_t +read_string(void *vpstring, char *buf, size_t len) { + const char **pstring = vpstring; + const char *string = *pstring; + size_t i; + for (i = 0; i < len; i++, string++) { + if (*string == 0) break; + buf[i] = *string; + } + *pstring = string; + return i; +} + +pom_conf * +pom_load_string(const char *filename, const char *string, pom_error **error) { + return pom_load(filename, read_string, &string, error); +} + +#ifndef POM_NO_STDIO +static size_t +read_file(void *file, char *buf, size_t len) { + return fread(buf, 1, len, file); +} + +pom_conf * +pom_load_file(const char *filename, FILE *file, pom_error **error) { + if (!filename) + fatal_error("%s called with NULL file name", __func__); + if (!file) + fatal_error("%s called with NULL file", __func__); + return pom_load(filename, read_file, file, error); +} + +pom_conf * +pom_load_path(const char *path, pom_error **error) { + if (!path) + fatal_error("%s called with NULL file name", __func__); + FILE *fp = fopen(path, "rb"); + if (!fp) { + if (error) { + const char *message = strerror(errno); + *error = make_error(path, 1, "Couldn't open file: %s", message); + } + return NULL; + } + pom_conf *conf = pom_load_file(path, fp, error); + fclose(fp); + return conf; +} +#endif |