add config support
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
/scanvalue
|
||||
/scankey
|
||||
/parser
|
||||
/*.so.*
|
||||
@@ -0,0 +1,56 @@
|
||||
HFILES := tomlc17.h
|
||||
CFILES := tomlc17.c
|
||||
OBJ := $(CFILES:.c=.o)
|
||||
|
||||
# set up LIB_SHARED
|
||||
LIB_VERSION := 1.0
|
||||
LIB := libtomlc17.a
|
||||
UNAME := $(shell uname)
|
||||
ifeq ($(UNAME), Darwin)
|
||||
LIB_SHARED =
|
||||
else ifeq ($(OS), Windows_NT)
|
||||
LIB_SHARED =
|
||||
else
|
||||
LIB_SHARED = libtomlc17.so.$(LIB_VERSION)
|
||||
endif
|
||||
EXEC =
|
||||
|
||||
# set up CFLAGS / CXXFLAGS
|
||||
CFLAGS := -std=c17 -Wmissing-declarations -Wall -Wextra -MMD
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -O0 -g -fsanitize=address,undefined
|
||||
else
|
||||
CFLAGS += -O3 -DNDEBUG
|
||||
endif
|
||||
# Add -fpic only for non-Windows platforms
|
||||
ifneq ($(OS), Windows_NT)
|
||||
CFLAGS += -fpic
|
||||
endif
|
||||
|
||||
|
||||
all: $(EXEC) $(LIB) $(LIB_SHARED)
|
||||
|
||||
*.o: $(HFILES)
|
||||
|
||||
libtomlc17.a: tomlc17.o
|
||||
ar -rcs $@ $^
|
||||
|
||||
$(LIB_SHARED): tomlc17.o
|
||||
$(CC) -shared -o $@ $^
|
||||
|
||||
-include $(OBJ:%.o=%.d) $(EXEC:%=%.d)
|
||||
|
||||
test: all
|
||||
|
||||
clean:
|
||||
rm -f *.o *.d *.a *.so.* $(EXEC)
|
||||
|
||||
distclean: clean
|
||||
|
||||
format:
|
||||
clang-format -i *.[ch] *.hpp
|
||||
|
||||
|
||||
.PHONY: all clean distclean format
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,277 @@
|
||||
/* Copyright (c) 2024-2026, CK Tan.
|
||||
* https://github.com/cktan/tomlc17/blob/main/LICENSE
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tomlc17.h
|
||||
* @brief A TOML parser for C17.
|
||||
*
|
||||
* This library provides a simple and efficient way to parse TOML documents
|
||||
* in C. It supports standard TOML features and provides an easy-to-use API
|
||||
* for traversing the parsed data.
|
||||
*/
|
||||
|
||||
#ifndef TOMLC17_H
|
||||
#define TOMLC17_H
|
||||
|
||||
// A crude way to determine version. Manually changed.
|
||||
#define TOMLC17_RELEASE_AFTER "260618"
|
||||
|
||||
/*
|
||||
* USAGE:
|
||||
*
|
||||
* 1. Call toml_parse(), toml_parse_file(), or toml_parse_file_ex()
|
||||
* 2. Check result.ok
|
||||
* 3. Use toml_get() or toml_seek() to query and traverse the
|
||||
* result.toptab
|
||||
* 4. Call toml_free() to release resources.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define TOML_EXTERN extern "C"
|
||||
#else
|
||||
#define TOML_EXTERN extern
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enumeration of TOML data types.
|
||||
*/
|
||||
enum toml_type_t {
|
||||
TOML_UNKNOWN = 0, /**< Unknown or invalid type */
|
||||
TOML_STRING, /**< String type */
|
||||
TOML_INT64, /**< 64-bit integer type */
|
||||
TOML_FP64, /**< 64-bit floating point type */
|
||||
TOML_BOOLEAN, /**< Boolean type */
|
||||
TOML_DATE, /**< Local date type */
|
||||
TOML_TIME, /**< Local time type */
|
||||
TOML_DATETIME, /**< Local datetime type */
|
||||
TOML_DATETIMETZ, /**< Offset datetime type */
|
||||
TOML_ARRAY, /**< Array type */
|
||||
TOML_TABLE, /**< Table type */
|
||||
};
|
||||
typedef enum toml_type_t toml_type_t;
|
||||
|
||||
/**
|
||||
* @brief Represents a single piece of TOML data.
|
||||
*
|
||||
* This structure is a node in a tree that represents a TOML document.
|
||||
* The `u` union contains the actual value based on the `type` field.
|
||||
*/
|
||||
typedef struct toml_datum_t toml_datum_t;
|
||||
struct toml_datum_t {
|
||||
toml_type_t type; /**< Type of the datum */
|
||||
uint32_t flag; /**< Internal flag, do not use */
|
||||
int lineno; /**< 1-based source line number, 0 if synthesized */
|
||||
int colno; /**< 1-based source column number, 0 if synthesized */
|
||||
const char *source; /**< Source name (e.g. filename), NULL if not provided.
|
||||
Owned by the result; valid until toml_free(). */
|
||||
union {
|
||||
const char *s; /**< Shorthand for str.ptr */
|
||||
struct {
|
||||
const char *ptr; /**< NUL terminated string pointer */
|
||||
int len; /**< Length of the string excluding the terminating NUL */
|
||||
} str;
|
||||
int64_t int64; /**< 64-bit integer value */
|
||||
double fp64; /**< 64-bit floating point value */
|
||||
bool boolean; /**< Boolean value */
|
||||
struct { /**< Date and time components */
|
||||
int16_t year, month, day;
|
||||
int16_t hour, minute, second;
|
||||
int32_t usec;
|
||||
int16_t tz; /**< Timezone offset in minutes */
|
||||
} ts;
|
||||
struct { /**< Array data */
|
||||
int32_t size; /**< Number of elements in the array */
|
||||
toml_datum_t *elem; /**< Array of elements */
|
||||
} arr;
|
||||
struct { /**< Table data */
|
||||
int32_t size; /**< Number of keys in the table */
|
||||
const char **key; /**< Array of keys */
|
||||
int *len; /**< Array of key lengths */
|
||||
toml_datum_t *value; /**< Array of values corresponding to keys */
|
||||
} tab;
|
||||
} u;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Result of a TOML parsing operation.
|
||||
*/
|
||||
typedef struct toml_result_t toml_result_t;
|
||||
struct toml_result_t {
|
||||
bool ok; /**< True if parsing was successful */
|
||||
toml_datum_t toptab; /**< The top-level table (valid if ok is true) */
|
||||
char errmsg[200]; /**< Error message (valid if ok is false) */
|
||||
void *__internal; /**< Internal state, do not use */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Parse a TOML document from a string.
|
||||
*
|
||||
* @param src A NUL-terminated string containing the TOML document.
|
||||
* @param len The length of the string (excluding the NUL terminator).
|
||||
* @return A toml_result_t structure. Must be freed with toml_free().
|
||||
*
|
||||
* IMPORTANT: src[] must be a NUL terminated string! The len parameter
|
||||
* does not include the NUL terminator.
|
||||
*/
|
||||
TOML_EXTERN toml_result_t toml_parse(const char *src, int len);
|
||||
|
||||
/**
|
||||
* @brief Parse a TOML document, tagging every datum with a source name.
|
||||
*
|
||||
* @param src A NUL-terminated string containing the TOML document.
|
||||
* @param len The length of the string (excluding the NUL terminator).
|
||||
* @param name A source name (e.g. filename) copied into the result, or NULL.
|
||||
* Every parsed datum's `source` is set to this name (or NULL).
|
||||
* @return A toml_result_t structure. Must be freed with toml_free().
|
||||
*
|
||||
* IMPORTANT: src[] must be a NUL terminated string! The len parameter
|
||||
* does not include the NUL terminator.
|
||||
*/
|
||||
TOML_EXTERN toml_result_t toml_parse_named(const char *src, int len,
|
||||
const char *name);
|
||||
|
||||
/**
|
||||
* @brief Parse a TOML document from a file pointer.
|
||||
*
|
||||
* @param fp A pointer to the open file. The caller is responsible for closing
|
||||
* it.
|
||||
* @return A toml_result_t structure. Must be freed with toml_free().
|
||||
*
|
||||
* IMPORTANT: you are still responsible to fclose(fp).
|
||||
*/
|
||||
TOML_EXTERN toml_result_t toml_parse_file(FILE *fp);
|
||||
|
||||
/**
|
||||
* @brief Parse a TOML document from a file pointer, tagging datums with a name.
|
||||
*
|
||||
* @param fp A pointer to the open file. The caller is responsible for closing
|
||||
* it.
|
||||
* @param name A source name copied into the result, or NULL.
|
||||
* @return A toml_result_t structure. Must be freed with toml_free().
|
||||
*
|
||||
* IMPORTANT: you are still responsible to fclose(fp).
|
||||
*/
|
||||
TOML_EXTERN toml_result_t toml_parse_file_named(FILE *fp, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Parse a TOML document from a file path.
|
||||
*
|
||||
* @param fname The path to the TOML file.
|
||||
* @return A toml_result_t structure. Must be freed with toml_free().
|
||||
*/
|
||||
TOML_EXTERN toml_result_t toml_parse_file_ex(const char *fname);
|
||||
|
||||
/**
|
||||
* @brief Release resources allocated for a TOML result.
|
||||
*
|
||||
* @param result The TOML result to free.
|
||||
*/
|
||||
TOML_EXTERN void toml_free(toml_result_t result);
|
||||
|
||||
/**
|
||||
* @brief Find a value for a specific key in a TOML table.
|
||||
*
|
||||
* @param table The TOML table to search in.
|
||||
* @param key The key to look for.
|
||||
* @return The value associated with the key, or a datum with type TOML_UNKNOWN
|
||||
* if not found.
|
||||
*/
|
||||
TOML_EXTERN toml_datum_t toml_get(toml_datum_t table, const char *key);
|
||||
|
||||
/**
|
||||
* @brief Locate a value using a multipart-key (e.g., "a.b.c").
|
||||
*
|
||||
* @param table The TOML table to start the search from.
|
||||
* @param multipart_key A dot-separated key string. No escape characters
|
||||
* allowed. Maximum length is 255 bytes.
|
||||
* @return The value found, or a datum with type TOML_UNKNOWN if not found.
|
||||
*/
|
||||
TOML_EXTERN toml_datum_t toml_seek(toml_datum_t table,
|
||||
const char *multipart_key);
|
||||
|
||||
/**
|
||||
* @brief OBSOLETE: use toml_get() instead.
|
||||
* Find a key in a toml_table. Return the value of the key if found,
|
||||
* or a TOML_UNKNOWN otherwise.
|
||||
*/
|
||||
static inline toml_datum_t toml_table_find(toml_datum_t table,
|
||||
const char *key) {
|
||||
return toml_get(table, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Merge two TOML results.
|
||||
*
|
||||
* All results (r1, r2, and the returned result) must be freed independently.
|
||||
*
|
||||
* @param r1 The base TOML result.
|
||||
* @param r2 The TOML result containing overrides.
|
||||
* @return A new toml_result_t representing the merged document.
|
||||
*
|
||||
* LOGIC:
|
||||
* ret = copy of r1
|
||||
* for each item x in r2:
|
||||
* if x is not in ret:
|
||||
* set x in ret
|
||||
* elif x in ret is NOT of the same type:
|
||||
* override
|
||||
* elif x in ret is an array of tables:
|
||||
* append r2.x to ret.x
|
||||
* elif x in ret is a table:
|
||||
* merge r2.x to ret.x
|
||||
* else:
|
||||
* override
|
||||
*/
|
||||
TOML_EXTERN toml_result_t toml_merge(const toml_result_t *r1,
|
||||
const toml_result_t *r2);
|
||||
|
||||
/**
|
||||
* @brief Compare two TOML results for equality.
|
||||
*
|
||||
* Comparison is sensitive to the order of elements in arrays and tables.
|
||||
*
|
||||
* @param r1 The first TOML result.
|
||||
* @param r2 The second TOML result.
|
||||
* @return True if they are equivalent, false otherwise.
|
||||
*/
|
||||
TOML_EXTERN bool toml_equiv(const toml_result_t *r1, const toml_result_t *r2);
|
||||
|
||||
/**
|
||||
* @brief Global options for the TOML parser.
|
||||
*/
|
||||
typedef struct toml_option_t toml_option_t;
|
||||
struct toml_option_t {
|
||||
bool check_utf8; /**< If true, check if all characters are valid UTF-8.
|
||||
Default: false. */
|
||||
void *(*mem_realloc)(
|
||||
void *ptr,
|
||||
size_t size); /**< Custom realloc function. Default: realloc(). */
|
||||
void (*mem_free)(void *ptr); /**< Custom free function. Default: free(). */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get the default parser options.
|
||||
*
|
||||
* Use this to obtain and initialize a toml_option_t structure before
|
||||
* customizing it.
|
||||
*
|
||||
* @return A toml_option_t with default values.
|
||||
*/
|
||||
TOML_EXTERN toml_option_t toml_default_option(void);
|
||||
|
||||
/**
|
||||
* @brief Set the global parser options.
|
||||
*
|
||||
* Call this only if you need to override the default behavior.
|
||||
*
|
||||
* @param opt The options to set.
|
||||
*/
|
||||
TOML_EXTERN void toml_set_option(toml_option_t opt);
|
||||
|
||||
#endif // TOMLC17_H
|
||||
@@ -0,0 +1,239 @@
|
||||
#pragma once
|
||||
#if !defined(__cplusplus) || __cplusplus < 202002L
|
||||
#error "This code requires C++20 or later"
|
||||
#endif
|
||||
|
||||
#include "tomlc17.h"
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace toml {
|
||||
|
||||
class Datum : public toml_datum_t {
|
||||
public:
|
||||
using datetime = std::chrono::sys_time<std::chrono::microseconds>;
|
||||
using time = std::chrono::hh_mm_ss<std::chrono::microseconds>;
|
||||
using date = std::chrono::year_month_day;
|
||||
|
||||
Datum(toml_datum_t dat = toml_datum_t{}) : toml_datum_t(dat) {}
|
||||
bool is_table() const { return type == TOML_TABLE; }
|
||||
bool is_array() const { return type == TOML_ARRAY; }
|
||||
|
||||
// Retrieve a string.
|
||||
std::optional<std::string_view> as_str() const {
|
||||
if (type != TOML_STRING)
|
||||
return std::nullopt;
|
||||
else
|
||||
return std::string_view{u.str.ptr, (size_t)u.str.len};
|
||||
}
|
||||
|
||||
// Retrieve an int.
|
||||
std::optional<std::int64_t> as_int() const {
|
||||
if (type != TOML_INT64)
|
||||
return std::nullopt;
|
||||
else
|
||||
return u.int64;
|
||||
}
|
||||
|
||||
// Retrieve a float.
|
||||
std::optional<double> as_real() const {
|
||||
if (type != TOML_FP64)
|
||||
return std::nullopt;
|
||||
else
|
||||
return u.fp64;
|
||||
}
|
||||
|
||||
// Retrieve a boolean.
|
||||
std::optional<bool> as_bool() const {
|
||||
if (type != TOML_BOOLEAN)
|
||||
return std::nullopt;
|
||||
else
|
||||
return !!u.boolean;
|
||||
}
|
||||
|
||||
// Retrieve a date.
|
||||
std::optional<date> as_date() const {
|
||||
using namespace std::chrono;
|
||||
if (type != TOML_DATE)
|
||||
return std::nullopt;
|
||||
else
|
||||
return date{year(u.ts.year), month(u.ts.month), day(u.ts.day)};
|
||||
}
|
||||
|
||||
// Retrieve a time.
|
||||
std::optional<time> as_time() const {
|
||||
using namespace std::chrono;
|
||||
if (type != TOML_TIME)
|
||||
return std::nullopt;
|
||||
else
|
||||
return time{hours(u.ts.hour) + minutes(u.ts.minute) +
|
||||
seconds(u.ts.second) + microseconds(u.ts.usec)};
|
||||
}
|
||||
|
||||
// Retrieve a datetime.
|
||||
std::optional<datetime> as_datetime() const {
|
||||
using namespace std::chrono;
|
||||
if (type != TOML_DATETIME) {
|
||||
return std::nullopt;
|
||||
}
|
||||
year_month_day ymd{year{u.ts.year}, month{(unsigned)u.ts.month},
|
||||
day{(unsigned)u.ts.day}};
|
||||
auto tod = hours{u.ts.hour} + minutes{u.ts.minute} + seconds{u.ts.second} +
|
||||
microseconds{u.ts.usec};
|
||||
return datetime{sys_days{ymd} + tod};
|
||||
}
|
||||
|
||||
// Retrieve a datetime and minute offset from UTC.
|
||||
std::optional<std::pair<datetime, int>> as_datetimetz() const {
|
||||
using namespace std::chrono;
|
||||
|
||||
if (type != TOML_DATETIMETZ) {
|
||||
return std::nullopt;
|
||||
}
|
||||
year_month_day ymd{year{u.ts.year}, month{(unsigned)u.ts.month},
|
||||
day{(unsigned)u.ts.day}};
|
||||
auto tod = hours{u.ts.hour} + minutes{u.ts.minute} + seconds{u.ts.second} +
|
||||
microseconds{u.ts.usec};
|
||||
return std::pair{datetime{sys_days{ymd} + tod}, u.ts.tz};
|
||||
}
|
||||
|
||||
// Retrieve an array of Datum from this datum.
|
||||
std::optional<std::vector<Datum>> as_vector() const {
|
||||
if (type != TOML_ARRAY) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::vector<Datum> ret;
|
||||
ret.assign(u.arr.elem, u.arr.elem + u.arr.size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Retrieve an array of strings from this datum.
|
||||
std::optional<std::vector<std::string_view>> as_strvec() const {
|
||||
try {
|
||||
auto vec = as_vector().value();
|
||||
std::vector<std::string_view> ret;
|
||||
ret.resize(vec.size());
|
||||
for (size_t i = 0; i < vec.size(); i++) {
|
||||
ret[i] = vec[i].as_str().value();
|
||||
}
|
||||
return ret;
|
||||
} catch (const std::bad_optional_access &ex) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve an array of ints from this datum
|
||||
std::optional<std::vector<int64_t>> as_intvec() const {
|
||||
try {
|
||||
auto vec = as_vector().value();
|
||||
std::vector<int64_t> ret;
|
||||
ret.resize(vec.size());
|
||||
for (size_t i = 0; i < vec.size(); i++) {
|
||||
ret[i] = vec[i].as_int().value();
|
||||
}
|
||||
return ret;
|
||||
} catch (const std::bad_optional_access &ex) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve an array of doubles from this datum
|
||||
std::optional<std::vector<double>> as_realvec() const {
|
||||
try {
|
||||
auto vec = as_vector().value();
|
||||
std::vector<double> ret;
|
||||
ret.resize(vec.size());
|
||||
for (size_t i = 0; i < vec.size(); i++) {
|
||||
ret[i] = vec[i].as_real().value();
|
||||
}
|
||||
return ret;
|
||||
} catch (const std::bad_optional_access &ex) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// For tables. Retrieve the value of a composite key from this datum
|
||||
// (which is a table).
|
||||
std::optional<Datum> get(std::initializer_list<std::string_view> keys) const {
|
||||
Datum tab = *this;
|
||||
Datum value;
|
||||
for (auto key : keys) {
|
||||
value = {};
|
||||
if (tab.type != TOML_TABLE) {
|
||||
return std::nullopt;
|
||||
}
|
||||
for (int i = 0; i < tab.u.tab.size; i++) {
|
||||
if (key ==
|
||||
std::string_view{tab.u.tab.key[i], (size_t)tab.u.tab.len[i]}) {
|
||||
value = tab.u.tab.value[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
tab = value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Retrieve the value of a key
|
||||
std::optional<Datum> get(std::string_view key) const { return get({key}); }
|
||||
|
||||
// Retrieve the value of a multi-part key
|
||||
std::optional<Datum> seek(const char *multipart_key) const {
|
||||
auto ret = toml_seek(*this, multipart_key);
|
||||
if (!ret.type) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}; // class Datum
|
||||
|
||||
class Result : private toml_result_t {
|
||||
public:
|
||||
Result(const toml_result_t &result) : toml_result_t(result) {}
|
||||
~Result() noexcept { toml_free(*this); }
|
||||
|
||||
// Disallow copying
|
||||
Result(const Result &) = delete;
|
||||
Result &operator=(const Result &) = delete;
|
||||
|
||||
// Allow moving
|
||||
Result(Result &&other) noexcept : toml_result_t() { swap(*this, other); }
|
||||
Result &operator=(Result &&other) noexcept {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Access methods
|
||||
bool ok() const { return toml_result_t::ok; }
|
||||
Datum toptab() const { return toml_result_t::toptab; }
|
||||
const char *errmsg() const { return toml_result_t::errmsg; }
|
||||
|
||||
// Shortcuts
|
||||
std::optional<Datum> get(std::initializer_list<std::string_view> keys) const {
|
||||
return toptab().get(keys);
|
||||
}
|
||||
std::optional<Datum> seek(const char *multipart_key) const {
|
||||
return toptab().seek(multipart_key);
|
||||
}
|
||||
|
||||
private:
|
||||
friend void swap(Result &a, Result &b) noexcept {
|
||||
std::swap(static_cast<toml_result_t &>(a), static_cast<toml_result_t &>(b));
|
||||
}
|
||||
};
|
||||
|
||||
static inline Result parse_file(FILE *fp) { return toml_parse_file(fp); }
|
||||
|
||||
static inline Result parse_file_ex(const char *fname) {
|
||||
return toml_parse_file_ex(fname);
|
||||
}
|
||||
|
||||
static inline Result parse(const std::string &s) {
|
||||
return toml_parse(s.data(), s.size());
|
||||
}
|
||||
|
||||
}; // namespace toml
|
||||
Reference in New Issue
Block a user