/* 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 ***/
