2025-03-31 14:56:58 +00:00
|
|
|
#ifndef CYAML_H
|
|
|
|
#define CYAML_H
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
|
|
|
/* --- API Declarations --- */
|
|
|
|
|
|
|
|
// Forward declarations for opaque types.
|
|
|
|
typedef struct cyaml_node_t cyaml_node_t;
|
|
|
|
typedef struct cyaml_document_t cyaml_document_t;
|
|
|
|
typedef struct cyaml_emitter_t cyaml_emitter_t;
|
|
|
|
|
|
|
|
// Node types.
|
|
|
|
typedef enum {
|
|
|
|
CYAML_NODE_UNDEFINED,
|
|
|
|
CYAML_NODE_SCALAR,
|
|
|
|
CYAML_NODE_SEQUENCE,
|
|
|
|
CYAML_NODE_MAPPING
|
|
|
|
} cyaml_node_type_t;
|
|
|
|
|
|
|
|
/* Document Loading and Parsing */
|
|
|
|
|
|
|
|
// Load YAML document from a file.
|
|
|
|
// Returns a pointer to a cyaml_document_t or NULL on error.
|
|
|
|
cyaml_document_t* cyaml_load_file(const char *filename);
|
|
|
|
|
|
|
|
// Load YAML document from a string.
|
|
|
|
// Returns a pointer to a cyaml_document_t or NULL on error.
|
|
|
|
cyaml_document_t* cyaml_load_string(const char *yaml_string);
|
|
|
|
|
|
|
|
// Free the document and all associated nodes.
|
|
|
|
void cyaml_document_destroy(cyaml_document_t *doc);
|
|
|
|
|
|
|
|
// Get the root node of the document.
|
|
|
|
const cyaml_node_t* cyaml_document_get_root(const cyaml_document_t *doc);
|
|
|
|
|
|
|
|
/* Node Querying and Access */
|
|
|
|
|
|
|
|
// Get the type of a node.
|
|
|
|
cyaml_node_type_t cyaml_node_get_type(const cyaml_node_t *node);
|
|
|
|
|
|
|
|
// For scalar nodes: retrieve as a string. Returns NULL if not convertible.
|
|
|
|
const char* cyaml_node_as_string(const cyaml_node_t *node);
|
|
|
|
|
|
|
|
// For scalar nodes: retrieve as an integer.
|
|
|
|
int cyaml_node_as_int(const cyaml_node_t *node);
|
|
|
|
|
|
|
|
// For scalar nodes: retrieve as a double.
|
|
|
|
double cyaml_node_as_double(const cyaml_node_t *node);
|
|
|
|
|
|
|
|
// For mapping nodes: retrieve a child node by key. Returns NULL if not found.
|
|
|
|
const cyaml_node_t* cyaml_node_get(const cyaml_node_t *node, const char *key);
|
|
|
|
|
|
|
|
// For sequence nodes: return the number of child nodes.
|
|
|
|
size_t cyaml_node_size(const cyaml_node_t *node);
|
|
|
|
|
|
|
|
// For sequence nodes: retrieve a child node by index.
|
|
|
|
const cyaml_node_t* cyaml_node_index(const cyaml_node_t *node, size_t index);
|
|
|
|
|
|
|
|
/* Emitting YAML */
|
|
|
|
|
|
|
|
// Create an emitter object.
|
|
|
|
cyaml_emitter_t* cyaml_emitter_create(void);
|
|
|
|
|
|
|
|
// Emit a document into the emitter's internal buffer.
|
|
|
|
int cyaml_emitter_emit(cyaml_emitter_t *emitter, const cyaml_document_t *doc);
|
|
|
|
|
|
|
|
// Write the emitter's contents to a file.
|
|
|
|
// Returns 0 on success, non-zero on error.
|
|
|
|
int cyaml_emitter_to_file(cyaml_emitter_t *emitter, const char *filename);
|
|
|
|
|
|
|
|
// Retrieve a string copy of the emitter's contents.
|
|
|
|
// The caller is responsible for freeing the returned string.
|
|
|
|
char* cyaml_emitter_to_string(cyaml_emitter_t *emitter);
|
|
|
|
|
|
|
|
// Destroy the emitter and free associated memory.
|
|
|
|
void cyaml_emitter_destroy(cyaml_emitter_t *emitter);
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* --- Implementation --- */
|
|
|
|
/* To include the implementation, define CYAML_IMPLEMENTATION in *one* source file before including "cyaml.h". */
|
|
|
|
|
|
|
|
#ifdef CYAML_IMPLEMENTATION
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
/* Internal structures */
|
|
|
|
|
|
|
|
struct cyaml_document_t {
|
|
|
|
cyaml_node_t *root;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct cyaml_node_t {
|
|
|
|
cyaml_node_type_t type;
|
|
|
|
char *scalar;
|
|
|
|
// For sequence nodes.
|
|
|
|
cyaml_node_t **sequence;
|
|
|
|
size_t sequence_size;
|
|
|
|
// For mapping nodes.
|
|
|
|
char **keys;
|
|
|
|
cyaml_node_t **values;
|
|
|
|
size_t mapping_size;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct cyaml_emitter_t {
|
|
|
|
char *buffer;
|
|
|
|
size_t size;
|
|
|
|
size_t capacity;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define CYAML_EMITTER_INITIAL_CAPACITY 256
|
|
|
|
|
|
|
|
/* --- Document Loading and Parsing --- */
|
|
|
|
|
|
|
|
// Loads the entire file into a buffer and then calls cyaml_load_string().
|
|
|
|
cyaml_document_t* cyaml_load_file(const char *filename) {
|
|
|
|
FILE *file = fopen(filename, "rb");
|
|
|
|
if (!file) return NULL;
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
|
long length = ftell(file);
|
|
|
|
rewind(file);
|
|
|
|
char *content = (char*)malloc(length + 1);
|
|
|
|
if (!content) {
|
|
|
|
fclose(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
fread(content, 1, length, file);
|
|
|
|
content[length] = '\0';
|
|
|
|
fclose(file);
|
|
|
|
cyaml_document_t *doc = cyaml_load_string(content);
|
|
|
|
free(content);
|
|
|
|
return doc;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For demonstration, cyaml_load_string() creates a document with a single scalar node.
|
|
|
|
cyaml_document_t* cyaml_load_string(const char *yaml_string) {
|
|
|
|
cyaml_document_t *doc = (cyaml_document_t*)malloc(sizeof(cyaml_document_t));
|
|
|
|
if (!doc) return NULL;
|
|
|
|
doc->root = (cyaml_node_t*)malloc(sizeof(cyaml_node_t));
|
|
|
|
if (!doc->root) {
|
|
|
|
free(doc);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
doc->root->type = CYAML_NODE_SCALAR;
|
|
|
|
doc->root->scalar = strdup(yaml_string);
|
|
|
|
doc->root->sequence = NULL;
|
|
|
|
doc->root->sequence_size = 0;
|
|
|
|
doc->root->keys = NULL;
|
|
|
|
doc->root->values = NULL;
|
|
|
|
doc->root->mapping_size = 0;
|
|
|
|
return doc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyaml_document_destroy(cyaml_document_t *doc) {
|
|
|
|
if (!doc) return;
|
|
|
|
// For this demonstration, assume only a scalar root node exists.
|
|
|
|
if (doc->root) {
|
|
|
|
if (doc->root->scalar)
|
|
|
|
free(doc->root->scalar);
|
|
|
|
free(doc->root);
|
|
|
|
}
|
|
|
|
free(doc);
|
|
|
|
}
|
|
|
|
|
|
|
|
const cyaml_node_t* cyaml_document_get_root(const cyaml_document_t *doc) {
|
|
|
|
if (!doc) return NULL;
|
|
|
|
return doc->root;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --- Node Querying and Access --- */
|
|
|
|
|
|
|
|
cyaml_node_type_t cyaml_node_get_type(const cyaml_node_t *node) {
|
|
|
|
if (!node) return CYAML_NODE_UNDEFINED;
|
|
|
|
return node->type;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* cyaml_node_as_string(const cyaml_node_t *node) {
|
|
|
|
if (!node || node->type != CYAML_NODE_SCALAR) return NULL;
|
|
|
|
return node->scalar;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cyaml_node_as_int(const cyaml_node_t *node) {
|
|
|
|
if (!node || node->type != CYAML_NODE_SCALAR) return 0;
|
|
|
|
return atoi(node->scalar);
|
|
|
|
}
|
|
|
|
|
|
|
|
double cyaml_node_as_double(const cyaml_node_t *node) {
|
|
|
|
if (!node || node->type != CYAML_NODE_SCALAR) return 0.0;
|
|
|
|
return atof(node->scalar);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dummy mapping lookup: searches keys in a mapping node.
|
|
|
|
const cyaml_node_t* cyaml_node_get(const cyaml_node_t *node, const char *key) {
|
|
|
|
if (!node || node->type != CYAML_NODE_MAPPING) return NULL;
|
|
|
|
for (size_t i = 0; i < node->mapping_size; i++) {
|
|
|
|
if (strcmp(node->keys[i], key) == 0) {
|
|
|
|
return node->values[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t cyaml_node_size(const cyaml_node_t *node) {
|
|
|
|
if (!node) return 0;
|
|
|
|
if (node->type == CYAML_NODE_SEQUENCE)
|
|
|
|
return node->sequence_size;
|
|
|
|
if (node->type == CYAML_NODE_MAPPING)
|
|
|
|
return node->mapping_size;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const cyaml_node_t* cyaml_node_index(const cyaml_node_t *node, size_t index) {
|
|
|
|
if (!node) return NULL;
|
|
|
|
if (node->type == CYAML_NODE_SEQUENCE && index < node->sequence_size)
|
|
|
|
return node->sequence[index];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --- YAML Emitting --- */
|
|
|
|
|
|
|
|
cyaml_emitter_t* cyaml_emitter_create(void) {
|
|
|
|
cyaml_emitter_t *emitter = (cyaml_emitter_t*)malloc(sizeof(cyaml_emitter_t));
|
|
|
|
if (!emitter) return NULL;
|
|
|
|
emitter->buffer = (char*)malloc(CYAML_EMITTER_INITIAL_CAPACITY);
|
|
|
|
if (!emitter->buffer) {
|
|
|
|
free(emitter);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
emitter->buffer[0] = '\0';
|
|
|
|
emitter->size = 0;
|
|
|
|
emitter->capacity = CYAML_EMITTER_INITIAL_CAPACITY;
|
|
|
|
return emitter;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Internal helper to append text to the emitter's buffer.
|
|
|
|
static int cyaml_emitter_append(cyaml_emitter_t *emitter, const char *str) {
|
|
|
|
if (!emitter || !str) return -1;
|
|
|
|
size_t len = strlen(str);
|
|
|
|
if (emitter->size + len + 1 > emitter->capacity) {
|
|
|
|
size_t new_capacity = emitter->capacity * 2;
|
|
|
|
while (emitter->size + len + 1 > new_capacity) {
|
|
|
|
new_capacity *= 2;
|
|
|
|
}
|
|
|
|
char *new_buffer = (char*)realloc(emitter->buffer, new_capacity);
|
|
|
|
if (!new_buffer) return -1;
|
|
|
|
emitter->buffer = new_buffer;
|
|
|
|
emitter->capacity = new_capacity;
|
|
|
|
}
|
|
|
|
memcpy(emitter->buffer + emitter->size, str, len + 1);
|
|
|
|
emitter->size += len;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A simple emitter that outputs the root scalar.
|
|
|
|
int cyaml_emitter_emit(cyaml_emitter_t *emitter, const cyaml_document_t *doc) {
|
|
|
|
if (!emitter || !doc) return -1;
|
|
|
|
if (doc->root && doc->root->type == CYAML_NODE_SCALAR) {
|
|
|
|
return cyaml_emitter_append(emitter, doc->root->scalar);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cyaml_emitter_to_file(cyaml_emitter_t *emitter, const char *filename) {
|
|
|
|
if (!emitter || !filename) return -1;
|
|
|
|
FILE *file = fopen(filename, "w");
|
|
|
|
if (!file) return -1;
|
|
|
|
fwrite(emitter->buffer, 1, emitter->size, file);
|
|
|
|
fclose(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* cyaml_emitter_to_string(cyaml_emitter_t *emitter) {
|
|
|
|
if (!emitter) return NULL;
|
|
|
|
return strdup(emitter->buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cyaml_emitter_destroy(cyaml_emitter_t *emitter) {
|
|
|
|
if (!emitter) return;
|
|
|
|
if (emitter->buffer)
|
|
|
|
free(emitter->buffer);
|
|
|
|
free(emitter);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // CYAML_IMPLEMENTATION
|
|
|
|
|
|
|
|
#endif // CYAML_H
|