2001-08-21 12:56:08 +00:00
/*
2007-07-08 22:51:20 +00:00
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
2004-03-30 19:35:44 +00:00
* Copyright ( C ) 2004 Red Hat , Inc . All rights reserved .
2001-08-21 12:56:08 +00:00
*
2004-03-30 19:35:44 +00:00
* This file is part of LVM2 .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
* of the GNU General Public License v .2 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2001-08-21 12:56:08 +00:00
*/
2002-11-18 14:01:16 +00:00
# include "lib.h"
# include "config.h"
# include "crc.h"
# include "device.h"
2004-05-04 18:28:15 +00:00
# include "str_list.h"
# include "toolcontext.h"
2007-04-25 20:38:39 +00:00
# include "lvm-string.h"
2007-07-24 17:48:08 +00:00
# include "lvm-file.h"
2002-11-18 14:01:16 +00:00
2001-08-21 12:56:08 +00:00
# include <sys/stat.h>
# include <sys/mman.h>
# include <unistd.h>
# include <fcntl.h>
# include <ctype.h>
2001-09-13 12:38:31 +00:00
2007-04-25 20:38:39 +00:00
# define SECTION_B_CHAR '{'
# define SECTION_E_CHAR '}'
2001-08-21 12:56:08 +00:00
enum {
2002-04-24 18:20:51 +00:00
TOK_INT ,
TOK_FLOAT ,
TOK_STRING ,
TOK_EQ ,
TOK_SECTION_B ,
TOK_SECTION_E ,
TOK_ARRAY_B ,
TOK_ARRAY_E ,
TOK_IDENTIFIER ,
2001-08-21 12:56:08 +00:00
TOK_COMMA ,
TOK_EOF
} ;
struct parser {
2006-05-16 16:48:31 +00:00
const char * fb , * fe ; /* file limits */
2001-08-21 12:56:08 +00:00
2002-04-24 18:20:51 +00:00
int t ; /* token limits and type */
2006-05-16 16:48:31 +00:00
const char * tb , * te ;
2001-08-21 12:56:08 +00:00
2002-04-24 18:20:51 +00:00
int fd ; /* descriptor for file being parsed */
2001-08-21 12:56:08 +00:00
int line ; /* line number we are on */
2005-10-16 23:03:59 +00:00
struct dm_pool * mem ;
2001-08-21 12:56:08 +00:00
} ;
struct cs {
2004-03-08 18:28:45 +00:00
struct config_tree cft ;
2005-10-16 23:03:59 +00:00
struct dm_pool * mem ;
2002-11-18 14:01:16 +00:00
time_t timestamp ;
char * filename ;
2004-05-04 18:28:15 +00:00
int exists ;
2006-11-04 03:34:10 +00:00
int keep_open ;
struct device * dev ;
2001-08-21 12:56:08 +00:00
} ;
2007-07-08 22:51:20 +00:00
struct output_line {
FILE * fp ;
struct dm_pool * mem ;
} ;
2003-02-03 20:08:45 +00:00
static void _get_token ( struct parser * p , int tok_prev ) ;
2001-08-21 12:56:08 +00:00
static void _eat_space ( struct parser * p ) ;
static struct config_node * _file ( struct parser * p ) ;
static struct config_node * _section ( struct parser * p ) ;
static struct config_value * _value ( struct parser * p ) ;
static struct config_value * _type ( struct parser * p ) ;
static int _match_aux ( struct parser * p , int t ) ;
static struct config_value * _create_value ( struct parser * p ) ;
static struct config_node * _create_node ( struct parser * p ) ;
static char * _dup_tok ( struct parser * p ) ;
2004-03-08 18:28:45 +00:00
static const int sep = ' / ' ;
2001-08-21 12:56:08 +00:00
# define MAX_INDENT 32
# define match(t) do {\
if ( ! _match_aux ( p , ( t ) ) ) { \
2007-04-26 16:40:46 +00:00
log_error ( " Parse error at byte % " PRIptrdiff_t " (line %d): unexpected token " , \
p - > tb - p - > fb + 1 , p - > line ) ; \
2001-08-21 12:56:08 +00:00
return 0 ; \
} \
} while ( 0 ) ;
2002-01-07 10:23:52 +00:00
static int _tok_match ( const char * str , const char * b , const char * e )
{
while ( * str & & ( b ! = e ) ) {
if ( * str + + ! = * b + + )
return 0 ;
}
return ! ( * str | | ( b ! = e ) ) ;
}
2001-08-21 12:56:08 +00:00
/*
* public interface
*/
2006-11-04 03:34:10 +00:00
struct config_tree * create_config_tree ( const char * filename , int keep_open )
2001-08-21 12:56:08 +00:00
{
2002-04-24 18:20:51 +00:00
struct cs * c ;
2005-10-16 23:03:59 +00:00
struct dm_pool * mem = dm_pool_create ( " config " , 10 * 1024 ) ;
2002-04-24 18:20:51 +00:00
if ( ! mem ) {
2006-05-16 16:48:31 +00:00
log_error ( " Failed to allocate config pool. " ) ;
2002-04-24 18:20:51 +00:00
return 0 ;
}
2005-10-16 23:03:59 +00:00
if ( ! ( c = dm_pool_zalloc ( mem , sizeof ( * c ) ) ) ) {
2006-05-16 16:48:31 +00:00
log_error ( " Failed to allocate config tree. " ) ;
2005-10-16 23:03:59 +00:00
dm_pool_destroy ( mem ) ;
2002-04-24 18:20:51 +00:00
return 0 ;
}
c - > mem = mem ;
2004-03-08 18:28:45 +00:00
c - > cft . root = ( struct config_node * ) NULL ;
2002-11-18 14:01:16 +00:00
c - > timestamp = 0 ;
2004-05-04 18:28:15 +00:00
c - > exists = 0 ;
2006-11-04 03:34:10 +00:00
c - > keep_open = keep_open ;
c - > dev = 0 ;
2004-05-04 18:28:15 +00:00
if ( filename )
2005-10-16 23:03:59 +00:00
c - > filename = dm_pool_strdup ( c - > mem , filename ) ;
2004-03-08 18:28:45 +00:00
return & c - > cft ;
2001-08-21 12:56:08 +00:00
}
2004-03-08 18:28:45 +00:00
void destroy_config_tree ( struct config_tree * cft )
2001-08-21 12:56:08 +00:00
{
2006-11-04 03:34:10 +00:00
struct cs * c = ( struct cs * ) cft ;
if ( c - > dev )
dev_close ( c - > dev ) ;
dm_pool_destroy ( c - > mem ) ;
2001-08-21 12:56:08 +00:00
}
2006-05-16 16:48:31 +00:00
static int _parse_config_file ( struct parser * p , struct config_tree * cft )
{
p - > tb = p - > te = p - > fb ;
p - > line = 1 ;
_get_token ( p , TOK_SECTION_E ) ;
if ( ! ( cft - > root = _file ( p ) ) )
return_0 ;
return 1 ;
}
struct config_tree * create_config_tree_from_string ( struct cmd_context * cmd ,
const char * config_settings )
{
struct cs * c ;
struct config_tree * cft ;
struct parser * p ;
2006-11-04 03:34:10 +00:00
if ( ! ( cft = create_config_tree ( NULL , 0 ) ) )
2006-05-16 16:48:31 +00:00
return_NULL ;
c = ( struct cs * ) cft ;
if ( ! ( p = dm_pool_alloc ( c - > mem , sizeof ( * p ) ) ) ) {
log_error ( " Failed to allocate config tree parser. " ) ;
destroy_config_tree ( cft ) ;
return NULL ;
}
p - > mem = c - > mem ;
p - > fb = config_settings ;
p - > fe = config_settings + strlen ( config_settings ) ;
if ( ! _parse_config_file ( p , cft ) ) {
destroy_config_tree ( cft ) ;
return_NULL ;
}
return cft ;
}
2004-03-08 18:28:45 +00:00
int read_config_fd ( struct config_tree * cft , struct device * dev ,
2002-12-19 23:25:55 +00:00
off_t offset , size_t size , off_t offset2 , size_t size2 ,
2002-11-18 14:01:16 +00:00
checksum_fn_t checksum_fn , uint32_t checksum )
2001-08-21 12:56:08 +00:00
{
2004-03-08 18:28:45 +00:00
struct cs * c = ( struct cs * ) cft ;
2002-04-24 18:20:51 +00:00
struct parser * p ;
2002-11-18 14:01:16 +00:00
int r = 0 ;
2003-07-04 22:34:56 +00:00
int use_mmap = 1 ;
off_t mmap_offset = 0 ;
2007-06-13 15:11:19 +00:00
char * buf = NULL ;
2002-04-24 18:20:51 +00:00
2005-10-16 23:03:59 +00:00
if ( ! ( p = dm_pool_alloc ( c - > mem , sizeof ( * p ) ) ) ) {
2002-04-24 18:20:51 +00:00
stack ;
return 0 ;
}
2001-08-21 12:56:08 +00:00
p - > mem = c - > mem ;
2003-07-04 22:34:56 +00:00
/* Only use mmap with regular files */
if ( ! ( dev - > flags & DEV_REGULAR ) | | size2 )
use_mmap = 0 ;
if ( use_mmap ) {
2006-08-17 18:23:44 +00:00
mmap_offset = offset % lvm_getpagesize ( ) ;
2002-11-18 14:01:16 +00:00
/* memory map the file */
p - > fb = mmap ( ( caddr_t ) 0 , size + mmap_offset , PROT_READ ,
2003-07-04 22:34:56 +00:00
MAP_PRIVATE , dev_fd ( dev ) , offset - mmap_offset ) ;
2002-11-18 14:01:16 +00:00
if ( p - > fb = = ( caddr_t ) ( - 1 ) ) {
2003-07-04 22:34:56 +00:00
log_sys_error ( " mmap " , dev_name ( dev ) ) ;
2002-11-18 14:01:16 +00:00
goto out ;
}
p - > fb = p - > fb + mmap_offset ;
2003-07-04 22:34:56 +00:00
} else {
2006-05-16 16:48:31 +00:00
if ( ! ( buf = dm_malloc ( size + size2 ) ) ) {
2003-07-04 22:34:56 +00:00
stack ;
return 0 ;
}
2007-04-19 02:10:42 +00:00
if ( ! dev_read_circular ( dev , ( uint64_t ) offset , size ,
( uint64_t ) offset2 , size2 , buf ) ) {
2003-07-04 22:34:56 +00:00
goto out ;
}
2006-05-16 16:48:31 +00:00
p - > fb = buf ;
2002-11-18 14:01:16 +00:00
}
if ( checksum_fn & & checksum ! =
( checksum_fn ( checksum_fn ( INITIAL_CRC , p - > fb , size ) ,
p - > fb + size , size2 ) ) ) {
2003-07-04 22:34:56 +00:00
log_error ( " %s: Checksum error " , dev_name ( dev ) ) ;
2002-11-18 14:01:16 +00:00
goto out ;
}
p - > fe = p - > fb + size + size2 ;
2006-05-16 16:48:31 +00:00
if ( ! _parse_config_file ( p , cft ) ) {
2002-11-18 14:01:16 +00:00
stack ;
goto out ;
}
r = 1 ;
out :
2003-07-04 22:34:56 +00:00
if ( ! use_mmap )
2006-05-16 16:48:31 +00:00
dm_free ( buf ) ;
2002-11-18 14:01:16 +00:00
else {
/* unmap the file */
2003-03-20 14:29:28 +00:00
if ( munmap ( ( char * ) ( p - > fb - mmap_offset ) , size + mmap_offset ) ) {
2003-07-04 22:34:56 +00:00
log_sys_error ( " munmap " , dev_name ( dev ) ) ;
2002-11-18 14:01:16 +00:00
r = 0 ;
}
}
return r ;
}
2004-05-04 18:28:15 +00:00
int read_config_file ( struct config_tree * cft )
2002-11-18 14:01:16 +00:00
{
2004-03-08 18:28:45 +00:00
struct cs * c = ( struct cs * ) cft ;
2002-11-18 14:01:16 +00:00
struct stat info ;
2003-07-04 22:34:56 +00:00
int r = 1 ;
2002-11-18 14:01:16 +00:00
2004-05-04 18:28:15 +00:00
if ( stat ( c - > filename , & info ) ) {
log_sys_error ( " stat " , c - > filename ) ;
c - > exists = 0 ;
2002-04-24 18:20:51 +00:00
return 0 ;
}
2002-11-18 14:01:16 +00:00
if ( ! S_ISREG ( info . st_mode ) ) {
2004-05-04 18:28:15 +00:00
log_error ( " %s is not a regular file " , c - > filename ) ;
c - > exists = 0 ;
2002-11-18 14:01:16 +00:00
return 0 ;
}
2004-05-04 18:28:15 +00:00
c - > exists = 1 ;
2002-04-24 18:20:51 +00:00
if ( info . st_size = = 0 ) {
2004-05-04 18:28:15 +00:00
log_verbose ( " %s is empty " , c - > filename ) ;
2002-04-24 18:20:51 +00:00
return 1 ;
}
2006-11-04 03:34:10 +00:00
if ( ! c - > dev ) {
if ( ! ( c - > dev = dev_create_file ( c - > filename , NULL , NULL , 1 ) ) )
return_0 ;
2003-07-04 22:34:56 +00:00
2006-11-04 03:34:10 +00:00
if ( ! dev_open_flags ( c - > dev , O_RDONLY , 0 , 0 ) )
return_0 ;
2002-04-24 18:20:51 +00:00
}
2006-11-04 03:34:10 +00:00
r = read_config_fd ( cft , c - > dev , 0 , ( size_t ) info . st_size , 0 , 0 ,
2002-11-18 14:01:16 +00:00
( checksum_fn_t ) NULL , 0 ) ;
2006-11-04 03:34:10 +00:00
if ( ! c - > keep_open ) {
dev_close ( c - > dev ) ;
c - > dev = 0 ;
}
2002-11-18 14:01:16 +00:00
2006-11-04 03:34:10 +00:00
c - > timestamp = info . st_ctime ;
2002-11-18 14:01:16 +00:00
return r ;
}
2004-03-08 18:28:45 +00:00
time_t config_file_timestamp ( struct config_tree * cft )
2003-01-06 21:09:04 +00:00
{
2004-03-08 18:28:45 +00:00
struct cs * c = ( struct cs * ) cft ;
2003-01-06 21:09:04 +00:00
return c - > timestamp ;
}
2002-11-18 14:01:16 +00:00
/*
2004-05-04 18:28:15 +00:00
* Return 1 if config files ought to be reloaded
2002-11-18 14:01:16 +00:00
*/
2004-05-04 18:28:15 +00:00
int config_file_changed ( struct config_tree * cft )
2002-11-18 14:01:16 +00:00
{
2004-05-04 18:28:15 +00:00
struct cs * c = ( struct cs * ) cft ;
2002-11-18 14:01:16 +00:00
struct stat info ;
2002-11-26 12:14:37 +00:00
if ( ! c - > filename )
return 0 ;
2002-11-18 14:01:16 +00:00
if ( stat ( c - > filename , & info ) = = - 1 ) {
2004-05-04 18:28:15 +00:00
/* Ignore a deleted config file: still use original data */
if ( errno = = ENOENT ) {
if ( ! c - > exists )
return 0 ;
log_very_verbose ( " Config file %s has disappeared! " ,
c - > filename ) ;
goto reload ;
}
2002-11-18 14:01:16 +00:00
log_sys_error ( " stat " , c - > filename ) ;
2004-05-04 18:28:15 +00:00
log_error ( " Failed to reload configuration files " ) ;
2002-04-24 18:20:51 +00:00
return 0 ;
}
2002-11-18 14:01:16 +00:00
if ( ! S_ISREG ( info . st_mode ) ) {
log_error ( " Configuration file %s is not a regular file " ,
c - > filename ) ;
2004-05-04 18:28:15 +00:00
goto reload ;
2002-04-24 18:20:51 +00:00
}
2002-11-18 14:01:16 +00:00
/* Unchanged? */
2006-11-04 03:34:10 +00:00
if ( c - > timestamp = = info . st_ctime )
2002-11-18 14:01:16 +00:00
return 0 ;
2004-05-04 18:28:15 +00:00
reload :
log_verbose ( " Detected config file change to %s " , c - > filename ) ;
return 1 ;
2001-08-21 12:56:08 +00:00
}
2007-07-08 22:51:20 +00:00
static int _line_start ( struct output_line * outline )
{
if ( ! dm_pool_begin_object ( outline - > mem , 128 ) ) {
log_error ( " dm_pool_begin_object failed for config line " ) ;
return 0 ;
}
return 1 ;
}
2007-07-20 15:38:19 +00:00
static int _line_append ( struct output_line * outline , const char * fmt , . . . )
__attribute__ ( ( format ( printf , 2 , 3 ) ) ) ;
2007-07-08 22:51:20 +00:00
static int _line_append ( struct output_line * outline , const char * fmt , . . . )
{
char buf [ 4096 ] ;
va_list ap ;
int n ;
va_start ( ap , fmt ) ;
2007-07-20 15:26:39 +00:00
n = vsnprintf ( & buf [ 0 ] , sizeof buf - 1 , fmt , ap ) ;
if ( n < 0 | | n > sizeof buf - 1 ) {
2007-07-08 22:51:20 +00:00
log_error ( " vsnprintf failed for config line " ) ;
return 0 ;
}
va_end ( ap ) ;
if ( ! dm_pool_grow_object ( outline - > mem , & buf [ 0 ] , strlen ( buf ) ) ) {
log_error ( " dm_pool_grew_object failed for config line " ) ;
return 0 ;
}
return 1 ;
}
# define line_append(args...) do {if (!_line_append(outline, args)) {return_0;}} while (0)
static int _line_end ( struct output_line * outline )
{
const char * line ;
if ( ! dm_pool_grow_object ( outline - > mem , " \0 " , 1 ) ) {
log_error ( " dm_pool_grow_object failed for config line " ) ;
return 0 ;
}
line = dm_pool_end_object ( outline - > mem ) ;
if ( ! outline - > fp )
log_print ( " %s " , line ) ;
else
fprintf ( outline - > fp , " %s \n " , line ) ;
return 1 ;
}
static int _write_value ( struct output_line * outline , struct config_value * v )
2001-08-21 12:56:08 +00:00
{
switch ( v - > type ) {
case CFG_STRING :
2007-07-08 22:51:20 +00:00
line_append ( " \" %s \" " , v - > v . str ) ;
2001-08-21 12:56:08 +00:00
break ;
case CFG_FLOAT :
2007-07-08 22:51:20 +00:00
line_append ( " %f " , v - > v . r ) ;
2001-08-21 12:56:08 +00:00
break ;
case CFG_INT :
2007-07-08 22:51:20 +00:00
line_append ( " % " PRId64 , v - > v . i ) ;
2001-08-21 12:56:08 +00:00
break ;
2002-08-01 12:46:52 +00:00
case CFG_EMPTY_ARRAY :
2007-07-08 22:51:20 +00:00
line_append ( " [] " ) ;
2002-08-01 12:46:52 +00:00
break ;
default :
2002-11-18 14:01:16 +00:00
log_error ( " _write_value: Unknown value type: %d " , v - > type ) ;
2001-08-21 12:56:08 +00:00
}
2007-07-08 22:51:20 +00:00
return 1 ;
2001-08-21 12:56:08 +00:00
}
2007-07-08 22:51:20 +00:00
static int _write_config ( struct config_node * n , int only_one ,
struct output_line * outline , int level )
2001-08-21 12:56:08 +00:00
{
2002-04-24 18:20:51 +00:00
char space [ MAX_INDENT + 1 ] ;
int l = ( level < MAX_INDENT ) ? level : MAX_INDENT ;
int i ;
2001-08-21 12:56:08 +00:00
if ( ! n )
return 1 ;
2002-04-24 18:20:51 +00:00
for ( i = 0 ; i < l ; i + + )
2002-08-01 08:22:09 +00:00
space [ i ] = ' \t ' ;
2002-04-24 18:20:51 +00:00
space [ i ] = ' \0 ' ;
2001-08-21 12:56:08 +00:00
2007-01-09 23:22:31 +00:00
do {
2007-07-08 22:51:20 +00:00
if ( ! _line_start ( outline ) )
return_0 ;
line_append ( " %s%s " , space , n - > key ) ;
2001-08-21 12:56:08 +00:00
if ( ! n - > v ) {
/* it's a sub section */
2007-07-08 22:51:20 +00:00
line_append ( " { " ) ;
if ( ! _line_end ( outline ) )
return_0 ;
if ( ! _line_start ( outline ) )
return_0 ;
_write_config ( n - > child , 0 , outline , level + 1 ) ;
line_append ( " %s} " , space ) ;
2001-08-21 12:56:08 +00:00
} else {
/* it's a value */
struct config_value * v = n - > v ;
2007-07-08 22:51:20 +00:00
line_append ( " = " ) ;
2001-08-21 12:56:08 +00:00
if ( v - > next ) {
2007-07-08 22:51:20 +00:00
line_append ( " [ " ) ;
2001-08-21 12:56:08 +00:00
while ( v ) {
2007-07-08 22:51:20 +00:00
if ( ! _write_value ( outline , v ) )
return_0 ;
2001-08-21 12:56:08 +00:00
v = v - > next ;
if ( v )
2007-07-08 22:51:20 +00:00
line_append ( " , " ) ;
2001-08-21 12:56:08 +00:00
}
2007-07-08 22:51:20 +00:00
line_append ( " ] " ) ;
2001-08-21 12:56:08 +00:00
} else
2007-07-08 22:51:20 +00:00
if ( ! _write_value ( outline , v ) )
return_0 ;
2001-08-21 12:56:08 +00:00
}
2007-07-08 22:51:20 +00:00
if ( ! _line_end ( outline ) )
return_0 ;
2002-04-24 18:20:51 +00:00
n = n - > sib ;
2007-01-09 23:22:31 +00:00
} while ( n & & ! only_one ) ;
2001-08-21 12:56:08 +00:00
/* FIXME: add error checking */
return 1 ;
}
2007-01-09 23:22:31 +00:00
int write_config_file ( struct config_tree * cft , const char * file ,
int argc , char * * argv )
2001-08-21 12:56:08 +00:00
{
2007-01-09 23:22:31 +00:00
struct config_node * cn ;
2001-08-21 12:56:08 +00:00
int r = 1 ;
2007-07-08 22:51:20 +00:00
struct output_line outline ;
outline . fp = NULL ;
2003-10-15 20:17:19 +00:00
2007-07-08 22:51:20 +00:00
if ( ! file )
2003-10-15 20:17:19 +00:00
file = " stdout " ;
2007-07-08 22:51:20 +00:00
else if ( ! ( outline . fp = fopen ( file , " w " ) ) ) {
2002-04-24 18:20:51 +00:00
log_sys_error ( " open " , file ) ;
return 0 ;
}
2001-08-21 12:56:08 +00:00
2007-07-08 22:51:20 +00:00
outline . mem = dm_pool_create ( " config_line " , 1024 ) ;
2003-10-15 20:17:19 +00:00
log_verbose ( " Dumping configuration to %s " , file ) ;
2007-01-09 23:22:31 +00:00
if ( ! argc ) {
2007-07-08 22:51:20 +00:00
if ( ! _write_config ( cft - > root , 0 , & outline , 0 ) ) {
2007-01-25 14:37:48 +00:00
log_error ( " Failure while writing to %s " , file ) ;
2007-01-09 23:22:31 +00:00
r = 0 ;
}
} else while ( argc - - ) {
if ( ( cn = find_config_node ( cft - > root , * argv ) ) ) {
2007-07-08 22:51:20 +00:00
if ( ! _write_config ( cn , 1 , & outline , 0 ) ) {
2007-01-25 14:37:48 +00:00
log_error ( " Failure while writing to %s " , file ) ;
2007-01-09 23:22:31 +00:00
r = 0 ;
}
} else {
log_error ( " Configuration node %s not found " , * argv ) ;
r = 0 ;
}
argv + + ;
2001-08-21 12:56:08 +00:00
}
2003-10-15 20:17:19 +00:00
2007-07-24 17:48:08 +00:00
if ( outline . fp & & lvm_fclose ( outline . fp , file ) ) {
stack ;
2007-01-25 14:37:48 +00:00
r = 0 ;
}
2003-10-15 20:17:19 +00:00
2007-07-08 22:51:20 +00:00
dm_pool_destroy ( outline . mem ) ;
2001-08-21 12:56:08 +00:00
return r ;
}
/*
* parser
*/
static struct config_node * _file ( struct parser * p )
{
2001-10-25 13:08:29 +00:00
struct config_node * root = NULL , * n , * l = NULL ;
2001-08-21 12:56:08 +00:00
while ( p - > t ! = TOK_EOF ) {
if ( ! ( n = _section ( p ) ) ) {
stack ;
return 0 ;
}
if ( ! root )
root = n ;
else
l - > sib = n ;
l = n ;
}
return root ;
}
static struct config_node * _section ( struct parser * p )
{
2007-04-25 20:38:39 +00:00
/* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */
2001-10-25 13:08:29 +00:00
struct config_node * root , * n , * l = NULL ;
2001-08-21 12:56:08 +00:00
if ( ! ( root = _create_node ( p ) ) ) {
stack ;
return 0 ;
}
if ( ! ( root - > key = _dup_tok ( p ) ) ) {
stack ;
return 0 ;
}
2002-04-24 18:20:51 +00:00
match ( TOK_IDENTIFIER ) ;
2001-08-21 12:56:08 +00:00
if ( p - > t = = TOK_SECTION_B ) {
match ( TOK_SECTION_B ) ;
while ( p - > t ! = TOK_SECTION_E ) {
if ( ! ( n = _section ( p ) ) ) {
stack ;
return 0 ;
}
if ( ! root - > child )
root - > child = n ;
else
l - > sib = n ;
l = n ;
}
match ( TOK_SECTION_E ) ;
} else {
match ( TOK_EQ ) ;
if ( ! ( root - > v = _value ( p ) ) ) {
stack ;
return 0 ;
}
}
2002-04-24 18:20:51 +00:00
return root ;
2001-08-21 12:56:08 +00:00
}
static struct config_value * _value ( struct parser * p )
{
2002-04-24 18:20:51 +00:00
/* '[' TYPE* ']' | TYPE */
2002-08-01 08:22:09 +00:00
struct config_value * h = NULL , * l , * ll = NULL ;
2002-04-24 18:20:51 +00:00
if ( p - > t = = TOK_ARRAY_B ) {
match ( TOK_ARRAY_B ) ;
while ( p - > t ! = TOK_ARRAY_E ) {
if ( ! ( l = _type ( p ) ) ) {
2001-08-21 12:56:08 +00:00
stack ;
return 0 ;
}
if ( ! h )
h = l ;
else
ll - > next = l ;
ll = l ;
if ( p - > t = = TOK_COMMA )
match ( TOK_COMMA ) ;
}
2002-04-24 18:20:51 +00:00
match ( TOK_ARRAY_E ) ;
2002-08-01 12:46:52 +00:00
/*
2002-11-18 14:01:16 +00:00
* Special case for an empty array .
2002-08-01 12:46:52 +00:00
*/
if ( ! h ) {
if ( ! ( h = _create_value ( p ) ) )
return NULL ;
h - > type = CFG_EMPTY_ARRAY ;
}
2002-11-18 14:01:16 +00:00
2002-04-24 18:20:51 +00:00
} else
2001-08-21 12:56:08 +00:00
h = _type ( p ) ;
2002-04-24 18:20:51 +00:00
return h ;
2001-08-21 12:56:08 +00:00
}
2001-10-25 11:34:55 +00:00
static struct config_value * _type ( struct parser * p )
{
2003-07-15 16:32:20 +00:00
/* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */
2001-08-21 12:56:08 +00:00
struct config_value * v = _create_value ( p ) ;
2002-11-18 14:01:16 +00:00
2002-08-01 12:46:52 +00:00
if ( ! v )
return NULL ;
2001-08-21 12:56:08 +00:00
2002-04-24 18:20:51 +00:00
switch ( p - > t ) {
case TOK_INT :
2001-08-21 12:56:08 +00:00
v - > type = CFG_INT ;
2007-04-27 20:41:50 +00:00
v - > v . i = strtoll ( p - > tb , NULL , 0 ) ; /* FIXME: check error */
2002-04-24 18:20:51 +00:00
match ( TOK_INT ) ;
break ;
2001-08-21 12:56:08 +00:00
2002-04-24 18:20:51 +00:00
case TOK_FLOAT :
2001-08-21 12:56:08 +00:00
v - > type = CFG_FLOAT ;
2002-08-01 08:22:09 +00:00
v - > v . r = strtod ( p - > tb , NULL ) ; /* FIXME: check error */
2002-04-24 18:20:51 +00:00
match ( TOK_FLOAT ) ;
break ;
2001-08-21 12:56:08 +00:00
2002-04-24 18:20:51 +00:00
case TOK_STRING :
2001-08-21 12:56:08 +00:00
v - > type = CFG_STRING ;
2002-04-24 18:20:51 +00:00
p - > tb + + , p - > te - - ; /* strip "'s */
2001-08-21 12:56:08 +00:00
if ( ! ( v - > v . str = _dup_tok ( p ) ) ) {
stack ;
return 0 ;
}
p - > te + + ;
2002-04-24 18:20:51 +00:00
match ( TOK_STRING ) ;
break ;
2001-08-21 12:56:08 +00:00
2002-04-24 18:20:51 +00:00
default :
2007-04-26 16:40:46 +00:00
log_error ( " Parse error at byte % " PRIptrdiff_t " (line %d): expected a value " ,
p - > tb - p - > fb + 1 , p - > line ) ;
2002-04-24 18:20:51 +00:00
return 0 ;
}
return v ;
2001-08-21 12:56:08 +00:00
}
static int _match_aux ( struct parser * p , int t )
{
if ( p - > t ! = t )
return 0 ;
2003-02-03 20:08:45 +00:00
_get_token ( p , t ) ;
2001-08-21 12:56:08 +00:00
return 1 ;
}
/*
* tokeniser
*/
2003-02-03 20:08:45 +00:00
static void _get_token ( struct parser * p , int tok_prev )
2001-08-21 12:56:08 +00:00
{
2003-02-03 20:08:45 +00:00
int values_allowed = 0 ;
2002-04-24 18:20:51 +00:00
p - > tb = p - > te ;
_eat_space ( p ) ;
2002-11-18 14:01:16 +00:00
if ( p - > tb = = p - > fe | | ! * p - > tb ) {
2001-08-21 12:56:08 +00:00
p - > t = TOK_EOF ;
return ;
}
2003-02-03 20:08:45 +00:00
/* Should next token be interpreted as value instead of identifier? */
if (