Magic Lantern / config.c

/** \file
 * Key/value parser until we have a proper config
 */
/*
 * Copyright (C) 2009 Trammell Hudson <hudson+ml@osresearch.net>
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * 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.,
 * 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */

#include "dryos.h"
#include "config.h"

// Don't use isspace since we don't have it
static inline int
is_space( char c )
{
	return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}

// Don't use strcmp since we don't have it
int
streq( const char * a, const char * b )
{
	while( *a && *b )
		if( *a++ != *b++ )
			return 0;
	return *a == *b;
}

struct config *
config_parse_line(
	const char *		line
)
{
	struct config *		config = malloc( sizeof(*config) );
	if( !config )
		goto malloc_error;

	config->next = 0;

	// Trim any leading whitespace
	int i = 0;
	while( line[i] && is_space( line[i] ) )
		i++;

	// Copy the name to the name buffer
	int name_len = 0;
	while( line[i]
	&& !is_space( line[i] )
	&& line[i] != '='
	&& name_len < MAX_NAME_LEN
	)
		config->name[ name_len++ ] = line[i++];

	if( name_len == MAX_NAME_LEN )
		goto parse_error;

	// And nul terminate it
	config->name[ name_len ] = '\0';

	// Skip any white space and = signs
	while( line[i] && is_space( line[i] ) )
		i++;
	if( line[i++] != '=' )
		goto parse_error;
	while( line[i] && is_space( line[i] ) )
		i++;

	// Copy the value to the value buffer
	int value_len = 0;
	while( line[i] && value_len < MAX_VALUE_LEN )
		config->value[ value_len++ ] = line[ i++ ];

	// Back up to trim any white space
	while( value_len > 0 && is_space( config->value[ value_len-1 ] ) )
		value_len--;

	// And nul terminate it
	config->value[ value_len ] = '\0';

	return config;

parse_error:
	free( config );
malloc_error:
	return 0;
}


int
read_line(
	FILE *			file,
	char *			buf,
	size_t			size
)
{
	size_t			len = 0;

	while( len < size )
	{
		int rc = FIO_ReadFile( file, buf+len, 1 );
		if( rc <= 0 )
			return -1;

		if( buf[len] == '\r' )
			continue;
		if( buf[len] == '\n' )
		{
			buf[len] = '\0';
			return len;
		}

		len++;
	}

	return -1;
}



struct config *
config_parse(
	FILE *			file
) {
	char line_buf[ MAX_NAME_LEN + MAX_VALUE_LEN ];
	struct config *	config = 0;

	while( read_line( file, line_buf, sizeof(line_buf) ) >= 0 )
	{
		// Ignore any line that begins with # or is empty
		if( line_buf[0] == '#'
		||  line_buf[0] == '\0' )
			continue;

		struct config * new_config = config_parse_line( line_buf );
		if( !new_config )
			goto error;

		new_config->next = config;
		config = new_config;
	}

	return config;

error:
	while( config )
	{
		struct config * next = config->next;
		free( config );
		config = next;
	}

	return NULL;
}


char *
config_value(
	struct config *		config,
	const char *		name
)
{
	while( config )
	{
		if( streq( config->name, name ) )
			return config->value;

		config = config->next;
	}

	return NULL;
}


int
config_int(
	struct config *		config,
	const char *		name,
	int			def
)
{
	const char *		str = config_value( config, name );
	if( !str )
	{
		DebugMsg( DM_MAGIC, 3,
			"Config '%s', using default %d",
			name,
			def
		);

		return def;
	}

	def = atoi( str );
	DebugMsg( DM_MAGIC, 3,
		"Config '%s', using user value %d ('%s')",
		name,
		def,
		str
	);

	return def;
}


struct config head = { .name = "config.file", .value = "" };
struct config fail = { .name = "config.failure", .value = "1" };

struct config *
config_parse_file(
	const char *		filename
)
{
	FILE * file = FIO_Open( filename, 0 );
	strcpy( head.value, filename );
	if( file == INVALID_PTR )
	{
		head.next = &fail;
		return &head;
	}

	struct config * config = config_parse( file );
	FIO_CloseFile( file );
	head.next = config;
	return &head;
}


int
atoi(
	const char *		s
)
{
	int value = 0;

	// Only handles base ten for now
	while( 1 )
	{
		char c = *s++;
		if( !c || c < '0' || c > '9' )
			break;
		value = value * 10 + c - '0';
	}

	return value;
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.