/* ** CONFIG FILE: Simple config file parser ** Copyright (C) 2002 Michael W. Shaffer ** ** 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 (see the file COPYING). If not, write to: ** ** The Free Software Foundation, Inc. ** 59 Temple Place, Suite 330, ** Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "config_file.h" /* ** Config file format is as follows: ** ** - settings are in the form of either: ** ** key = value ** ** or: ** ** key : value ** ** - keys and values may contain only the characters: ** ** - _ . / + a-z A-Z 0-9 ** ** - whitespace is optional and is discarded along with anything ** else no explicitly listed above ** - comments start with either ';' or '#' and continue ** until end of line ** - only one 'setting' per line ** ** The parsing routine basically supports either the classic ** unix style conf file with ':' as key/value separator and ** '#' as comment character as well as .ini like syntax with ** '=' as key/value separator and ';' as comment character. I ** don't think that real windows .ini files allow comments on ** the same line as settings, but this routine does. ** ** Here are some short samples of each format: ** ******************************************************************************* ** ** # ** # sample exampled.conf file ** # (classic unix syntax) ** # ** ** host: 127.0.0.1 # peer host to monitor ** interval: 20 # interval in seconds between checks ** tolerance: 3 # number of failed checks to initiate failover ** ******************************************************************************* ** ** ; ** ; sample exampled.conf file ** ; (.ini like systax) ** ; ** ** host = 127.0.0.1 ; peer host to monitor ** interval = 20 ; interval in seconds between checks ** tolerance = 3 ; number of failed checks to initiate failover ** ******************************************************************************* */ enum { tagsize = 8192, bufsize = 8192 }; enum character_classes { c_junk = 0, c_name_char, c_separator, c_end_of_line, c_comment }; enum parse_states { s_comment = 0, s_key, s_value }; static int parse_state = s_key; static int classify (char c) { if ((parse_state == s_comment) && !((c == '\n') || (c == '\r'))) return c_comment; if ((c >= 'a') && (c <= 'z')) return c_name_char; if ((c >= 'A') && (c <= 'Z')) return c_name_char; if ((c >= '0') && (c <= '9')) return c_name_char; switch (c) { case '-': case '_': case '.': case '/': case '+': return c_name_char; case ':': case '=': case '|': parse_state = s_value; return c_separator; case ';': case '#': parse_state = s_comment; return c_comment; case '\n': case '\r': parse_state = s_key; return c_end_of_line; default: return c_junk; } } int parse_config_file (char *conf_path, struct hash_table *conf) { int fd = -1; char *buf = NULL; char *bufp = NULL; struct datum d; char c = '\0'; char key[tagsize]; char val[tagsize]; int kpos = 0; int vpos = 0; if ( !(conf_path && conf)) return -1; fd = open (conf_path, O_RDONLY); if (fd == -1) { return -1; } buf = (char *) malloc (bufsize); if (!buf) { return -1; } parse_state = s_key; memset (key, 0, sizeof (key)); memset (val, 0, sizeof (val)); memset (&d, 0, sizeof (struct datum)); d.key = key; d.val = val; memset (buf, 0, bufsize); while (read (fd, buf, (bufsize - 1)) > 0) { bufp = buf; while ((c = *(bufp++))) { switch (classify (c)) { case c_name_char: if (parse_state == s_key) { if (kpos < (tagsize - 1)) key[kpos++] = c; } else if (parse_state == s_value) { if (vpos < (tagsize - 1)) val[vpos++] = c; } break; case c_end_of_line: if (strlen (key) > 0) { d.ksize = strlen (key); d.vsize = strlen (val); hash_table_insert (conf, &d); } memset (key, 0, sizeof (key)); memset (val, 0, sizeof (val)); kpos = 0; vpos = 0; break; default: break; } } memset (buf, 0, bufsize); } if (buf) free (buf); if (fd > 0) close (fd); return 0; }