Addition of configuration file support

Cleanup a few warnings
This commit is contained in:
Mark Riddoch 2013-06-21 17:14:39 +02:00
parent dcc6071ddd
commit 3956abe604
63 changed files with 1189 additions and 70 deletions

View File

@ -20,10 +20,12 @@
# 17/06/13 Mark Riddoch Addition of documentation and depend
# targets
# 18/06/13 Mark Riddoch Addition of install target
# 21/06/13 Mark Riddoch Addition of inih
DEST=/usr/local/skysql
all:
(cd inih/extra ; make -f Makefile.static)
(cd core; make)
(cd modules/routing; make)
(cd modules/protocol; make)

View File

@ -21,16 +21,16 @@
# 17/06/13 Mark Riddoch Addition of dependency generation
CC=cc
CFLAGS=-c -I/usr/include -I../include -Wall -g
CFLAGS=-c -I/usr/include -I../include -I../inih -Wall -g
LDFLAGS=-rdynamic
SRCS= atomic.c buffer.c spinlock.c gateway.c gateway_mysql_protocol.c gw_utils.c \
utils.c dcb.c load_utils.c session.c service.c server.c poll.c
utils.c dcb.c load_utils.c session.c service.c server.c poll.c config.c
HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \
../include/gateway_mysql.h ../include/gw.h ../include/mysql_protocol.h \
../include/session.h ../include/spinlock.h ../include/thread.h \
../include/modules.h ../include/poll.h
../include/modules.h ../include/poll.h ../include/config.h
OBJ=$(SRCS:.c=.o)
LIBS=-lssl
LIBS=-L../inih/extra -linih -lssl -lstdc++
gateway: $(OBJ)
$(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@

211
core/config.c Normal file
View File

@ -0,0 +1,211 @@
/*
* This file is distributed as part of the SkySQL Gateway. It 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,
* version 2.
*
* 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.
*
* Copyright SkySQL Ab 2013
*/
/**
* @file config.c - Read the gateway.cnf configuration file
*
* @verbatim
* Revision History
*
* Date Who Description
* 21/06/13 Mark Riddoch Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ini.h>
#include <config.h>
#include <service.h>
#include <server.h>
static int process_config_context(CONFIG_CONTEXT *);
static char *config_get_value(CONFIG_PARAMETER *, const char *);
/**
* Config item handler
*
* @param userdata The config context element
* @param secton The config file section
* @param name The Parameter name
* @param value The Parameter value
*/
static int
handler(void *userdata, const char *section, const char *name, const char *value)
{
CONFIG_CONTEXT *cntxt = (CONFIG_CONTEXT *)userdata;
CONFIG_CONTEXT *ptr = cntxt;
CONFIG_PARAMETER *param;
/*
* If we already have some parameters for the object
* add the parameters to that object. If not create
* a new object.
*/
while (ptr && strcmp(ptr->object, section) != 0)
ptr = ptr->next;
if (!ptr)
{
if ((ptr = (CONFIG_CONTEXT *)malloc(sizeof(CONFIG_CONTEXT))) == NULL)
return 0;
ptr->object = strdup(section);
ptr->parameters = NULL;
ptr->next = cntxt->next;
cntxt->next = ptr;
}
if ((param = (CONFIG_PARAMETER *)malloc(sizeof(CONFIG_PARAMETER))) == NULL)
return 0;
param->name = strdup(name);
param->value = strdup(value);
param->next = ptr->parameters;
ptr->parameters = param;
return 1;
}
/**
*
* @param file The filename of the configuration file
*/
int
load_config(char *file)
{
CONFIG_CONTEXT config;
config.object = "";
config.next = NULL;
if (ini_parse(file, handler, &config) < 0)
return 0;
return process_config_context(config.next);
}
/**
* Process a configuration context and turn it into the set of object
* we need.
*
* @param context The configuration data
*/
static int
process_config_context(CONFIG_CONTEXT *context)
{
CONFIG_CONTEXT *obj;
/**
* Process the data and create the services and servers defined
* in the data.
*/
obj = context;
while (obj)
{
char *type = config_get_value(obj->parameters, "type");
if (type == NULL)
fprintf(stderr, "Object %s has no type\n", obj->object);
else if (!strcmp(type, "service"))
{
char *router = config_get_value(obj->parameters, "router");
if (router)
obj->element = service_alloc(obj->object, router);
else
fprintf(stderr, "No router define for service '%s'\n",
obj->object);
}
else if (!strcmp(type, "server"))
{
char *address = config_get_value(obj->parameters, "address");
char *port = config_get_value(obj->parameters, "port");
char *protocol = config_get_value(obj->parameters, "protocol");
if (address && port && protocol)
obj->element = server_alloc(address, protocol, atoi(port));
}
obj = obj->next;
}
/*
* Now we have the services we can add the servers to the services
* add the protocols to the services
*/
obj = context;
while (obj)
{
char *type = config_get_value(obj->parameters, "type");
if (type == NULL)
;
else if (!strcmp(type, "service"))
{
char *servers = config_get_value(obj->parameters, "servers");
if (servers)
{
char *s = strtok(servers, ",");
while (s)
{
CONFIG_CONTEXT *obj1 = context;
while (obj1)
{
if (strcmp(s, obj1->object) == 0)
serviceAddBackend(obj->element, obj1->element);
obj1 = obj1->next;
}
s = strtok(NULL, ",");
}
}
}
else if (!strcmp(type, "listener"))
{
char *service = config_get_value(obj->parameters, "service");
char *port = config_get_value(obj->parameters, "port");
char *protocol = config_get_value(obj->parameters, "protocol");
if (service && port && protocol)
{
CONFIG_CONTEXT *ptr = context;
while (ptr && strcmp(ptr->object, service) != 0)
ptr = ptr->next;
if (ptr)
serviceAddProtocol(ptr->element, protocol, atoi(port));
}
}
obj = obj->next;
}
return 1;
}
/**
* Get the value of a config parameter
*
* @param params The linked list of config parameters
* @param name The parameter to return
* @return the parameter value or NULL if not found
*/
static char *
config_get_value(CONFIG_PARAMETER *params, const char *name)
{
while (params)
{
if (!strcmp(params->name, name))
return params->value;
params = params->next;
}
return NULL;
}

View File

@ -69,7 +69,7 @@ gateway.o: gateway.c ../include/gw.h /usr/include/stdio.h \
/usr/include/bits/sched.h /usr/include/bits/setjmp.h ../include/buffer.h \
../include/mysql_protocol.h ../include/dcb.h ../include/service.h \
../include/server.h ../include/session.h ../include/modules.h \
../include/poll.h
../include/config.h ../include/poll.h
gateway_mysql_protocol.o: gateway_mysql_protocol.c ../include/gw.h \
/usr/include/stdio.h /usr/include/features.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
@ -364,14 +364,35 @@ poll.o: poll.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/include/string.h /usr/include/xlocale.h /usr/include/unistd.h \
/usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
/usr/include/bits/confname.h /usr/include/getopt.h \
/usr/include/sys/epoll.h /usr/include/stdint.h /usr/include/bits/wchar.h \
/usr/include/sys/types.h /usr/include/time.h /usr/include/endian.h \
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
/usr/include/sys/select.h /usr/include/bits/select.h \
/usr/include/bits/confname.h /usr/include/getopt.h /usr/include/stdlib.h \
/usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
/usr/include/endian.h /usr/include/bits/endian.h \
/usr/include/bits/byteswap.h /usr/include/sys/types.h \
/usr/include/time.h /usr/include/sys/select.h /usr/include/bits/select.h \
/usr/include/bits/sigset.h /usr/include/bits/time.h \
/usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
../include/poll.h ../include/dcb.h ../include/spinlock.h \
../include/thread.h /usr/include/pthread.h /usr/include/sched.h \
/usr/include/bits/sched.h /usr/include/bits/setjmp.h ../include/buffer.h \
../include/atomic.h
/usr/include/alloca.h /usr/include/sys/epoll.h /usr/include/stdint.h \
/usr/include/bits/wchar.h ../include/poll.h ../include/dcb.h \
../include/spinlock.h ../include/thread.h /usr/include/pthread.h \
/usr/include/sched.h /usr/include/bits/sched.h \
/usr/include/bits/setjmp.h ../include/buffer.h ../include/atomic.h
config.o: config.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/include/string.h /usr/include/xlocale.h /usr/include/stdlib.h \
/usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
/usr/include/endian.h /usr/include/bits/endian.h \
/usr/include/bits/byteswap.h /usr/include/sys/types.h \
/usr/include/time.h /usr/include/sys/select.h /usr/include/bits/select.h \
/usr/include/bits/sigset.h /usr/include/bits/time.h \
/usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
/usr/include/alloca.h ../inih/ini.h ../include/config.h \
../include/service.h ../include/spinlock.h ../include/thread.h \
/usr/include/pthread.h /usr/include/sched.h /usr/include/bits/sched.h \
/usr/include/bits/setjmp.h ../include/dcb.h ../include/buffer.h \
../include/server.h

View File

@ -29,6 +29,7 @@
* listening port
* and bind addr is 0.0.0.0
* 19/06/13 Mark Riddoch Extract the epoll functionality
* 21/06/13 Mark Riddoch Added initial config support
*
* @endverbatim
*/
@ -39,6 +40,7 @@
#include <dcb.h>
#include <session.h>
#include <modules.h>
#include <config.h>
#include <poll.h>
/* basic signal handling */
@ -168,48 +170,29 @@ main(int argc, char **argv)
int daemon_mode = 1;
sigset_t sigset;
int n;
unsigned short port = 4406;
SERVICE *service1, *service2;
SERVER *server1, *server2, *server3;
char *cnf_file = "/etc/gateway.cnf";
for (n = 0; n < argc; n++)
{
if (strncmp(argv[n], "-p", 2) == 0)
{
port = atoi(&argv[n][2]);
}
if (strcmp(argv[n], "-d") == 0)
{
// Debug mode
daemon_mode = 0;
}
if (strncmp(argv[n], "-c", 2) == 0)
{
cnf_file = &argv[n][2];
}
}
/*
* Build the services etc. This would normally be done by the
* configuration, however in lieu of that being available we
* will build a static configuration here
*/
if ((service1 = service_alloc("Test Service", "readconnroute")) == NULL)
if (!load_config(cnf_file))
{
fprintf(stderr, "Failed to load gateway configuration file %s\n", cnf_file);
exit(1);
serviceAddProtocol(service1, "MySQLClient", port);
}
server1 = server_alloc("127.0.0.1", "MySQLBackend", 3306);
server2 = server_alloc("127.0.0.1", "MySQLBackend", 3307);
server3 = server_alloc("127.0.0.1", "MySQLBackend", 3308);
serviceAddBackend(service1, server1);
serviceAddBackend(service1, server2);
serviceAddBackend(service1, server3);
if ((service2 = service_alloc("Debug Service", "debugcli")) == NULL)
exit(1);
serviceAddProtocol(service2, "telnetd", 4442);
fprintf(stderr, "(C) SkySQL Ab 2013\n");
load_module("testroute", "Router");
fprintf(stderr, "SkySQL Gateway (C) SkySQL Ab 2013\n");
if (daemon_mode == 1)
{
@ -238,15 +221,12 @@ SERVER *server1, *server2, *server3;
fprintf(stderr, "GATEWAY is starting, PID %i\n\n", getpid());
fprintf(stderr, ">> GATEWAY log is /dev/stderr\n");
poll_init();
/*
* Start the service that was created above
*/
serviceStart(service1);
serviceStart(service2);
printf("Started %d services\n", serviceStartAll());
while (1)
{

View File

@ -18,6 +18,7 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <poll.h>
#include <dcb.h>

View File

@ -126,6 +126,26 @@ GWPROTOCOL *funcs;
return listeners;
}
/**
* Start all the services
*
* @return Return the number of services started
*/
int
serviceStartAll()
{
SERVICE *ptr;
int n = 0;
ptr = allServices;
while (ptr)
{
n += serviceStart(ptr);
ptr = ptr->next;
}
return n;
}
/**
* Deallocate the specified service
*

41
gateway.cnf Normal file
View File

@ -0,0 +1,41 @@
#
# Example gateway.cnf configuration file
#
[Test Service]
type=service
router=readconnroute
servers=server1,server2,server3
[server1]
type=server
address=localhost
port=3306
protocol=MySQLBackend
[server2]
type=server
address=localhost
port=3307
protocol=MySQLBackend
[server3]
type=server
address=localhost
port=3308
protocol=MySQLBackend
[Debug Service]
type=service
router=debugcli
[Debug Listener]
type=listener
service=Debug Service
protocol=telnetd
port=4042
[Test Listener]
type=listener
service=Test Service
protocol=MySQLClient
port=4006

53
include/config.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef _CONFIG_H
#define _CONFIG_H
/*
* This file is distributed as part of the SkySQL Gateway. It 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,
* version 2.
*
* 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.
*
* Copyright SkySQL Ab 2013
*/
/**
* @file config.h The configuration handling elements
*
* @verbatim
* Revision History
*
* Date Who Description
* 21/06/13 Mark Riddoch Initial implementation
*
* @endverbatim
*/
/**
* The config parameter
*/
typedef struct config_parameter {
char *name; /**< The name of the parameter */
char *value; /**< The value of the parameter */
struct config_parameter *next; /**< Next pointer in the linked list */
} CONFIG_PARAMETER;
/**
* The config context structure, used to build the configuration
* data during the parse process
*/
typedef struct config_context {
char *object; /**< The name of the object being configured */
CONFIG_PARAMETER *parameters; /**< The list of parameter values */
void *element; /**< The element created from the data */
struct config_context *next; /**< Next pointer in the linked list */
} CONFIG_CONTEXT;
extern int load_config(char *);
#endif

View File

@ -96,6 +96,7 @@ extern int service_free(SERVICE *);
extern int serviceAddProtocol(SERVICE *, char *, unsigned short);
extern void serviceAddBackend(SERVICE *, SERVER *);
extern int serviceStart(SERVICE *);
extern int serviceStartAll();
extern void printService(SERVICE *);
extern void printAllServices();
extern void dprintAllServices(DCB *);

BIN
inih/._LICENSE.txt Executable file

Binary file not shown.

BIN
inih/._README.txt Executable file

Binary file not shown.

BIN
inih/._cpp Executable file

Binary file not shown.

BIN
inih/._examples Executable file

Binary file not shown.

BIN
inih/._extra Executable file

Binary file not shown.

BIN
inih/._ini.c Executable file

Binary file not shown.

BIN
inih/._ini.h Executable file

Binary file not shown.

BIN
inih/._tests Executable file

Binary file not shown.

27
inih/LICENSE.txt Executable file
View File

@ -0,0 +1,27 @@
The "inih" library is distributed under the New BSD license:
Copyright (c) 2009, Brush Technology
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 Brush Technology 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 BRUSH TECHNOLOGY ''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 BRUSH TECHNOLOGY 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.

5
inih/README.txt Executable file
View File

@ -0,0 +1,5 @@
inih is a simple .INI file parser written in C, released under the New BSD
license (see LICENSE.txt). Go to the project home page for more info:
http://code.google.com/p/inih/

BIN
inih/cpp/._INIReader.cpp Executable file

Binary file not shown.

BIN
inih/cpp/._INIReader.h Executable file

Binary file not shown.

BIN
inih/cpp/._INIReaderTest.cpp Executable file

Binary file not shown.

67
inih/cpp/INIReader.cpp Executable file
View File

@ -0,0 +1,67 @@
// Read an INI file into easy-to-access name/value pairs.
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include "../ini.h"
#include "INIReader.h"
using std::string;
INIReader::INIReader(string filename)
{
_error = ini_parse(filename.c_str(), ValueHandler, this);
}
int INIReader::ParseError()
{
return _error;
}
string INIReader::Get(string section, string name, string default_value)
{
string key = MakeKey(section, name);
return _values.count(key) ? _values[key] : default_value;
}
long INIReader::GetInteger(string section, string name, long default_value)
{
string valstr = Get(section, name, "");
const char* value = valstr.c_str();
char* end;
// This parses "1234" (decimal) and also "0x4D2" (hex)
long n = strtol(value, &end, 0);
return end > value ? n : default_value;
}
bool INIReader::GetBoolean(string section, string name, bool default_value)
{
string valstr = Get(section, name, "");
// Convert to lower case to make string comparisons case-insensitive
std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower);
if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
return true;
else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
return false;
else
return default_value;
}
string INIReader::MakeKey(string section, string name)
{
string key = section + "." + name;
// Convert to lower case to make section/name lookups case-insensitive
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
return key;
}
int INIReader::ValueHandler(void* user, const char* section, const char* name,
const char* value)
{
INIReader* reader = (INIReader*)user;
string key = MakeKey(section, name);
if (reader->_values[key].size() > 0)
reader->_values[key] += "\n";
reader->_values[key] += value;
return 1;
}

48
inih/cpp/INIReader.h Executable file
View File

@ -0,0 +1,48 @@
// Read an INI file into easy-to-access name/value pairs.
// inih and INIReader are released under the New BSD license (see LICENSE.txt).
// Go to the project home page for more info:
//
// http://code.google.com/p/inih/
#ifndef __INIREADER_H__
#define __INIREADER_H__
#include <map>
#include <string>
// Read an INI file into easy-to-access name/value pairs. (Note that I've gone
// for simplicity here rather than speed, but it should be pretty decent.)
class INIReader
{
public:
// Construct INIReader and parse given filename. See ini.h for more info
// about the parsing.
INIReader(std::string filename);
// Return the result of ini_parse(), i.e., 0 on success, line number of
// first error on parse error, or -1 on file open error.
int ParseError();
// Get a string value from INI file, returning default_value if not found.
std::string Get(std::string section, std::string name,
std::string default_value);
// Get an integer (long) value from INI file, returning default_value if
// not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2").
long GetInteger(std::string section, std::string name, long default_value);
// Get a boolean value from INI file, returning default_value if not found or if
// not a valid true/false value. Valid true values are "true", "yes", "on", "1",
// and valid false values are "false", "no", "off", "0" (not case sensitive).
bool GetBoolean(std::string section, std::string name, bool default_value);
private:
int _error;
std::map<std::string, std::string> _values;
static std::string MakeKey(std::string section, std::string name);
static int ValueHandler(void* user, const char* section, const char* name,
const char* value);
};
#endif // __INIREADER_H__

20
inih/cpp/INIReaderTest.cpp Executable file
View File

@ -0,0 +1,20 @@
// Example that shows simple usage of the INIReader class
#include <iostream>
#include "INIReader.h"
int main()
{
INIReader reader("../examples/test.ini");
if (reader.ParseError() < 0) {
std::cout << "Can't load 'test.ini'\n";
return 1;
}
std::cout << "Config loaded from 'test.ini': version="
<< reader.GetInteger("protocol", "version", -1) << ", name="
<< reader.Get("user", "name", "UNKNOWN") << ", email="
<< reader.Get("user", "email", "UNKNOWN") << ", active="
<< reader.GetBoolean("user", "active", true) << "\n";
return 0;
}

BIN
inih/examples/._config.def Executable file

Binary file not shown.

BIN
inih/examples/._ini_dump.c Executable file

Binary file not shown.

BIN
inih/examples/._ini_example.c Executable file

Binary file not shown.

BIN
inih/examples/._ini_xmacros.c Executable file

Binary file not shown.

BIN
inih/examples/._test.ini Executable file

Binary file not shown.

8
inih/examples/config.def Executable file
View File

@ -0,0 +1,8 @@
// CFG(section, name, default)
CFG(protocol, version, "0")
CFG(user, name, "Fatty Lumpkin")
CFG(user, email, "fatty@lumpkin.com")
#undef CFG

40
inih/examples/ini_dump.c Executable file
View File

@ -0,0 +1,40 @@
/* ini.h example that simply dumps an INI file without comments */
#include <stdio.h>
#include <string.h>
#include "../ini.h"
static int dumper(void* user, const char* section, const char* name,
const char* value)
{
static char prev_section[50] = "";
if (strcmp(section, prev_section)) {
printf("%s[%s]\n", (prev_section[0] ? "\n" : ""), section);
strncpy(prev_section, section, sizeof(prev_section));
prev_section[sizeof(prev_section) - 1] = '\0';
}
printf("%s = %s\n", name, value);
return 1;
}
int main(int argc, char* argv[])
{
int error;
if (argc <= 1) {
printf("Usage: ini_dump filename.ini\n");
return 1;
}
error = ini_parse(argv[1], dumper, NULL);
if (error < 0) {
printf("Can't read '%s'!\n", argv[1]);
return 2;
}
else if (error) {
printf("Bad config file (first error on line %d)!\n", error);
return 3;
}
return 0;
}

44
inih/examples/ini_example.c Executable file
View File

@ -0,0 +1,44 @@
/* Example: parse a simple configuration file */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../ini.h"
typedef struct
{
int version;
const char* name;
const char* email;
} configuration;
static int handler(void* user, const char* section, const char* name,
const char* value)
{
configuration* pconfig = (configuration*)user;
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
if (MATCH("protocol", "version")) {
pconfig->version = atoi(value);
} else if (MATCH("user", "name")) {
pconfig->name = strdup(value);
} else if (MATCH("user", "email")) {
pconfig->email = strdup(value);
} else {
return 0; /* unknown section/name, error */
}
return 1;
}
int main(int argc, char* argv[])
{
configuration config;
if (ini_parse("test.ini", handler, &config) < 0) {
printf("Can't load 'test.ini'\n");
return 1;
}
printf("Config loaded from 'test.ini': version=%d, name=%s, email=%s\n",
config.version, config.name, config.email);
return 0;
}

46
inih/examples/ini_xmacros.c Executable file
View File

@ -0,0 +1,46 @@
/* Parse a configuration file into a struct using X-Macros */
#include <stdio.h>
#include <string.h>
#include "../ini.h"
/* define the config struct type */
typedef struct {
#define CFG(s, n, default) char *s##_##n;
#include "config.def"
} config;
/* create one and fill in its default values */
config Config = {
#define CFG(s, n, default) default,
#include "config.def"
};
/* process a line of the INI file, storing valid values into config struct */
int handler(void *user, const char *section, const char *name,
const char *value)
{
config *cfg = (config *)user;
if (0) ;
#define CFG(s, n, default) else if (strcmp(section, #s)==0 && \
strcmp(name, #n)==0) cfg->s##_##n = strdup(value);
#include "config.def"
return 1;
}
/* print all the variables in the config, one per line */
void dump_config(config *cfg)
{
#define CFG(s, n, default) printf("%s_%s = %s\n", #s, #n, cfg->s##_##n);
#include "config.def"
}
int main(int argc, char* argv[])
{
if (ini_parse("test.ini", handler, &Config) < 0)
printf("Can't load 'test.ini', using defaults\n");
dump_config(&Config);
return 0;
}

9
inih/examples/test.ini Executable file
View File

@ -0,0 +1,9 @@
; Test config file for ini_example.c and INIReaderTest.cpp
[protocol] ; Protocol configuration
version=6 ; IPv6
[user]
name = Bob Smith ; Spaces around '=' are stripped
email = bob@smith.com ; And comments (like this) ignored
active = true ; Test a boolean

BIN
inih/extra/._Makefile.static Executable file

Binary file not shown.

19
inih/extra/Makefile.static Executable file
View File

@ -0,0 +1,19 @@
# Simple makefile to build inih as a static library using g++
SRC = ../ini.c
OBJ = $(SRC:.c=.o)
OUT = libinih.a
INCLUDES = -I..
CCFLAGS = -g -O2
CC = g++
default: $(OUT)
.c.o:
$(CC) $(INCLUDES) $(CCFLAGS) $(EXTRACCFLAGS) -c $< -o $@
$(OUT): $(OBJ)
ar rcs $(OUT) $(OBJ) $(EXTRAARFLAGS)
clean:
rm -f $(OBJ) $(OUT)

176
inih/ini.c Executable file
View File

@ -0,0 +1,176 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
http://code.google.com/p/inih/
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "ini.h"
#if !INI_USE_STACK
#include <stdlib.h>
#endif
#define MAX_SECTION 50
#define MAX_NAME 50
/* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s)
{
char* p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p)))
*p = '\0';
return s;
}
/* Return pointer to first non-whitespace char in given string. */
static char* lskip(const char* s)
{
while (*s && isspace((unsigned char)(*s)))
s++;
return (char*)s;
}
/* Return pointer to first char c or ';' comment in given string, or pointer to
null at end of string if neither found. ';' must be prefixed by a whitespace
character to register as a comment. */
static char* find_char_or_comment(const char* s, char c)
{
int was_whitespace = 0;
while (*s && *s != c && !(was_whitespace && *s == ';')) {
was_whitespace = isspace((unsigned char)(*s));
s++;
}
return (char*)s;
}
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
static char* strncpy0(char* dest, const char* src, size_t size)
{
strncpy(dest, src, size);
dest[size - 1] = '\0';
return dest;
}
/* See documentation in header file. */
int ini_parse_file(FILE* file,
int (*handler)(void*, const char*, const char*,
const char*),
void* user)
{
/* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK
char line[INI_MAX_LINE];
#else
char* line;
#endif
char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = "";
char* start;
char* end;
char* name;
char* value;
int lineno = 0;
int error = 0;
#if !INI_USE_STACK
line = (char*)malloc(INI_MAX_LINE);
if (!line) {
return -2;
}
#endif
/* Scan through file line by line */
while (fgets(line, INI_MAX_LINE, file) != NULL) {
lineno++;
start = line;
#if INI_ALLOW_BOM
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
(unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) {
start += 3;
}
#endif
start = lskip(rstrip(start));
if (*start == ';' || *start == '#') {
/* Per Python ConfigParser, allow '#' comments at start of line */
}
#if INI_ALLOW_MULTILINE
else if (*prev_name && *start && start > line) {
/* Non-black line with leading whitespace, treat as continuation
of previous name's value (as per Python ConfigParser). */
if (!handler(user, section, prev_name, start) && !error)
error = lineno;
}
#endif
else if (*start == '[') {
/* A "[section]" line */
end = find_char_or_comment(start + 1, ']');
if (*end == ']') {
*end = '\0';
strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0';
}
else if (!error) {
/* No ']' found on section line */
error = lineno;
}
}
else if (*start && *start != ';') {
/* Not a comment, must be a name[=:]value pair */
end = find_char_or_comment(start, '=');
if (*end != '=') {
end = find_char_or_comment(start, ':');
}
if (*end == '=' || *end == ':') {
*end = '\0';
name = rstrip(start);
value = lskip(end + 1);
end = find_char_or_comment(value, '\0');
if (*end == ';')
*end = '\0';
rstrip(value);
/* Valid name[=:]value pair found, call handler */
strncpy0(prev_name, name, sizeof(prev_name));
if (!handler(user, section, name, value) && !error)
error = lineno;
}
else if (!error) {
/* No '=' or ':' found on name[=:]value line */
error = lineno;
}
}
}
#if !INI_USE_STACK
free(line);
#endif
return error;
}
/* See documentation in header file. */
int ini_parse(const char* filename,
int (*handler)(void*, const char*, const char*, const char*),
void* user)
{
FILE* file;
int error;
file = fopen(filename, "r");
if (!file)
return -1;
error = ini_parse_file(file, handler, user);
fclose(file);
return error;
}

72
inih/ini.h Executable file
View File

@ -0,0 +1,72 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
http://code.google.com/p/inih/
*/
#ifndef __INI_H__
#define __INI_H__
/* Make this header file easier to include in C++ code */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
/* Parse given INI-style file. May have [section]s, name=value pairs
(whitespace stripped), and comments starting with ';' (semicolon). Section
is "" if name=value pair parsed before any section heading. name:value
pairs are also supported as a concession to Python's ConfigParser.
For each name=value pair parsed, call handler function with given user
pointer as well as section, name, and value (data only valid for duration
of handler call). Handler should return nonzero on success, zero on error.
Returns 0 on success, line number of first error on parse error (doesn't
stop on first error), -1 on file open error, or -2 on memory allocation
error (only when INI_USE_STACK is zero).
*/
int ini_parse(const char* filename,
int (*handler)(void* user, const char* section,
const char* name, const char* value),
void* user);
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
close the file when it's finished -- the caller must do that. */
int ini_parse_file(FILE* file,
int (*handler)(void* user, const char* section,
const char* name, const char* value),
void* user);
/* Nonzero to allow multi-line value parsing, in the style of Python's
ConfigParser. If allowed, ini_parse() will call the handler with the same
name for each subsequent line parsed. */
#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif
/* Nonzero to use stack, zero to use heap (malloc/free). */
#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif
/* Maximum line length for any line in INI file. */
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
#endif
#ifdef __cplusplus
}
#endif
#endif /* __INI_H__ */

BIN
inih/tests/._bad_comment.ini Executable file

Binary file not shown.

BIN
inih/tests/._bad_multi.ini Executable file

Binary file not shown.

BIN
inih/tests/._bad_section.ini Executable file

Binary file not shown.

BIN
inih/tests/._baseline_multi.txt Executable file

Binary file not shown.

BIN
inih/tests/._baseline_single.txt Executable file

Binary file not shown.

BIN
inih/tests/._bom.ini Executable file

Binary file not shown.

BIN
inih/tests/._multi_line.ini Executable file

Binary file not shown.

BIN
inih/tests/._normal.ini Executable file

Binary file not shown.

BIN
inih/tests/._unittest.bat Executable file

Binary file not shown.

BIN
inih/tests/._unittest.c Executable file

Binary file not shown.

BIN
inih/tests/._user_error.ini Executable file

Binary file not shown.

1
inih/tests/bad_comment.ini Executable file
View File

@ -0,0 +1 @@
This is an error

1
inih/tests/bad_multi.ini Executable file
View File

@ -0,0 +1 @@
indented

5
inih/tests/bad_section.ini Executable file
View File

@ -0,0 +1,5 @@
[section1]
name1=value1
[section2
[section3 ; comment ]
name2=value2

47
inih/tests/baseline_multi.txt Executable file
View File

@ -0,0 +1,47 @@
no_file.ini: e=-1 user=0
... [section1]
... one=This is a test;
... two=1234;
... [ section 2 ]
... happy=4;
... sad=;
... [comment_test]
... test1=1;2;3;
... test2=2;3;4;this won't be a comment, needs whitespace before ';';
... test;3=345;
... test4=4#5#6;
... [colon_tests]
... Content-Type=text/html;
... foo=bar;
... adams=42;
normal.ini: e=0 user=101
... [section1]
... name1=value1;
... name2=value2;
bad_section.ini: e=3 user=102
bad_comment.ini: e=1 user=102
... [section]
... a=b;
... user=parse_error;
... c=d;
user_error.ini: e=3 user=104
... [section1]
... single1=abc;
... multi=this is a;
... multi=multi-line value;
... single2=xyz;
... [section2]
... multi=a;
... multi=b;
... multi=c;
... [section3]
... single=ghi;
... multi=the quick;
... multi=brown fox;
... name=bob smith;
multi_line.ini: e=0 user=105
bad_multi.ini: e=1 user=105
... [bom_section]
... bom_name=bom_value;
... key“=value“;
bom.ini: e=0 user=107

43
inih/tests/baseline_single.txt Executable file
View File

@ -0,0 +1,43 @@
no_file.ini: e=-1 user=0
... [section1]
... one=This is a test;
... two=1234;
... [ section 2 ]
... happy=4;
... sad=;
... [comment_test]
... test1=1;2;3;
... test2=2;3;4;this won't be a comment, needs whitespace before ';';
... test;3=345;
... test4=4#5#6;
... [colon_tests]
... Content-Type=text/html;
... foo=bar;
... adams=42;
normal.ini: e=0 user=101
... [section1]
... name1=value1;
... name2=value2;
bad_section.ini: e=3 user=102
bad_comment.ini: e=1 user=102
... [section]
... a=b;
... user=parse_error;
... c=d;
user_error.ini: e=3 user=104
... [section1]
... single1=abc;
... multi=this is a;
... single2=xyz;
... [section2]
... multi=a;
... [section3]
... single=ghi;
... multi=the quick;
... name=bob smith;
multi_line.ini: e=4 user=105
bad_multi.ini: e=1 user=105
... [bom_section]
... bom_name=bom_value;
... key“=value“;
bom.ini: e=0 user=107

3
inih/tests/bom.ini Executable file
View File

@ -0,0 +1,3 @@
[bom_section]
bom_name=bom_value
key“ = value“

15
inih/tests/multi_line.ini Executable file
View File

@ -0,0 +1,15 @@
[section1]
single1 = abc
multi = this is a
multi-line value
single2 = xyz
[section2]
multi = a
b
c
[section3]
single: ghi
multi: the quick
brown fox
name = bob smith ; comment line 1
; comment line 2

25
inih/tests/normal.ini Executable file
View File

@ -0,0 +1,25 @@
; This is an INI file
[section1] ; section comment
one=This is a test ; name=value comment
two = 1234
; x=y
[ section 2 ]
happy = 4
sad =
[empty]
; do nothing
[comment_test]
test1 = 1;2;3 ; only this will be a comment
test2 = 2;3;4;this won't be a comment, needs whitespace before ';'
test;3 = 345 ; key should be "test;3"
test4 = 4#5#6 ; '#' only starts a comment at start of line
#test5 = 567 ; entire line commented
# test6 = 678 ; entire line commented, except in MULTILINE mode
[colon_tests]
Content-Type: text/html
foo:bar
adams : 42

2
inih/tests/unittest.bat Executable file
View File

@ -0,0 +1,2 @@
@call tcc ..\ini.c -I..\ -run unittest.c > baseline_multi.txt
@call tcc ..\ini.c -I..\ -DINI_ALLOW_MULTILINE=0 -run unittest.c > baseline_single.txt

58
inih/tests/unittest.c Executable file
View File

@ -0,0 +1,58 @@
/* inih -- unit tests
This works simply by dumping a bunch of info to standard output, which is
redirected to an output file (baseline_*.txt) and checked into the Subversion
repository. This baseline file is the test output, so the idea is to check it
once, and if it changes -- look at the diff and see which tests failed.
Here's how I produced the two baseline files (with Tiny C Compiler):
tcc -DINI_ALLOW_MULTILINE=1 ../ini.c -run unittest.c > baseline_multi.txt
tcc -DINI_ALLOW_MULTILINE=0 ../ini.c -run unittest.c > baseline_single.txt
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../ini.h"
int User;
char Prev_section[50];
int dumper(void* user, const char* section, const char* name,
const char* value)
{
User = (int)user;
if (strcmp(section, Prev_section)) {
printf("... [%s]\n", section);
strncpy(Prev_section, section, sizeof(Prev_section));
Prev_section[sizeof(Prev_section) - 1] = '\0';
}
printf("... %s=%s;\n", name, value);
return strcmp(name, "user")==0 && strcmp(value, "parse_error")==0 ? 0 : 1;
}
void parse(const char* fname) {
static int u = 100;
int e;
*Prev_section = '\0';
e = ini_parse(fname, dumper, (void*)u);
printf("%s: e=%d user=%d\n", fname, e, User);
u++;
}
int main(void)
{
parse("no_file.ini");
parse("normal.ini");
parse("bad_section.ini");
parse("bad_comment.ini");
parse("user_error.ini");
parse("multi_line.ini");
parse("bad_multi.ini");
parse("bom.ini");
return 0;
}

4
inih/tests/user_error.ini Executable file
View File

@ -0,0 +1,4 @@
[section]
a = b
user = parse_error
c = d

View File

@ -33,10 +33,11 @@ mysql_client.o: mysql_client.c ../include/mysql_client_server_protocol.h \
/usr/include/arpa/inet.h \
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdbool.h \
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/stat.h \
../../include/dcb.h ../../include/spinlock.h ../../include/thread.h \
../../include/service.h ../../include/spinlock.h ../../include/thread.h \
/usr/include/pthread.h /usr/include/sched.h /usr/include/bits/sched.h \
/usr/include/bits/setjmp.h ../../include/buffer.h \
../../include/session.h ../../include/poll.h
/usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \
../../include/server.h ../../include/router.h ../../include/session.h \
../../include/poll.h
mysql_common.o: mysql_common.c ../include/mysql_client_server_protocol.h \
/usr/include/stdio.h /usr/include/features.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
@ -72,30 +73,30 @@ mysql_common.o: mysql_common.c ../include/mysql_client_server_protocol.h \
/usr/include/arpa/inet.h \
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdbool.h \
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/stat.h \
../../include/dcb.h ../../include/spinlock.h ../../include/thread.h \
../../include/service.h ../../include/spinlock.h ../../include/thread.h \
/usr/include/pthread.h /usr/include/sched.h /usr/include/bits/sched.h \
/usr/include/bits/setjmp.h ../../include/buffer.h \
../../include/session.h
mysql_backend.o: mysql_backend.c ../../include/session.h \
/usr/include/time.h /usr/include/features.h /usr/include/sys/cdefs.h \
/usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \
../../include/server.h ../../include/router.h ../../include/session.h \
../../include/poll.h
mysql_backend.o: mysql_backend.c \
../include/mysql_client_server_protocol.h /usr/include/stdio.h \
/usr/include/features.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
/usr/include/bits/time.h /usr/include/bits/types.h \
/usr/include/bits/typesizes.h /usr/include/xlocale.h \
../../include/server.h ../include/mysql_client_server_protocol.h \
/usr/include/stdio.h /usr/include/libio.h /usr/include/_G_config.h \
/usr/include/wchar.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
/usr/include/bits/waitstatus.h /usr/include/endian.h \
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
/usr/include/sys/types.h /usr/include/sys/select.h \
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
/usr/include/bits/select.h /usr/include/bits/sigset.h \
/usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
/usr/include/alloca.h /usr/include/stdint.h /usr/include/bits/wchar.h \
/usr/include/string.h /usr/include/openssl/sha.h \
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
/usr/include/stdint.h /usr/include/bits/wchar.h /usr/include/string.h \
/usr/include/xlocale.h /usr/include/openssl/sha.h \
/usr/include/openssl/e_os2.h /usr/include/openssl/opensslconf.h \
/usr/include/openssl/opensslconf-x86_64.h /usr/include/sys/ioctl.h \
/usr/include/bits/ioctls.h /usr/include/asm/ioctls.h \
@ -113,9 +114,11 @@ mysql_backend.o: mysql_backend.c ../../include/session.h \
/usr/include/arpa/inet.h \
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdbool.h \
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/stat.h \
../../include/dcb.h ../../include/spinlock.h ../../include/thread.h \
../../include/service.h ../../include/spinlock.h ../../include/thread.h \
/usr/include/pthread.h /usr/include/sched.h /usr/include/bits/sched.h \
/usr/include/bits/setjmp.h ../../include/buffer.h
/usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \
../../include/server.h ../../include/router.h ../../include/session.h \
../../include/poll.h
mysql_common.o: mysql_common.c ../include/mysql_client_server_protocol.h \
/usr/include/stdio.h /usr/include/features.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
@ -151,7 +154,8 @@ mysql_common.o: mysql_common.c ../include/mysql_client_server_protocol.h \
/usr/include/arpa/inet.h \
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdbool.h \
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/stat.h \
../../include/dcb.h ../../include/spinlock.h ../../include/thread.h \
../../include/service.h ../../include/spinlock.h ../../include/thread.h \
/usr/include/pthread.h /usr/include/sched.h /usr/include/bits/sched.h \
/usr/include/bits/setjmp.h ../../include/buffer.h \
../../include/session.h
/usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \
../../include/server.h ../../include/router.h ../../include/session.h \
../../include/poll.h