1642 lines
46 KiB
C
1642 lines
46 KiB
C
/* This file is an autogenerated single-file version of liblcfg.
|
|
* It is recommended that you update this file on a regular
|
|
* basis from the original liblcfg distribution package.
|
|
*
|
|
* The most recent version of liblcfg is available at
|
|
* <http://liblcfg.carnivore.it>
|
|
*/
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include "lcfg_static.h"
|
|
/*** begin file include/lcfg/lcfg_string.h ***/
|
|
/*
|
|
Copyright (c) 2012, Paul Baecher
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the <organization> nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#ifndef LCFG_STRING_H
|
|
#define LCFG_STRING_H
|
|
|
|
struct lcfg_string * lcfg_string_new();
|
|
struct lcfg_string * lcfg_string_new_copy(struct lcfg_string *);
|
|
int lcfg_string_set(struct lcfg_string *, const char *);
|
|
int lcfg_string_cat_char(struct lcfg_string *, char);
|
|
int lcfg_string_cat_cstr(struct lcfg_string *, const char *);
|
|
int lcfg_string_cat_uint(struct lcfg_string *, unsigned int);
|
|
int lcfg_string_find(struct lcfg_string *, char);
|
|
int lcfg_string_rfind(struct lcfg_string *, char);
|
|
void lcfg_string_trunc(struct lcfg_string *, unsigned int);
|
|
const char * lcfg_string_cstr(struct lcfg_string *);
|
|
unsigned int lcfg_string_len(struct lcfg_string *);
|
|
void lcfg_string_delete(struct lcfg_string *);
|
|
|
|
#endif
|
|
/*** end file include/lcfg/lcfg_string.h ***/
|
|
/*** begin file include/lcfg/lcfg_token.h ***/
|
|
/*
|
|
Copyright (c) 2012, Paul Baecher
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the <organization> nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#ifndef LCFG_TOKEN_H
|
|
#define LCFG_TOKEN_H
|
|
|
|
|
|
enum lcfg_token_type
|
|
{
|
|
lcfg_null_token = 0,
|
|
lcfg_identifier,
|
|
lcfg_equals,
|
|
lcfg_string,
|
|
lcfg_sbracket_open,
|
|
lcfg_sbracket_close,
|
|
lcfg_comma,
|
|
lcfg_brace_open,
|
|
lcfg_brace_close
|
|
};
|
|
|
|
extern const char *lcfg_token_map[];
|
|
|
|
struct lcfg_token
|
|
{
|
|
enum lcfg_token_type type;
|
|
struct lcfg_string *string;
|
|
short line;
|
|
short col;
|
|
};
|
|
|
|
|
|
#endif
|
|
/*** end file include/lcfg/lcfg_token.h ***/
|
|
/*** begin file include/lcfg/lcfg_scanner.h ***/
|
|
#/*
|
|
Copyright (c) 2012, Paul Baecher
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the <organization> nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#ifndef LCFG_SCANNER_H
|
|
#define LCFG_SCANNER_H
|
|
|
|
|
|
struct lcfg_scanner;
|
|
struct lcfg_token;
|
|
|
|
struct lcfg_scanner * lcfg_scanner_new(struct lcfg *, int fd);
|
|
enum lcfg_status lcfg_scanner_init(struct lcfg_scanner *);
|
|
enum lcfg_status lcfg_scanner_next_token(struct lcfg_scanner *, struct lcfg_token *);
|
|
int lcfg_scanner_has_next(struct lcfg_scanner *);
|
|
void lcfg_scanner_delete(struct lcfg_scanner *);
|
|
|
|
#endif
|
|
/*** end file include/lcfg/lcfg_scanner.h ***/
|
|
/*** begin file include/lcfg/lcfg_parser.h ***/
|
|
/*
|
|
Copyright (c) 2012, Paul Baecher
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the <organization> nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#ifndef LCFG_PARSER_H
|
|
#define LCFG_PARSER_H
|
|
|
|
|
|
struct lcfg_parser;
|
|
|
|
struct lcfg_parser * lcfg_parser_new(struct lcfg *, const char *);
|
|
enum lcfg_status lcfg_parser_run(struct lcfg_parser *);
|
|
enum lcfg_status lcfg_parser_accept(struct lcfg_parser *, lcfg_visitor_function, void *);
|
|
void lcfg_parser_delete(struct lcfg_parser *);
|
|
enum lcfg_status lcfg_parser_get(struct lcfg_parser *, const char *, void **, size_t *);
|
|
|
|
#endif
|
|
/*** end file include/lcfg/lcfg_parser.h ***/
|
|
/*** begin file include/lcfgx/lcfgx_tree.h ***/
|
|
/*
|
|
Copyright (c) 2012, Paul Baecher
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the <organization> nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#ifndef LCFGX_TREE_H
|
|
#define LCFGX_TREE_H
|
|
|
|
|
|
enum lcfgx_type
|
|
{
|
|
lcfgx_string,
|
|
lcfgx_list,
|
|
lcfgx_map,
|
|
};
|
|
|
|
struct lcfgx_tree_node
|
|
{
|
|
enum lcfgx_type type;
|
|
char *key; /* NULL for root node */
|
|
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
void *data;
|
|
size_t len;
|
|
} string;
|
|
struct lcfgx_tree_node *elements; /* in case of list or map type */
|
|
} value;
|
|
|
|
struct lcfgx_tree_node *next;
|
|
};
|
|
|
|
struct lcfgx_tree_node *lcfgx_tree_new(struct lcfg *);
|
|
|
|
void lcfgx_tree_delete(struct lcfgx_tree_node *);
|
|
void lcfgx_tree_dump(struct lcfgx_tree_node *node, int depth);
|
|
|
|
enum lcfgx_path_access
|
|
{
|
|
LCFGX_PATH_NOT_FOUND,
|
|
LCFGX_PATH_FOUND_WRONG_TYPE_BAD,
|
|
LCFGX_PATH_FOUND_TYPE_OK,
|
|
};
|
|
|
|
extern const char *lcfgx_path_access_strings[];
|
|
|
|
|
|
enum lcfgx_path_access lcfgx_get(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key, enum lcfgx_type type);
|
|
enum lcfgx_path_access lcfgx_get_list(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key);
|
|
enum lcfgx_path_access lcfgx_get_map(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key);
|
|
enum lcfgx_path_access lcfgx_get_string(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key);
|
|
|
|
|
|
#endif
|
|
|
|
/*** end file include/lcfgx/lcfgx_tree.h ***/
|
|
/*** begin file src/lcfg_string.c ***/
|
|
/*
|
|
Copyright (c) 2012, Paul Baecher
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the <organization> nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
struct lcfg_string
|
|
{
|
|
char *str;
|
|
unsigned int size;
|
|
unsigned int capacity;
|
|
};
|
|
|
|
int lcfg_string_set(struct lcfg_string *s, const char *cstr)
|
|
{
|
|
lcfg_string_trunc(s, 0);
|
|
return lcfg_string_cat_cstr(s, cstr);
|
|
}
|
|
|
|
|
|
/* make sure new_size bytes fit into the string */
|
|
inline static void lcfg_string_grow(struct lcfg_string *s, unsigned int new_size)
|
|
{
|
|
/* always allocate one byte more than needed
|
|
* to make _cstr() working in any case without realloc. */
|
|
while( (new_size + 1) > s->capacity )
|
|
{
|
|
s->capacity *= 2;
|
|
s->str = realloc(s->str, s->capacity);
|
|
}
|
|
}
|
|
|
|
struct lcfg_string *lcfg_string_new()
|
|
{
|
|
struct lcfg_string *s = malloc(sizeof(struct lcfg_string));
|
|
assert(s);
|
|
|
|
s->capacity = 8;
|
|
s->size = 0;
|
|
s->str = malloc(s->capacity);
|
|
|
|
assert(s->str);
|
|
|
|
return s;
|
|
}
|
|
|
|
struct lcfg_string *lcfg_string_new_copy(struct lcfg_string *s)
|
|
{
|
|
struct lcfg_string *s_new = malloc(sizeof(struct lcfg_string));
|
|
assert(s_new);
|
|
|
|
s_new->capacity = s->capacity;
|
|
s_new->size = s->size;
|
|
s_new->str = malloc(s_new->capacity);
|
|
|
|
memcpy(s_new->str, s->str, s_new->size);
|
|
|
|
return s_new;
|
|
}
|
|
|
|
int lcfg_string_cat_uint(struct lcfg_string *s, unsigned int i)
|
|
{
|
|
unsigned int size_needed = 1;
|
|
unsigned int ii = i;
|
|
char c;
|
|
|
|
while( ii >= 10 )
|
|
{
|
|
size_needed++;
|
|
ii /= 10;
|
|
}
|
|
|
|
lcfg_string_grow(s, s->size + size_needed);
|
|
|
|
ii = size_needed - 1;
|
|
do
|
|
{
|
|
c = '0' + i % 10;
|
|
s->str[s->size + ii--] = c;
|
|
i /= 10;
|
|
}
|
|
while( i != 0 );
|
|
|
|
s->size += size_needed;
|
|
|
|
return s->size;
|
|
}
|
|
|
|
int lcfg_string_find(struct lcfg_string *s, char c)
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < s->size; i++ )
|
|
{
|
|
if( s->str[i] == c )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int lcfg_string_rfind(struct lcfg_string *s, char c)
|
|
{
|
|
int i;
|
|
|
|
for( i = s->size - 1; i >= 0; i-- )
|
|
{
|
|
if( s->str[i] == c )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void lcfg_string_trunc(struct lcfg_string *s, unsigned int max_size)
|
|
{
|
|
if( max_size < s->size )
|
|
{
|
|
s->size = max_size;
|
|
}
|
|
}
|
|
|
|
int lcfg_string_cat_cstr(struct lcfg_string *s, const char *cstr)
|
|
{
|
|
size_t len = strlen(cstr);
|
|
|
|
lcfg_string_grow(s, s->size + len);
|
|
|
|
memcpy(s->str + s->size, cstr, len);
|
|
|
|
s->size += len;
|
|
|
|
return s->size;
|
|
}
|
|
|
|
int lcfg_string_cat_char(struct lcfg_string *s, char c)
|
|
{
|
|
lcfg_string_grow(s, s->size + 1);
|
|
|
|
s->str[s->size++] = c;
|
|
|
|
return s->size;
|
|
}
|
|
|
|
const char *lcfg_string_cstr(struct lcfg_string *s)
|
|
{
|
|
s->str[s->size] = '\0';
|
|
return s->str;
|
|
}
|
|
|
|
unsigned int lcfg_string_len(struct lcfg_string *s)
|
|
{
|
|
return s->size;
|
|
}
|
|
|
|
void lcfg_string_delete(struct lcfg_string *s)
|
|
{
|
|
free(s->str);
|
|
free(s);
|
|
}
|
|
/*** end file src/lcfg_string.c ***/
|
|
/*** begin file src/lcfg_token.c ***/
|
|
/*
|
|
Copyright (c) 2012, Paul Baecher
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the <organization> nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
const char *lcfg_token_map[] =
|
|
{
|
|
"null_token",
|
|
"T_IDENTIFIER",
|
|
"`='",
|
|
"T_STRING",
|
|
"`['",
|
|
"`]'",
|
|
"`,'",
|
|
"`{'",
|
|
"`}'"
|
|
};
|
|
/*** end file src/lcfg_token.c ***/
|
|
/*** begin file src/lcfg_scanner.c ***/
|
|
/*
|
|
Copyright (c) 2012, Paul Baecher
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the <organization> nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
#define BUFFER_SIZE 0xff
|
|
|
|
struct lcfg_scanner
|
|
{
|
|
struct lcfg *lcfg;
|
|
|
|
int fd;
|
|
char buffer[BUFFER_SIZE];
|
|
int offset;
|
|
int size;
|
|
int eof;
|
|
|
|
short line;
|
|
short col;
|
|
|
|
struct lcfg_token prepared_token;
|
|
int token_eof;
|
|
};
|
|
|
|
|
|
static enum lcfg_status lcfg_scanner_buffer_fill(struct lcfg_scanner *s)
|
|
{
|
|
if( (s->size = read(s->fd, s->buffer, BUFFER_SIZE)) < 0 )
|
|
{
|
|
lcfg_error_set(s->lcfg, "read(): %s", strerror(errno));
|
|
return lcfg_status_error;
|
|
}
|
|
else if( s->size == 0 )
|
|
{
|
|
s->eof = !0;
|
|
}
|
|
else
|
|
{
|
|
s->offset = 0;
|
|
}
|
|
|
|
return lcfg_status_ok;
|
|
}
|
|
|
|
static inline int lcfg_scanner_char_eof(struct lcfg_scanner *s)
|
|
{
|
|
if( s->eof )
|
|
{
|
|
return !0;
|
|
}
|
|
else
|
|
{
|
|
if( s->size == 0 || s->offset == BUFFER_SIZE )
|
|
{
|
|
lcfg_scanner_buffer_fill(s);
|
|
}
|
|
if( s->size < BUFFER_SIZE && s->offset == s->size )
|
|
{
|
|
s->eof = !0;
|
|
}
|
|
|
|
return s->eof;
|
|
}
|
|
}
|
|
|
|
static enum lcfg_status lcfg_scanner_char_read(struct lcfg_scanner *s, char *c)
|
|
{
|
|
if( lcfg_scanner_char_eof(s) )
|
|
{
|
|
lcfg_error_set(s->lcfg, "%s", "cannot read beyond eof");
|
|
return lcfg_status_error;
|
|
}
|
|
|
|
*c = s->buffer[s->offset++];
|
|
|
|
return lcfg_status_ok;
|
|
}
|
|
|
|
static enum lcfg_status lcfg_scanner_char_peek(struct lcfg_scanner *s, char *c)
|
|
{
|
|
if( lcfg_scanner_char_eof(s) )
|
|
{
|
|
lcfg_error_set(s->lcfg, "%s", "cannot peek beyond eof");
|
|
return lcfg_status_error;
|
|
}
|
|
|
|
*c = s->buffer[s->offset];
|
|
|
|
return lcfg_status_ok;
|
|
}
|
|
|
|
/* the beautiful lowlevel fsm */
|
|
static enum lcfg_status lcfg_scanner_token_read(struct lcfg_scanner *s)
|
|
{
|
|
enum scanner_state { start = 0, comm_start, in_oneline, in_multiline, multiline_end, in_identifier, in_str, in_esc, esc_hex_exp_first, esc_hex_exp_second, invalid };
|
|
enum scanner_state state = start;
|
|
char c = '\0';
|
|
char hex[3];
|
|
|
|
s->prepared_token.type = lcfg_null_token;
|
|
|
|
while( !lcfg_scanner_char_eof(s) )
|
|
{
|
|
int consume = !0;
|
|
lcfg_scanner_char_peek(s, &c);
|
|
|
|
switch( state )
|
|
{
|
|
case start:
|
|
switch( c )
|
|
{
|
|
case ' ':
|
|
case '\t':
|
|
case '\r':
|
|
case '\n':
|
|
break;
|
|
case '=':
|
|
s->prepared_token.type = lcfg_equals;
|
|
break;
|
|
case '[':
|
|
s->prepared_token.type = lcfg_sbracket_open;
|
|
break;
|
|
case ']':
|
|
s->prepared_token.type = lcfg_sbracket_close;
|
|
break;
|
|
case '{':
|
|
s->prepared_token.type = lcfg_brace_open;
|
|
break;
|
|
case '}':
|
|
s->prepared_token.type = lcfg_brace_close;
|
|
break;
|
|
case ',':
|
|
s->prepared_token.type = lcfg_comma;
|
|
break;
|
|
case '/':
|
|
state = comm_start;
|
|
break;
|
|
case '"':
|
|
state = in_str;
|
|
lcfg_string_trunc(s->prepared_token.string, 0);
|
|
break;
|
|
default:
|
|
if( isalpha(c) )
|
|
{
|
|
lcfg_string_trunc(s->prepared_token.string, 0);
|
|
lcfg_string_cat_char(s->prepared_token.string, c);
|
|
state = in_identifier;
|
|
}
|
|
else
|
|
{
|
|
lcfg_error_set(s->lcfg, "parse error: invalid input character `%c' (0x%02x) near line %d, col %d", isprint(c) ? c : '.', c, s->line, s->col);
|
|
state = invalid;
|
|
}
|
|
}
|
|
break;
|
|
case comm_start:
|
|
if( c == '/' )
|
|
{
|
|
state = in_oneline;
|
|
}
|
|
else if( c == '*' )
|
|
{
|
|
state = in_multiline;
|
|
}
|
|
else
|
|
{
|
|
lcfg_error_set(s->lcfg, "parse error: invalid input character `%c' (0x%02x) near line %d, col %d", isprint(c) ? c : '.', c, s->line, s->col);
|
|
state = invalid;
|
|
}
|
|
break;
|
|
case in_oneline:
|
|
if( c == '\n' )
|
|
{
|
|
state = start;
|
|
}
|
|
break;
|
|
case in_multiline:
|
|
if( c == '*' )
|
|
{
|
|
state = multiline_end;
|
|
}
|
|
break;
|
|
case multiline_end:
|
|
if( c == '/' )
|
|
{
|
|
state = start;
|
|
}
|
|
else if( c != '*' )
|
|
{
|
|
state = in_multiline;
|
|
}
|
|
break;
|
|
case in_identifier:
|
|
if( isalnum(c) || c == '-' || c == '_' )
|
|
{
|
|
lcfg_string_cat_char(s->prepared_token.string, c);
|
|
}
|
|
else
|
|
{
|
|
s->prepared_token.type = lcfg_identifier;
|
|
consume = 0;
|
|
state = start;
|
|
}
|
|
break;
|
|
case in_str:
|
|
if( c == '"' )
|
|
{
|
|
s->prepared_token.type = lcfg_string;
|
|
state = start;
|
|
}
|
|
else if( c == '\\' )
|
|
{
|
|
state = in_esc;
|
|
}
|
|
else
|
|
{
|
|
lcfg_string_cat_char(s->prepared_token.string, c);
|
|
}
|
|
break;
|
|
case in_esc:
|
|
state = in_str;
|
|
switch( c )
|
|
{
|
|
case '"':
|
|
lcfg_string_cat_char(s->prepared_token.string, '"');
|
|
break;
|
|
case 'n':
|
|
lcfg_string_cat_char(s->prepared_token.string, '\n');
|
|
break;
|
|
case 't':
|
|
lcfg_string_cat_char(s->prepared_token.string, '\t');
|
|
break;
|
|
case 'r':
|
|
lcfg_string_cat_char(s->prepared_token.string, '\r');
|
|
break;
|
|
case '0':
|
|
lcfg_string_cat_char(s->prepared_token.string, '\0');
|
|
break;
|
|
case '\\':
|
|
lcfg_string_cat_char(s->prepared_token.string, '\\');
|
|
break;
|
|
case 'x':
|
|
state = esc_hex_exp_first;
|
|
break;
|
|
default:
|
|
lcfg_error_set(s->lcfg, "invalid string escape sequence `%c' near line %d, col %d", c, s->line, s->col);
|
|
state = invalid;
|
|
}
|
|
break;
|
|
case esc_hex_exp_first:
|
|
if( !isxdigit(c) )
|
|
{
|
|
lcfg_error_set(s->lcfg, "invalid hex escape sequence `%c' on line %d column %d", c, s->line, s->col);
|
|
state = invalid;
|
|
}
|
|
hex[0] = c;
|
|
state = esc_hex_exp_second;
|
|
break;
|
|
case esc_hex_exp_second:
|
|
if( !isxdigit(c) )
|
|
{
|
|
lcfg_error_set(s->lcfg, "invalid hex escape sequence `%c' on line %d column %d", c, s->line, s->col);
|
|
state = invalid;
|
|
}
|
|
hex[1] = c;
|
|
hex[2] = '\0';
|
|
lcfg_string_cat_char(s->prepared_token.string, strtoul(hex, NULL, 16));
|
|
state = in_str;
|
|
break;
|
|
case invalid:
|
|
break;
|
|
}
|
|
/*#include <stdio.h>
|
|
printf("read %c at line %d column %d, new state is %d\n", isprint(c) ? c : '.', s->line, s->col, state);*/
|
|
|
|
/* this is technically not optimal (token position identified by last char), but it will suffice for now */
|
|
s->prepared_token.line = s->line;
|
|
s->prepared_token.col = s->col;
|
|
|
|
if( consume )
|
|
{
|
|
lcfg_scanner_char_read(s, &c);
|
|
if( c == '\n' )
|
|
{
|
|
s->line++;
|
|
s->col = 1;
|
|
}
|
|
else
|
|
{
|
|
s->col++;
|
|
}
|
|
}
|
|
|
|
if( s->prepared_token.type != lcfg_null_token || state == invalid )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( state != start )
|
|
{
|
|
if( state != invalid )
|
|
{
|
|
lcfg_error_set(s->lcfg, "parse error: premature end of file near line %d, col %d", s->line, s->col);
|
|
}
|
|
|
|
return lcfg_status_error;
|
|
}
|
|
|
|
return lcfg_status_ok;
|
|
}
|
|
|
|
enum lcfg_status lcfg_scanner_init(struct lcfg_scanner *s)
|
|
{
|
|
/* prepare the first token */
|
|
return lcfg_scanner_token_read(s);
|
|
}
|
|
|
|
int lcfg_scanner_has_next(struct lcfg_scanner *s)
|
|
{
|
|
return s->prepared_token.type != lcfg_null_token;
|
|
}
|
|
|
|
enum lcfg_status lcfg_scanner_next_token(struct lcfg_scanner *s, struct lcfg_token *t)
|
|
{
|
|
if( !lcfg_scanner_has_next(s) )
|
|
{
|
|
lcfg_error_set(s->lcfg, "%s", "cannot access tokenstream beyond eof");
|
|
return lcfg_status_error;
|
|
}
|
|
|
|
memcpy(t, &s->prepared_token, sizeof(struct lcfg_token));
|
|
t->string = lcfg_string_new_copy(s->prepared_token.string);
|
|
|
|
/* prepare the next token */
|
|
return lcfg_scanner_token_read(s);
|
|
}
|
|
|
|
struct lcfg_scanner *lcfg_scanner_new(struct lcfg *c, int fd)
|
|
{
|
|
struct lcfg_scanner *s = malloc(sizeof(struct lcfg_scanner));
|
|
assert(s);
|
|
|
|
memset(s, 0, sizeof(struct lcfg_scanner));
|
|
|
|
s->lcfg = c;
|
|
s->fd = fd;
|
|
|
|
s->line = s->col = 1;
|
|
|
|
s->prepared_token.string = lcfg_string_new();
|
|
|
|
return s;
|
|
}
|
|
|
|
void lcfg_scanner_delete(struct lcfg_scanner *s)
|
|
{
|
|
lcfg_string_delete(s->prepared_token.string);
|
|
free(s);
|
|
}
|
|
|
|
/*** end file src/lcfg_scanner.c ***/
|
|
/*** begin file src/lcfg_parser.c ***/
|
|
/*
|
|
Copyright (c) 2012, Paul Baecher
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the <organization> nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
#ifndef strdup
|
|
char *strdup(const char *s)
|
|
{
|
|
char *sdup;
|
|
|
|
size_t len = strlen(s) + 1;
|
|
sdup = malloc(len);
|
|
|
|
if( sdup != NULL )
|
|
{
|
|
memcpy(sdup, s, len);
|
|
}
|
|
|
|
return sdup;
|
|
}
|
|
#endif
|
|
|
|
struct lcfg_parser_value_pair
|
|
{
|
|
char *key;
|
|
struct lcfg_string *value;
|
|
};
|
|
|
|
|
|
struct lcfg_parser
|
|
{
|
|
struct lcfg *lcfg;
|
|
char *filename;
|
|
struct lcfg_scanner *scanner;
|
|
|
|
struct lcfg_parser_value_pair *values;
|
|
unsigned int value_length;
|
|
unsigned int value_capacity;
|
|
};
|
|
|
|
static int lcfg_parser_add_value(struct lcfg_parser *p, const char *key, struct lcfg_string *value)
|
|
{
|
|
if( p->value_length == p->value_capacity )
|
|
{
|
|
p->value_capacity *= 2;
|
|
p->values = realloc(p->values, sizeof(struct lcfg_parser_value_pair) * p->value_capacity);
|
|
assert(p->values);
|
|
}
|
|
|
|
p->values[p->value_length].key = strdup(key);
|
|
p->values[p->value_length].value = lcfg_string_new_copy(value);
|
|
|
|
return ++p->value_length;
|
|
}
|
|
|
|
struct lcfg_parser *lcfg_parser_new(struct lcfg *c, const char *filename)
|
|
{
|
|
struct lcfg_parser *p = malloc(sizeof(struct lcfg_parser));
|
|
assert(p);
|
|
|
|
memset(p, 0, sizeof(struct lcfg_parser));
|
|
|
|
p->filename = strdup(filename);
|
|
p->lcfg = c;
|
|
|
|
p->value_length = 0;
|
|
p->value_capacity = 8;
|
|
p->values = malloc(sizeof(struct lcfg_parser_value_pair) * p->value_capacity);
|
|
assert(p->values);
|
|
|
|
return p;
|
|
}
|
|
|
|
/* this is a basic push down automata */
|
|
static enum lcfg_status lcfg_parser_parse(struct lcfg_parser *p)
|
|
{
|
|
enum state { top_level = 0, exp_equals, exp_value, in_list, in_map, invalid };
|
|
/*const char *state_map[] = { "top_level", "exp_equals", "exp_value", "in_list", "in_map", "invalid" };*/
|
|
|
|
struct state_element
|
|
{
|
|
enum state s;
|
|
int list_counter;
|
|
};
|
|
|
|
/* start of ugly preproc stuff */
|
|
#define STATE_STACK_PUSH(t) \
|
|
if( ssi + 1 == state_stack_size ) \
|
|
{ \
|
|
state_stack_size *= 2; \
|
|
state_stack = realloc(state_stack, state_stack_size * sizeof(struct state_element)); \
|
|
} \
|
|
state_stack[++ssi].s = t; \
|
|
state_stack[ssi].list_counter = 0
|
|
#define STATE_STACK_POP() ssi--
|
|
#define PATH_PUSH_STR(s) \
|
|
if( lcfg_string_len(current_path) != 0 ) \
|
|
{ \
|
|
lcfg_string_cat_char(current_path, '.'); \
|
|
} \
|
|
lcfg_string_cat_cstr(current_path, s);
|
|
#define PATH_PUSH_INT(i) \
|
|
if( lcfg_string_len(current_path) != 0 ) \
|
|
{ \
|
|
lcfg_string_cat_char(current_path, '.'); \
|
|
} \
|
|
lcfg_string_cat_uint(current_path, i);
|
|
#define PATH_POP() \
|
|
if( lcfg_string_rfind(current_path, '.') != -1 ) \
|
|
{ \
|
|
lcfg_string_trunc(current_path, lcfg_string_rfind(current_path, '.')); \
|
|
} \
|
|
else \
|
|
{ \
|
|
lcfg_string_trunc(current_path, 0); \
|
|
}
|
|
/* end of ugly preproc stuff */
|
|
|
|
if( lcfg_scanner_init(p->scanner) != lcfg_status_ok )
|
|
{
|
|
return lcfg_status_error;
|
|
}
|
|
|
|
int state_stack_size = 8;
|
|
int ssi = 0; /* ssi = state stack index */
|
|
struct state_element *state_stack = malloc(sizeof(struct state_element) * state_stack_size);
|
|
|
|
state_stack[ssi].s = top_level;
|
|
state_stack[ssi].list_counter = 0;
|
|
|
|
struct lcfg_token t;
|
|
struct lcfg_string *current_path = lcfg_string_new();
|
|
|
|
while( lcfg_scanner_has_next(p->scanner) && state_stack[ssi].s != invalid )
|
|
{
|
|
if( lcfg_scanner_next_token(p->scanner, &t) != lcfg_status_ok )
|
|
{
|
|
free(state_stack);
|
|
lcfg_string_delete(t.string);
|
|
lcfg_string_delete(current_path);
|
|
return lcfg_status_error;
|
|
}
|
|
|
|
switch( state_stack[ssi].s )
|
|
{
|
|
case top_level:
|
|
case in_map:
|
|
if( t.type == lcfg_identifier )
|
|
{
|
|
PATH_PUSH_STR(lcfg_string_cstr(t.string));
|
|
STATE_STACK_PUSH(exp_equals);
|
|
}
|
|
else if( state_stack[ssi].s == in_map && t.type == lcfg_brace_close )
|
|
{
|
|
STATE_STACK_POP();
|
|
PATH_POP();
|
|
}
|
|
else
|
|
{
|
|
lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected identifier%s", lcfg_token_map[t.type], t.line, t.col, state_stack[ssi].s == in_map ? " or `}'" : "");
|
|
state_stack[ssi].s = invalid;
|
|
}
|
|
break;
|
|
case exp_equals:
|
|
if( t.type == lcfg_equals )
|
|
state_stack[ssi].s = exp_value;
|
|
else
|
|
{
|
|
lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected `='", lcfg_token_map[t.type], t.line, t.col);
|
|
state_stack[ssi].s = invalid;
|
|
}
|
|
break;
|
|
case exp_value:
|
|
if( t.type == lcfg_string )
|
|
{
|
|
lcfg_parser_add_value(p, lcfg_string_cstr(current_path), t.string);
|
|
/*printf("adding string value for single statement\n");*/
|
|
STATE_STACK_POP();
|
|
PATH_POP();
|
|
}
|
|
else if( t.type == lcfg_sbracket_open )
|
|
{
|
|
state_stack[ssi].s = in_list;
|
|
}
|
|
else if( t.type == lcfg_brace_open )
|
|
{
|
|
state_stack[ssi].s = in_map;
|
|
}
|
|
else
|
|
{
|
|
lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected string, `[' or `{'", lcfg_token_map[t.type], t.line, t.col);
|
|
state_stack[ssi].s = invalid;
|
|
}
|
|
break;
|
|
case in_list:
|
|
if( t.type == lcfg_comma ); /* ignore comma */
|
|
else if( t.type == lcfg_string )
|
|
{
|
|
PATH_PUSH_INT(state_stack[ssi].list_counter);
|
|
lcfg_parser_add_value(p, lcfg_string_cstr(current_path), t.string);
|
|
PATH_POP();
|
|
/*printf("adding string to list pos %d\n", state_stack[ssi].list_counter);*/
|
|
state_stack[ssi].list_counter++;
|
|
}
|
|
else if( t.type == lcfg_sbracket_open )
|
|
{
|
|
PATH_PUSH_INT(state_stack[ssi].list_counter);
|
|
/*printf("adding list to list pos %d\n", state_stack[ssi].list_counter);*/
|
|
state_stack[ssi].list_counter++;
|
|
STATE_STACK_PUSH(in_list);
|
|
}
|
|
else if( t.type == lcfg_brace_open )
|
|
{
|
|
PATH_PUSH_INT(state_stack[ssi].list_counter);
|
|
/*printf("adding map to list pos %d\n", state_stack[ssi].list_counter);*/
|
|
state_stack[ssi].list_counter++;
|
|
STATE_STACK_PUSH(in_map);
|
|
}
|
|
else if( t.type == lcfg_sbracket_close )
|
|
{
|
|
PATH_POP();
|
|
STATE_STACK_POP();
|
|
}
|
|
else
|
|
{
|
|
lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected string, `[', `{', `,' or `]'", lcfg_token_map[t.type], t.line, t.col);
|
|
state_stack[ssi].s = invalid;
|
|
}
|
|
break;
|
|
case invalid: /* unreachable */
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
lcfg_string_delete(t.string);
|
|
|
|
/*printf(" *** pda: read %s, state is now %s\n", lcfg_token_map[t.type], state_map[state_stack[ssi].s]);*/
|
|
}
|
|
|
|
lcfg_string_delete(current_path);
|
|
|
|
if( state_stack[ssi].s == top_level && ssi == 0 )
|
|
{
|
|
free(state_stack);
|
|
return lcfg_status_ok;
|
|
}
|
|
else
|
|
{
|
|
free(state_stack);
|
|
lcfg_error_set(p->lcfg, "%s", "unexpected end of file: unterminated list/map?");
|
|
return lcfg_status_error;
|
|
}
|
|
}
|
|
|
|
enum lcfg_status lcfg_parser_run(struct lcfg_parser *p)
|
|
{
|
|
int fd = open(p->filename, 0);
|
|
enum lcfg_status status;
|
|
|
|
if( fd < 0 )
|
|
{
|
|
lcfg_error_set(p->lcfg, "open(): %s", strerror(errno));
|
|
return lcfg_status_error;
|
|
}
|
|
|
|
p->scanner = lcfg_scanner_new(p->lcfg, fd);
|
|
|
|
status = lcfg_parser_parse(p);
|
|
|
|
close(fd);
|
|
|
|
return status;
|
|
}
|
|
enum lcfg_status lcfg_parser_accept(struct lcfg_parser *p, lcfg_visitor_function fn, void *user_data)
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < p->value_length; i++ )
|
|
{
|
|
if( fn(p->values[i].key, (void *)lcfg_string_cstr(p->values[i].value), lcfg_string_len(p->values[i].value), user_data) != lcfg_status_ok )
|
|
{
|
|
lcfg_error_set(p->lcfg, "%s", "configuration value traversal aborted upon user request");
|
|
return lcfg_status_error;
|
|
}
|
|
}
|
|
|
|
return lcfg_status_ok;
|
|
}
|
|
|
|
enum lcfg_status lcfg_parser_get(struct lcfg_parser *p, const char *key, void **data, size_t *len)
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < p->value_length; i++ )
|
|
{
|
|
if( !strcmp(p->values[i].key, key) )
|
|
{
|
|
*data = (void *)lcfg_string_cstr(p->values[i].value);
|
|
*len = lcfg_string_len(p->values[i].value);
|
|
return lcfg_status_ok;
|
|
}
|
|
}
|
|
|
|
return lcfg_status_error;
|
|
}
|
|
|
|
|
|
void lcfg_parser_delete(struct lcfg_parser *p)
|
|
{
|
|
if( p->scanner != NULL )
|
|
{
|
|
lcfg_scanner_delete(p->scanner);
|
|
}
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < p->value_length; i++ )
|
|
{
|
|
free(p->values[i].key);
|
|
lcfg_string_delete(p->values[i].value);
|
|
}
|
|
free(p->values);
|
|
free(p->filename);
|
|
free(p);
|
|
}
|
|
/*** end file src/lcfg_parser.c ***/
|
|
/*** begin file src/lcfg.c ***/
|
|
/*
|
|
Copyright (c) 2012, Paul Baecher
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the <organization> nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
struct lcfg
|
|
{
|
|
char error[0xff];
|
|
struct lcfg_parser *parser;
|
|
};
|
|
|
|
struct lcfg *lcfg_new(const char *filename)
|
|
{
|
|
struct lcfg *c = malloc(sizeof(struct lcfg));
|
|
assert(c);
|
|
memset(c, 0, sizeof(struct lcfg));
|
|
|
|
c->parser = lcfg_parser_new(c, filename);
|
|
assert(c->parser);
|
|
|
|
return c;
|
|
}
|
|
|
|
void lcfg_delete(struct lcfg *c)
|
|
{
|
|
lcfg_parser_delete(c->parser);
|
|
free(c);
|
|
}
|
|
|
|
const char *lcfg_error_get(struct lcfg *c)
|
|
{
|
|
return c->error;
|
|
}
|
|
|
|
enum lcfg_status lcfg_parse(struct lcfg *c)
|
|
{
|
|
return lcfg_parser_run(c->parser);
|
|
}
|
|
|
|
enum lcfg_status lcfg_accept(struct lcfg *c, lcfg_visitor_function fn, void *user_data)
|
|
{
|
|
return lcfg_parser_accept(c->parser, fn, user_data);
|
|
}
|
|
|
|
enum lcfg_status lcfg_value_get(struct lcfg *c, const char *key, void **data, size_t *len)
|
|
{
|
|
return lcfg_parser_get(c->parser, key, data, len);
|
|
}
|
|
|
|
void lcfg_error_set(struct lcfg *c, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vsnprintf(c->error, sizeof(c->error), fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/*** end file src/lcfg.c ***/
|
|
/*** begin file src/lcfgx_tree.c ***/
|
|
/*
|
|
Copyright (c) 2012, Paul Baecher
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the <organization> nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL <THE COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
static struct lcfgx_tree_node *lcfgx_tree_node_new(enum lcfgx_type type, const char *key)
|
|
{
|
|
struct lcfgx_tree_node *node = malloc(sizeof(struct lcfgx_tree_node));
|
|
|
|
node->type = type;
|
|
|
|
if( key != NULL )
|
|
node->key = strdup(key);
|
|
else
|
|
node->key = NULL;
|
|
|
|
node->next = NULL;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
void lcfgx_tree_dump(struct lcfgx_tree_node *node, int depth)
|
|
{
|
|
// printf("%s node %p node->key %s depth %i\n", __PRETTY_FUNCTION__, node, node->key, depth);
|
|
void sp(int n)
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < n; i++ )
|
|
printf("%c", ' ');
|
|
}
|
|
|
|
sp(depth);
|
|
if( node->key != NULL )
|
|
printf("%s", node->key);
|
|
else
|
|
printf("%s", "(none)");
|
|
|
|
struct lcfgx_tree_node *n;
|
|
|
|
switch( node->type )
|
|
{
|
|
case lcfgx_string:
|
|
printf(" = \"%s\"\n", (char *)node->value.string.data);
|
|
break;
|
|
|
|
case lcfgx_list:
|
|
case lcfgx_map:
|
|
printf("%c", '\n');
|
|
n = node->value.elements;
|
|
for( ; n != NULL; n = n->next )
|
|
lcfgx_tree_dump(n, depth + 2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lcfgx_tree_insert(int pathc, char **pathv, void *data, size_t len, struct lcfgx_tree_node *node)
|
|
{
|
|
struct lcfgx_tree_node *n;
|
|
|
|
for( n = node->value.elements; n != NULL; n = n->next )
|
|
if( !strcmp(pathv[0], n->key) )
|
|
break;
|
|
|
|
if( pathc == 1 )
|
|
{
|
|
/* leaf node --> string value */
|
|
if( n == NULL )
|
|
{
|
|
/* not found, insert */
|
|
n = lcfgx_tree_node_new(lcfgx_string, pathv[0]);
|
|
n->value.string.len = len;
|
|
n->value.string.data = malloc(len+1);
|
|
memset(n->value.string.data, 0, len+1);
|
|
memcpy(n->value.string.data, data, len);
|
|
n->next = NULL;
|
|
if ( node->value.elements != NULL )
|
|
{
|
|
struct lcfgx_tree_node *end = node->value.elements;
|
|
while (end->next != NULL)
|
|
end = end->next;
|
|
end->next = n;
|
|
}else
|
|
{
|
|
node->value.elements = n;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* inner node --> (map/list) */
|
|
if( n == NULL )
|
|
{
|
|
/* not found, insert it */
|
|
n = lcfgx_tree_node_new(lcfgx_map, pathv[0]);
|
|
n->value.elements = NULL;
|
|
n->next = NULL;
|
|
if ( node->value.elements != NULL )
|
|
{
|
|
struct lcfgx_tree_node *end = node->value.elements;
|
|
while (end->next != NULL)
|
|
end = end->next;
|
|
end->next = n;
|
|
}else
|
|
{
|
|
node->value.elements = n;
|
|
}
|
|
}
|
|
|
|
/* recurse into map/list */
|
|
lcfgx_tree_insert(pathc - 1, &pathv[1], data, len, n);
|
|
}
|
|
}
|
|
|
|
enum lcfg_status lcfgx_tree_visitor(const char *key, void *data, size_t len, void *user_data)
|
|
{
|
|
struct lcfgx_tree_node *root = user_data;
|
|
char path[strlen(key) + 1];
|
|
int path_components = 1;
|
|
|
|
strncpy(path, key, strlen(key) + 1);
|
|
|
|
while( *key != 0 )
|
|
if( *key++ == '.' )
|
|
path_components++;
|
|
|
|
char *pathv[path_components];
|
|
char *token;
|
|
char *saveptr = NULL;
|
|
int pathc = 0;
|
|
|
|
while( (token = strtok_r(pathc == 0 ? path : NULL, ".", &saveptr)) != NULL )
|
|
pathv[pathc++] = token;
|
|
|
|
lcfgx_tree_insert(pathc, pathv, data, len, root);
|
|
|
|
return lcfg_status_ok;
|
|
}
|
|
|
|
|
|
|
|
void lcfgx_correct_type(struct lcfgx_tree_node *node)
|
|
{
|
|
if( node->type == lcfgx_string )
|
|
return;
|
|
|
|
struct lcfgx_tree_node *n = NULL;
|
|
if( node->key == NULL ) /* root node */
|
|
n = node;
|
|
|
|
if( node->type == lcfgx_map || node->type == lcfgx_list )
|
|
n = node->value.elements;
|
|
|
|
if( n == NULL )
|
|
return;
|
|
|
|
/* child key is integer, we have a list */
|
|
char *end;
|
|
if( strtol(n->key, &end, 10) >= 0 && n->key != end )
|
|
node->type = lcfgx_list;
|
|
|
|
struct lcfgx_tree_node *it;
|
|
for( it = n; it != NULL; it = it->next )
|
|
lcfgx_correct_type(it);
|
|
}
|
|
|
|
struct lcfgx_tree_node *lcfgx_tree_new(struct lcfg *c)
|
|
{
|
|
struct lcfgx_tree_node *root = lcfgx_tree_node_new(lcfgx_map, NULL);
|
|
|
|
root->value.elements = NULL;
|
|
|
|
lcfg_accept(c, lcfgx_tree_visitor, root);
|
|
lcfgx_correct_type(root);
|
|
return root;
|
|
}
|
|
|
|
void lcfgx_tree_delete(struct lcfgx_tree_node *n)
|
|
{
|
|
|
|
if( n->type != lcfgx_string )
|
|
{
|
|
struct lcfgx_tree_node *m, *next;
|
|
|
|
for( m = n->value.elements; m != NULL; )
|
|
{
|
|
next = m->next;
|
|
lcfgx_tree_delete(m);
|
|
m = next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
free(n->value.string.data);
|
|
}
|
|
|
|
if( n->key != NULL )
|
|
free(n->key);
|
|
|
|
free(n);
|
|
}
|
|
|
|
const char *lcfgx_path_access_strings[] =
|
|
{
|
|
"LCFGX_PATH_NOT_FOUND",
|
|
"LCFGX_PATH_FOUND_WRONG_TYPE_BAD",
|
|
"LCFGX_PATH_FOUND_TYPE_OK",
|
|
};
|
|
|
|
struct lcfgx_tree_node *cfg_get_recursive(struct lcfgx_tree_node *node, int pathc, char **pathv)
|
|
{
|
|
struct lcfgx_tree_node *it;// = node;
|
|
|
|
for( it = node->value.elements; it != NULL; it = it->next )
|
|
{
|
|
if( strcmp(pathv[0], it->key) == 0 )
|
|
break;
|
|
}
|
|
|
|
if( it != NULL )
|
|
{
|
|
if( pathc == 1 )
|
|
return it;
|
|
else
|
|
return cfg_get_recursive(it, pathc - 1, &pathv[1]);
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
enum lcfgx_path_access lcfgx_get(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key, enum lcfgx_type type)
|
|
{
|
|
char path[strlen(key) + 1];
|
|
int path_components = 1;
|
|
|
|
strncpy(path, key, strlen(key) + 1);
|
|
|
|
while( *key != 0 )
|
|
if( *key++ == '.' )
|
|
path_components++;
|
|
|
|
char *pathv[path_components];
|
|
char *token;
|
|
char *saveptr = NULL;
|
|
int pathc = 0;
|
|
|
|
while( (token = strtok_r(pathc == 0 ? path : NULL, ".", &saveptr)) != NULL )
|
|
pathv[pathc++] = token;
|
|
|
|
|
|
struct lcfgx_tree_node *node;
|
|
|
|
if( pathc == 0 )
|
|
node = root;
|
|
else
|
|
node = cfg_get_recursive(root, pathc, pathv);
|
|
|
|
if( node == NULL )
|
|
return LCFGX_PATH_NOT_FOUND;
|
|
|
|
if( node->type != type )
|
|
return LCFGX_PATH_FOUND_WRONG_TYPE_BAD;
|
|
|
|
*n = node;
|
|
|
|
return LCFGX_PATH_FOUND_TYPE_OK;
|
|
}
|
|
|
|
enum lcfgx_path_access lcfgx_get_list(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key)
|
|
{
|
|
return lcfgx_get(root, n, key, lcfgx_list);
|
|
}
|
|
|
|
enum lcfgx_path_access lcfgx_get_map(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key)
|
|
{
|
|
return lcfgx_get(root, n, key, lcfgx_map);
|
|
}
|
|
|
|
enum lcfgx_path_access lcfgx_get_string(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key)
|
|
{
|
|
return lcfgx_get(root, n, key, lcfgx_string);
|
|
}
|
|
|
|
/*** end file src/lcfgx_tree.c ***/
|