Camel/include/cyaml.h

499 lines
14 KiB
C

#ifndef YAML_H
#define YAML_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
/* --- Public Types --- */
// Node types.
typedef enum {
YAML_UNDEF, // Undefined
YAML_STR, // Scalar string
YAML_SEQ, // Sequence (list)
YAML_MAP // Mapping (dictionary)
} YAMLType;
// Opaque types.
typedef struct YAMLNode YAMLNode;
typedef struct YAMLDoc YAMLDoc;
typedef struct YAMLEmitter YAMLEmitter;
/* --- Document Loading and Parsing --- */
// Load a YAML document from a file. For demonstration, the file content is treated as a scalar.
YAMLDoc* yaml_load_file(const char *filename);
// Load a YAML document from a string. (Currently, the entire string is parsed as a scalar.)
YAMLDoc* yaml_load_str(const char *yaml_string);
// Destroy the document and free all associated memory.
void yaml_doc_destroy(YAMLDoc *doc);
// Get the root node of the document.
const YAMLNode* yaml_doc_root(const YAMLDoc *doc);
/* --- Node Querying and Access --- */
// Get the type of a YAML node.
YAMLType yaml_node_type(const YAMLNode *node);
// Retrieve the scalar value of a node (if it is a scalar), or NULL.
const char* yaml_as_str(const YAMLNode *node);
// Convert a scalar node to int. Returns 0 if not a scalar.
int yaml_as_int(const YAMLNode *node);
// Convert a scalar node to double. Returns 0.0 if not a scalar.
double yaml_as_double(const YAMLNode *node);
// For mapping nodes: get a child node by key (returns NULL if not found).
const YAMLNode* yaml_map_get(const YAMLNode *node, const char *key);
// For sequence nodes: return the number of child nodes.
size_t yaml_seq_size(const YAMLNode *node);
// For sequence nodes: retrieve a child node by index (returns NULL if out of range).
const YAMLNode* yaml_seq_index(const YAMLNode *node, size_t index);
/* --- Node Creation and Modification --- */
// Create a new scalar node with the given string value.
YAMLNode* yaml_new_str(const char *value);
// Create a new, empty sequence node.
YAMLNode* yaml_new_seq(void);
// Create a new, empty mapping node.
YAMLNode* yaml_new_map(void);
// Append a child node to a sequence node.
int yaml_seq_append(YAMLNode *node, YAMLNode *child);
// Set a key/value pair in a mapping node (the key is copied).
int yaml_map_set(YAMLNode *node, const char *key, YAMLNode *child);
// Recursively free a YAML node and its children.
void yaml_node_destroy(YAMLNode *node);
/* --- YAML Emitting --- */
// Create a new YAML emitter.
YAMLEmitter* yaml_emitter_create(void);
// Emit the YAML document into the emitter's buffer.
int yaml_emit(YAMLEmitter *emitter, const YAMLDoc *doc);
// Write the emitter's content to a file (returns 0 on success).
int yaml_emit_to_file(YAMLEmitter *emitter, const char *filename);
// Get a string copy of the emitter's buffer (caller must free it).
char* yaml_emit_to_str(YAMLEmitter *emitter);
// Destroy the emitter and free its memory.
void yaml_emitter_destroy(YAMLEmitter *emitter);
#ifdef __cplusplus
}
#endif
/* --- Implementation --- */
/* To compile the implementation, define YAML_IMPLEMENTATION in one source file before including "yaml.h". */
#ifdef YAML_IMPLEMENTATION
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* --- Internal Structures --- */
struct YAMLDoc {
YAMLNode *root;
};
struct YAMLNode {
YAMLType type;
// For scalar nodes:
char *str;
// For sequence nodes:
YAMLNode **seq;
size_t seq_size;
// For mapping nodes:
char **keys;
YAMLNode **vals;
size_t map_size;
};
struct YAMLEmitter {
char *buffer;
size_t size;
size_t capacity;
};
#define YAMLEMIT_INITIAL_CAPACITY 256
/* --- Document Loading and Parsing --- */
// For demonstration purposes, our "parser" treats the entire input as a scalar node.
// In a full implementation, you would integrate a complete YAML parser.
YAMLDoc* yaml_load_str(const char *yaml_string) {
if (!yaml_string)
return NULL;
YAMLDoc *doc = (YAMLDoc*)malloc(sizeof(YAMLDoc));
if (!doc)
return NULL;
doc->root = (YAMLNode*)malloc(sizeof(YAMLNode));
if (!doc->root) {
free(doc);
return NULL;
}
doc->root->type = YAML_STR;
doc->root->str = strdup(yaml_string);
doc->root->seq = NULL;
doc->root->seq_size = 0;
doc->root->keys = NULL;
doc->root->vals = NULL;
doc->root->map_size = 0;
return doc;
}
YAMLDoc* yaml_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);
YAMLDoc *doc = yaml_load_str(content);
free(content);
return doc;
}
void yaml_doc_destroy(YAMLDoc *doc) {
if (!doc)
return;
if (doc->root)
yaml_node_destroy(doc->root);
free(doc);
}
const YAMLNode* yaml_doc_root(const YAMLDoc *doc) {
if (!doc)
return NULL;
return doc->root;
}
/* --- Node Querying and Access --- */
YAMLType yaml_node_type(const YAMLNode *node) {
if (!node)
return YAML_UNDEF;
return node->type;
}
const char* yaml_as_str(const YAMLNode *node) {
if (!node || node->type != YAML_STR)
return NULL;
return node->str;
}
int yaml_as_int(const YAMLNode *node) {
if (!node || node->type != YAML_STR)
return 0;
return atoi(node->str);
}
double yaml_as_double(const YAMLNode *node) {
if (!node || node->type != YAML_STR)
return 0.0;
return atof(node->str);
}
const YAMLNode* yaml_map_get(const YAMLNode *node, const char *key) {
if (!node || node->type != YAML_MAP)
return NULL;
for (size_t i = 0; i < node->map_size; i++) {
if (strcmp(node->keys[i], key) == 0)
return node->vals[i];
}
return NULL;
}
size_t yaml_seq_size(const YAMLNode *node) {
if (!node || node->type != YAML_SEQ)
return 0;
return node->seq_size;
}
const YAMLNode* yaml_seq_index(const YAMLNode *node, size_t index) {
if (!node || node->type != YAML_SEQ || index >= node->seq_size)
return NULL;
return node->seq[index];
}
/* --- Node Creation and Modification --- */
YAMLNode* yaml_new_str(const char *value) {
YAMLNode *node = (YAMLNode*)malloc(sizeof(YAMLNode));
if (!node)
return NULL;
node->type = YAML_STR;
node->str = strdup(value ? value : "");
node->seq = NULL;
node->seq_size = 0;
node->keys = NULL;
node->vals = NULL;
node->map_size = 0;
return node;
}
YAMLNode* yaml_new_seq(void) {
YAMLNode *node = (YAMLNode*)malloc(sizeof(YAMLNode));
if (!node)
return NULL;
node->type = YAML_SEQ;
node->str = NULL;
node->seq = NULL;
node->seq_size = 0;
node->keys = NULL;
node->vals = NULL;
node->map_size = 0;
return node;
}
YAMLNode* yaml_new_map(void) {
YAMLNode *node = (YAMLNode*)malloc(sizeof(YAMLNode));
if (!node)
return NULL;
node->type = YAML_MAP;
node->str = NULL;
node->seq = NULL;
node->seq_size = 0;
node->keys = NULL;
node->vals = NULL;
node->map_size = 0;
return node;
}
int yaml_seq_append(YAMLNode *node, YAMLNode *child) {
if (!node || node->type != YAML_SEQ)
return -1;
YAMLNode **new_seq = (YAMLNode**)realloc(node->seq, sizeof(YAMLNode*) * (node->seq_size + 1));
if (!new_seq)
return -1;
node->seq = new_seq;
node->seq[node->seq_size] = child;
node->seq_size++;
return 0;
}
int yaml_map_set(YAMLNode *node, const char *key, YAMLNode *child) {
if (!node || node->type != YAML_MAP)
return -1;
// Append new key/value pair
char **new_keys = (char**)realloc(node->keys, sizeof(char*) * (node->map_size + 1));
if (!new_keys)
return -1;
node->keys = new_keys;
node->keys[node->map_size] = strdup(key);
YAMLNode **new_vals = (YAMLNode**)realloc(node->vals, sizeof(YAMLNode*) * (node->map_size + 1));
if (!new_vals)
return -1;
node->vals = new_vals;
node->vals[node->map_size] = child;
node->map_size++;
return 0;
}
void yaml_node_destroy(YAMLNode *node) {
if (!node)
return;
if (node->type == YAML_STR) {
free(node->str);
} else if (node->type == YAML_SEQ) {
for (size_t i = 0; i < node->seq_size; i++) {
yaml_node_destroy(node->seq[i]);
}
free(node->seq);
} else if (node->type == YAML_MAP) {
for (size_t i = 0; i < node->map_size; i++) {
free(node->keys[i]);
yaml_node_destroy(node->vals[i]);
}
free(node->keys);
free(node->vals);
}
free(node);
}
/* --- YAML Emitting --- */
YAMLEmitter* yaml_emitter_create(void) {
YAMLEmitter *emitter = (YAMLEmitter*)malloc(sizeof(YAMLEmitter));
if (!emitter)
return NULL;
emitter->buffer = (char*)malloc(YAMLEMIT_INITIAL_CAPACITY);
if (!emitter->buffer) {
free(emitter);
return NULL;
}
emitter->buffer[0] = '\0';
emitter->size = 0;
emitter->capacity = YAMLEMIT_INITIAL_CAPACITY;
return emitter;
}
// Internal function: append string to emitter's buffer.
static int yaml_emitter_append(YAMLEmitter *emitter, const char *str) {
if (!emitter || !str)
return -1;
size_t len = strlen(str);
if (emitter->size + len + 1 > emitter->capacity) {
size_t new_cap = emitter->capacity * 2;
while (emitter->size + len + 1 > new_cap)
new_cap *= 2;
char *new_buf = (char*)realloc(emitter->buffer, new_cap);
if (!new_buf)
return -1;
emitter->buffer = new_buf;
emitter->capacity = new_cap;
}
memcpy(emitter->buffer + emitter->size, str, len);
emitter->size += len;
emitter->buffer[emitter->size] = '\0';
return 0;
}
// Recursive function to emit a YAML node with proper indentation.
static int yaml_emit_node(YAMLEmitter *emitter, const YAMLNode *node, int indent) {
char indent_str[256];
if (indent < (int)sizeof(indent_str)) {
memset(indent_str, ' ', indent);
indent_str[indent] = '\0';
} else {
indent_str[0] = '\0';
}
char line_buf[1024];
int ret;
if (!node)
return -1;
switch (node->type) {
case YAML_STR:
// Use block scalar style if the string contains newline(s).
if (strchr(node->str, '\n')) {
ret = yaml_emitter_append(emitter, "|-\n");
if (ret)
return ret;
char *copy = strdup(node->str);
char *line = strtok(copy, "\n");
while (line) {
snprintf(line_buf, sizeof(line_buf), "%s%s\n", indent_str, line);
ret = yaml_emitter_append(emitter, line_buf);
if (ret) {
free(copy);
return ret;
}
line = strtok(NULL, "\n");
}
free(copy);
} else {
snprintf(line_buf, sizeof(line_buf), "%s%s\n", indent_str, node->str);
ret = yaml_emitter_append(emitter, line_buf);
if (ret)
return ret;
}
break;
case YAML_SEQ:
for (size_t i = 0; i < node->seq_size; i++) {
snprintf(line_buf, sizeof(line_buf), "%s- ", indent_str);
ret = yaml_emitter_append(emitter, line_buf);
if (ret)
return ret;
if (node->seq[i]->type == YAML_STR) {
ret = yaml_emitter_append(emitter, node->seq[i]->str);
ret = yaml_emitter_append(emitter, "\n");
if (ret)
return ret;
} else {
ret = yaml_emitter_append(emitter, "\n");
if (ret)
return ret;
ret = yaml_emit_node(emitter, node->seq[i], indent + 2);
if (ret)
return ret;
}
}
break;
case YAML_MAP:
for (size_t i = 0; i < node->map_size; i++) {
snprintf(line_buf, sizeof(line_buf), "%s%s: ", indent_str, node->keys[i]);
ret = yaml_emitter_append(emitter, line_buf);
if (ret)
return ret;
if (node->vals[i]->type == YAML_STR) {
ret = yaml_emitter_append(emitter, node->vals[i]->str);
ret = yaml_emitter_append(emitter, "\n");
if (ret)
return ret;
} else {
ret = yaml_emitter_append(emitter, "\n");
if (ret)
return ret;
ret = yaml_emit_node(emitter, node->vals[i], indent + 2);
if (ret)
return ret;
}
}
break;
default:
break;
}
return 0;
}
int yaml_emit(YAMLEmitter *emitter, const YAMLDoc *doc) {
if (!emitter || !doc || !doc->root)
return -1;
return yaml_emit_node(emitter, doc->root, 0);
}
int yaml_emit_to_file(YAMLEmitter *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* yaml_emit_to_str(YAMLEmitter *emitter) {
if (!emitter)
return NULL;
return strdup(emitter->buffer);
}
void yaml_emitter_destroy(YAMLEmitter *emitter) {
if (!emitter)
return;
free(emitter->buffer);
free(emitter);
}
#endif // YAML_IMPLEMENTATION
#endif // YAML_H