libyaml C-language wrapper
Need
- libyaml
- apach portable routine
// -*- mode:c++; indent-tabs-mode: nil -*- | |
// | |
// yaml_parser.c | |
// | |
#include <stdint.h> | |
#include <stdbool.h> | |
#include <assert.h> | |
#include <yaml.h> | |
#include <apr_general.h> | |
#include <apr_tables.h> | |
#include <apr_strings.h> | |
#include <apr_hash.h> | |
#ifdef NDEBUG | |
#define dprintf(...) ((void)0) | |
#else | |
#define dprintf(format, args ...) fprintf(stderr, "%s:%d: " format, __func__, __LINE__,## args) | |
#define dprint_var(var,f) fprintf(stderr, #var "=%" #f "\n", var) | |
#endif | |
typedef enum yaml_object_type_e { | |
eNULL, | |
eSEQUENCE, | |
eMAPPING, | |
eVALUE, | |
eEOL | |
} yaml_object_type_t; | |
typedef struct yaml_object_s { | |
yaml_object_type_t type; | |
apr_array_header_t *sequence; | |
apr_hash_t *mapping; | |
uint8_t *value; | |
} yaml_object_t; | |
static apr_array_header_t *parseSequence ( apr_pool_t * pool, | |
yaml_parser_t * parser ); | |
static apr_hash_t *parseMapping ( apr_pool_t * pool, yaml_parser_t * parser ); | |
static uint8_t *parseValue ( apr_pool_t * pool, yaml_event_t * event ); | |
#ifndef NDEBUG | |
void debug_print ( uint8_t *, int, yaml_object_t * ); | |
#endif | |
yaml_object_t * | |
yamlToObject ( apr_pool_t * pool, uint8_t * string ) | |
{ | |
yaml_parser_t parser; | |
yaml_parser_initialize ( &parser ); | |
const char *chars = ( uint8_t * ) apr_pstrdup ( pool, string ); | |
yaml_parser_set_input_string ( &parser, ( const uint8_t * ) chars, | |
strlen ( chars ) ); | |
while ( true ) { | |
yaml_event_t event; | |
yaml_object_t *ret = | |
( yaml_object_t * ) apr_palloc ( pool, sizeof ( yaml_object_t ) ); | |
assert ( ret ); | |
if ( !yaml_parser_parse ( &parser, &event ) ) { | |
yaml_parser_delete ( &parser ); | |
ret->type = eNULL; | |
ret->mapping = ( void * ) NULL; | |
ret->sequence = ( void * ) NULL; | |
ret->value = ( void * ) NULL; | |
return ret; | |
} | |
switch ( event.type ) { | |
case YAML_SCALAR_EVENT: | |
{ | |
uint8_t *value = parseValue ( pool, &event ); | |
yaml_event_delete ( &event ); | |
yaml_parser_delete ( &parser ); | |
ret->type = eVALUE; | |
ret->mapping = ( void * ) NULL; | |
ret->sequence = ( void * ) NULL; | |
ret->value = value; | |
return ret; | |
} | |
case YAML_SEQUENCE_START_EVENT: | |
{ | |
apr_array_header_t *sequenceValue = parseSequence ( pool, &parser ); | |
yaml_event_delete ( &event ); | |
yaml_parser_delete ( &parser ); | |
ret->type = eSEQUENCE; | |
ret->mapping = ( void * ) NULL; | |
ret->sequence = sequenceValue; | |
ret->value = ( void * ) NULL; | |
return ret; | |
} | |
case YAML_MAPPING_START_EVENT: | |
{ | |
apr_hash_t *mappingValue = parseMapping ( pool, &parser ); | |
yaml_event_delete ( &event ); | |
yaml_parser_delete ( &parser ); | |
ret->type = eMAPPING; | |
ret->mapping = mappingValue; | |
ret->sequence = ( void * ) NULL; | |
ret->value = ( void * ) NULL; | |
return ret; | |
} | |
case YAML_STREAM_END_EVENT: | |
{ | |
yaml_event_delete ( &event ); | |
yaml_parser_delete ( &parser ); | |
ret->type = eNULL; | |
ret->mapping = ( void * ) NULL; | |
ret->sequence = ( void * ) NULL; | |
ret->value = ( void * ) NULL; | |
return ret; | |
} | |
} | |
yaml_event_delete ( &event ); | |
} | |
} | |
yaml_object_t * | |
searchObject ( apr_pool_t * pool, yaml_object_t * obj, uint8_t * key ) | |
{ | |
#ifndef NDEBUG | |
dprintf ( "%s: key=%s\n", __func__, key ); | |
#endif | |
while ( true ) { | |
switch ( obj->type ) { | |
case eNULL: | |
#ifndef NDEBUG | |
dprintf ( "type NULL: " ); | |
#endif | |
break; | |
case eSEQUENCE: | |
#ifndef NDEBUG | |
dprintf ( "type SEQUENCE: " ); | |
#endif | |
{ | |
int j; | |
apr_array_header_t *s = obj->sequence; | |
for ( j = 0; j < s->nelts; j++ ) { | |
yaml_object_t *v = | |
*( yaml_object_t ** ) ( s->elts + ( s->elt_size * j ) ); | |
if ( v->type == eVALUE ) { | |
#ifndef NDEBUG | |
dprintf ( "%s ", v->value ); | |
#endif | |
if ( apr_strnatcmp ( key, v->value ) == 0 ) | |
return obj; | |
} else { | |
yaml_object_t *res; | |
#ifndef NDEBUG | |
if ( v->type == eMAPPING ) { | |
dprintf ( "v is mapping\n" ); | |
} else if ( v->type == eSEQUENCE ) { | |
dprintf ( "v is sequence\n" ); | |
} | |
#endif | |
res = searchObject ( pool, v, key ); | |
if ( res ) | |
return res; | |
} | |
} | |
return NULL; | |
} | |
break; | |
case eMAPPING: | |
#ifndef NDEBUG | |
dprintf ( "type MAPPING: " ); | |
#endif | |
{ | |
int j; | |
apr_hash_t *m = obj->mapping; | |
apr_hash_index_t *idx; | |
for ( idx = apr_hash_first ( NULL, m ); idx; | |
idx = apr_hash_next ( idx ) ) { | |
const char *k; | |
yaml_object_t *v, *res; | |
apr_hash_this ( idx, ( const void ** ) &k, NULL, ( void ** ) &v ); | |
#ifndef NDEBUG | |
if ( v->type == eVALUE ) { | |
dprintf ( "key=%s, val=%s\n", k, v->value ); | |
} else if ( v->type == eMAPPING ) { | |
dprintf ( "v is mapping\n" ); | |
} else if ( v->type == eSEQUENCE ) { | |
dprintf ( "v is sequence\n" ); | |
} | |
#endif | |
if ( apr_strnatcmp ( key, k ) == 0 ) | |
return obj; | |
res = searchObject ( pool, v, key ); | |
if ( res != ( void * ) NULL ) | |
return res; | |
} | |
return NULL; | |
} | |
break; | |
case eVALUE: | |
#ifndef NDEBUG | |
dprintf ( "type VALUE: " ); | |
dprintf ( "%s\n", ( uint8_t * ) obj->value ); | |
#endif | |
if ( apr_strnatcmp ( key, obj->value ) == 0 ) | |
return obj; | |
else | |
return NULL; | |
case eEOL: | |
#ifndef NDEBUG | |
dprintf ( "type EOL: " ); | |
#endif | |
break; | |
} | |
} | |
return NULL; | |
} | |
static apr_array_header_t * | |
parseSequence ( apr_pool_t * pool, yaml_parser_t * parser ) | |
{ | |
apr_array_header_t *sequence = | |
apr_array_make ( pool, 0, sizeof ( yaml_object_t ) ); | |
while ( true ) { | |
yaml_event_t event; | |
if ( !yaml_parser_parse ( parser, &event ) ) { | |
return NULL; | |
} | |
switch ( event.type ) { | |
case YAML_SCALAR_EVENT: | |
{ | |
uint8_t *value = parseValue ( pool, &event ); | |
assert ( value ); | |
yaml_object_t *val = | |
( yaml_object_t * ) apr_palloc ( pool, sizeof ( yaml_object_t ) ); | |
assert ( val ); | |
val->type = eVALUE; | |
val->mapping = ( void * ) NULL; | |
val->sequence = ( void * ) NULL; | |
val->value = value; | |
*( yaml_object_t ** ) apr_array_push ( sequence ) = val; | |
break; | |
} | |
case YAML_SEQUENCE_START_EVENT: | |
{ | |
apr_array_header_t *sequenceValue = parseSequence ( pool, parser ); | |
if ( !sequenceValue ) { | |
yaml_event_delete ( &event ); | |
return NULL; | |
} | |
yaml_object_t *val = | |
( yaml_object_t * ) apr_palloc ( pool, sizeof ( yaml_object_t ) ); | |
assert ( val ); | |
val->type = eSEQUENCE; | |
val->mapping = ( void * ) NULL; | |
val->sequence = sequenceValue; | |
val->value = ( void * ) NULL; | |
*( yaml_object_t ** ) apr_array_push ( sequence ) = val; | |
break; | |
} | |
case YAML_SEQUENCE_END_EVENT: | |
{ | |
yaml_event_delete ( &event ); | |
return sequence; | |
} | |
case YAML_MAPPING_START_EVENT: | |
{ | |
apr_hash_t *mappingValue = parseMapping ( pool, parser ); | |
if ( !mappingValue ) { | |
yaml_event_delete ( &event ); | |
return NULL; | |
} | |
yaml_object_t *val = | |
( yaml_object_t * ) apr_palloc ( pool, sizeof ( yaml_object_t ) ); | |
assert ( val ); | |
val->type = eMAPPING; | |
val->mapping = mappingValue; | |
val->sequence = ( void * ) NULL; | |
val->value = ( void * ) NULL; | |
*( yaml_object_t ** ) apr_array_push ( sequence ) = val; | |
break; | |
} | |
case YAML_STREAM_END_EVENT: | |
{ | |
yaml_event_delete ( &event ); | |
return NULL; | |
} | |
} | |
yaml_event_delete ( &event ); | |
} | |
} | |
static apr_hash_t * | |
parseMapping ( apr_pool_t * pool, yaml_parser_t * parser ) | |
{ | |
apr_hash_t *mapping = apr_hash_make ( pool ); | |
uint8_t *key = ( void * ) NULL; | |
while ( true ) { | |
yaml_event_t event; | |
if ( !yaml_parser_parse ( parser, &event ) ) { | |
return NULL; | |
} | |
switch ( event.type ) { | |
case YAML_SCALAR_EVENT: | |
{ | |
uint8_t *value = parseValue ( pool, &event ); | |
assert ( value ); | |
if ( !key ) { | |
key = value; | |
} else { | |
yaml_object_t *val = | |
( yaml_object_t * ) apr_palloc ( pool, sizeof ( yaml_object_t ) ); | |
assert ( val ); | |
val->type = eVALUE; | |
val->value = value; | |
apr_hash_set ( mapping, ( uint8_t * ) apr_pstrdup ( pool, key ), | |
APR_HASH_KEY_STRING, ( void * ) val ); | |
key = ( void * ) NULL; | |
} | |
break; | |
} | |
case YAML_SEQUENCE_START_EVENT: | |
{ | |
apr_array_header_t *sequenceValue = parseSequence ( pool, parser ); | |
if ( !sequenceValue ) { | |
yaml_event_delete ( &event ); | |
return NULL; | |
} | |
if ( key ) { | |
yaml_object_t *val = | |
( yaml_object_t * ) apr_palloc ( pool, sizeof ( yaml_object_t ) ); | |
assert ( val ); | |
val->type = eSEQUENCE; | |
val->sequence = sequenceValue; | |
apr_hash_set ( mapping, ( uint8_t * ) apr_pstrdup ( pool, key ), | |
APR_HASH_KEY_STRING, ( void * ) val ); | |
key = ( void * ) NULL; | |
} | |
break; | |
} | |
case YAML_MAPPING_START_EVENT: | |
{ | |
apr_hash_t *mappingValue = parseMapping ( pool, parser ); | |
if ( !mappingValue ) { | |
yaml_event_delete ( &event ); | |
return NULL; | |
} | |
if ( key ) { | |
yaml_object_t *val = | |
( yaml_object_t * ) apr_palloc ( pool, sizeof ( yaml_object_t ) ); | |
assert ( val ); | |
val->type = eMAPPING; | |
val->mapping = mappingValue; | |
apr_hash_set ( mapping, ( uint8_t * ) apr_pstrdup ( pool, key ), | |
APR_HASH_KEY_STRING, ( void * ) val ); | |
key = ( void * ) NULL; | |
} | |
break; | |
} | |
case YAML_MAPPING_END_EVENT: | |
{ | |
yaml_event_delete ( &event ); | |
return mapping; | |
} | |
case YAML_STREAM_END_EVENT: | |
{ | |
yaml_event_delete ( &event ); | |
return NULL; | |
} | |
} | |
yaml_event_delete ( &event ); | |
} | |
} | |
static uint8_t * | |
parseValue ( apr_pool_t * pool, yaml_event_t * event ) | |
{ | |
uint8_t *value = | |
( uint8_t * ) apr_pstrdup ( pool, event->data.scalar.value ); | |
if ( event->data.scalar.quoted_implicit ) { | |
return value; | |
} | |
// TODO: Add other type conversions. | |
return value; | |
} | |
#ifndef NDEBUG | |
debug_print ( uint8_t * func, int line, yaml_object_t * obj ) | |
{ | |
if ( obj ) { | |
fprintf ( stderr, "%s:%d = ", func, line ); | |
switch ( obj->type ) { | |
case eNULL: | |
fprintf ( stderr, "type NULL: " ); | |
break; | |
case eSEQUENCE: | |
fprintf ( stderr, "type SEQUENCE: " ); | |
{ | |
int j; | |
apr_array_header_t *s = obj->sequence; | |
for ( j = 0; j < s->nelts; j++ ) { | |
yaml_object_t *v = | |
*( yaml_object_t ** ) ( s->elts + ( s->elt_size * j ) ); | |
fprintf ( stderr, "%s ", v->value ); | |
} | |
fprintf ( stderr, "\n" ); | |
fflush ( stdout ); | |
} | |
break; | |
case eMAPPING: | |
fprintf ( stderr, "type MAPPING: " ); | |
{ | |
int j; | |
apr_hash_t *m = obj->mapping; | |
apr_hash_index_t *idx; | |
for ( idx = apr_hash_first ( NULL, m ); idx; | |
idx = apr_hash_next ( idx ) ) { | |
const char *k; | |
yaml_object_t *v; | |
apr_hash_this ( idx, ( const void ** ) &k, NULL, ( void ** ) &v ); | |
fprintf ( stderr, "key=%s, val=%s\n", k, v->value ); | |
} | |
fprintf ( stderr, "\n" ); | |
fflush ( stdout ); | |
} | |
break; | |
case eVALUE: | |
fprintf ( stderr, "type VALUE: " ); | |
{ | |
fprintf ( stderr, "%s\n", ( uint8_t * ) obj->value ); | |
} | |
break; | |
case eEOL: | |
fprintf ( stderr, "type EOL: " ); | |
break; | |
} | |
} | |
} | |
#endif |
libyaml C-language wrapper
Need