config.c
Author: cmaier@cmassoc.net

    /*====================================================================*
     *
     *   Copyright (c) 2013 Qualcomm Atheros, Inc.
     *
     *   All rights reserved.
     *
     *   Redistribution and use in source and binary forms, with or
     *   without modification, are permitted (subject to the limitations
     *   in the disclaimer below) 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 Qualcomm Atheros nor the names of
     *     its contributors may be used to endorse or promote products
     *     derived from this software without specific prior written
     *     permission.
     *
     *   NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
     *   GRANTED BY THIS LICENSE. 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 OWNER
     *   OR CONTRIBUTORS 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.
     *
     *--------------------------------------------------------------------*/

    /*====================================================================*
     *
     *   config.c - configuration file reader;
     *
     *   configuration files contain named parts where each part may
     *   contain one of more named items that have text definitions;
     *
     *   the named file can be searched for the first occurance of a
     *   named part then the first occurance of a named item;
     *
     *   [part1]
     *   item1=text
     *   item2=text
     *
     *   [part2]
     *   item1=text
     *   item2=text
     *
     *--------------------------------------------------------------------*/

    #ifndef CONFIG_SOURCE
    #define CONFIG_SOURCE

    /*====================================================================*
     *   system header files;
     *--------------------------------------------------------------------*/

    #include <stdio.h>
    #include <ctype.h>
    #include <unistd.h>
    #include <string.h>

    /*====================================================================*
     *   custom header files;
     *--------------------------------------------------------------------*/

    #include "../tools/config.h"
    #include "../tools/types.h"
    #include "../tools/chars.h"

    /*====================================================================*
     *   program variables;
     *--------------------------------------------------------------------*/

    static char buffer [1024] = "";
    static signed c;

    /*====================================================================*
     *
     *   bool compare (FILE * fp, char const *sp);
     *
     *   compare file and text characters until they differ or until end
     *   of text, line or file occurs; a match is declared when the text
     *   ends before the line or file;
     *
     *   spaces and tabs within the argument string or file string are
     *   ignored such that "item1", " item1 " and "item 1" all match;
     *
     *--------------------------------------------------------------------*/

    static bool compare (FILE * fp, char const * sp)

    {
    	while (isblank (*sp))
    	{
    		sp++;
    	}
    	while ((*sp) && (c != '\n') && (c != EOF))
    	{
    		if (toupper (c) != toupper (*sp))
    		{
    			return (0);
    		}
    		do
    		{
    			sp++;
    		}
    		while (isblank (*sp));
    		do
    		{
    			c = getc (fp);
    		}
    		while (isblank (c));
    	}
    	return (!*sp);
    }

    /*====================================================================*
     *
     *   void collect (FILE * fp);
     *
     *   collect text to end of line; remove leading and trailing space
     *   but preserve embedded space; replace selected escape sequences;
     *
     *--------------------------------------------------------------------*/

    static void collect (FILE * fp)

    {
    	char *bp = buffer;
    	char *cp = buffer;
    	while ((c != ';') && (c != '\n') && (c != EOF))
    	{
    		if (c == '\\')
    		{
    			c = getc (fp);
    			if (c == 'n')
    			{
    				c = '\n';
    			}
    			if (c == 't')
    			{
    				c = '\t';
    			}
    		}
    		if ((cp - buffer) < (signed)(sizeof (buffer) - 1))
    		{
    			*cp++ = c;
    		}
    		if (!isblank (c))
    		{
    			bp = cp;
    		}
    		c = getc (fp);
    	}
    	*bp = '\0';
    	return;
    }

    /*====================================================================*
     *
     *   void discard (FILE * fp);
     *
     *   read and discard characters until end-of-line or end-of-file
     *   is detected; read the first character of next line if end of
     *   file has not been detected;
     *
     *--------------------------------------------------------------------*/

    static void discard (FILE * fp)

    {
    	while ((c != '\n') && (c != EOF))
    	{
    		c = getc (fp);
    	}
    	if (c != EOF)
    	{
    		c = getc (fp);
    	}
    	return;
    }

    /*====================================================================*
     *
     *   Const char * configstring (char const * file, char const * part, char const * item, char const * text)
     *
     *   open the named file, locate the named part and return the named
     *   item text, if present; return alternative text if the file part
     *   or item is missing; the calling function must preserve returned
     *   text because it may be over-written on successive calls;
     *
     *--------------------------------------------------------------------*/

    char const * configstring (char const * file, char const * part, char const * item, char const * text)

    {
    	FILE *fp;
    	if ((!file) || (!part) || (!item))
    	{
    		return (text);
    	}
    	if ((fp = fopen (file, "rb")))
    	{
    		for (c = getc (fp); c != EOF; discard (fp))
    		{
    			while (isblank (c))
    			{
    				c = getc (fp);
    			}
    			if (c != '[')
    			{
    				continue;
    			}
    			do
    			{
    				c = getc (fp);
    			}
    			while (isblank (c));
    			if (!compare (fp, part))
    			{
    				continue;
    			}
    			if (c != ']')
    			{
    				continue;
    			}
    			for (discard (fp); (c != '[') && (c != EOF); discard (fp))
    			{
    				while (isblank (c))
    				{
    					c = getc (fp);
    				}
    				if (c == ';')
    				{
    					continue;
    				}
    				if (!compare (fp, item))
    				{
    					continue;
    				}
    				if (c != '=')
    				{
    					continue;
    				}
    				do
    				{
    					c = getc (fp);
    				}
    				while (isblank (c));
    				collect (fp);
    				text = strdup (buffer);
    				break;
    			}
    			break;
    		}
    		fclose (fp);
    	}
    	return (text);
    }

    /*====================================================================*
     *
     *   unsigned confignumber (char const * file, char const * part, char const * item, unsigned number)
     *
     *   open the named file, locate the named part and return the named
     *   item as an unsigned integer, if present; return a default number
     *   if the file, part or item is missing;
     *
     *--------------------------------------------------------------------*/

    unsigned confignumber (char const * file, char const * part, char const * item, unsigned number)

    {
    	unsigned value = 0;
    	char const * string = configstring (file, part, item, "");
    	if ((string) && (* string))
    	{
    		while (isdigit (* string))
    		{
    			value *= 10;
    			value += * string++ - '0';
    		}
    		if (! (* string))
    		{
    			number = value;
    		}
    	}
    	return (number);
    }

    /*====================================================================*
     *
     *   unsigned confignumber_range (char const * file, char const * part, char const * item, unsigned number, unsigned min, unsigned max)
     *
     *   open the named file, locate the named part and return the named
     *   item as an unsigned integer, if present; return a default number
     *   if the file, part or item is missing or item is out of range;
     *
     *--------------------------------------------------------------------*/

    unsigned confignumber_range (char const * file, char const * part, char const * item, unsigned number, unsigned min, unsigned max)

    {
    	unsigned value = 0;
    	char const * string = configstring (file, part, item, "");
    	if ((string) && (* string))
    	{
    		while (isdigit (* string))
    		{
    			value *= 10;
    			value += * string++ - '0';
    		}
    		if (! (* string))
    		{
    			if ((value >= min) && (value <= max))
    			{
    			    number = value;
    			}
    		}
    	}
    	return (number);
    }

    /*====================================================================*
     *
     *   int main (int argc, char const * argv []);
     *
     *   demo/test program; arguments are file, part, item and text in
     *   that order; you can construct your own configuration file and
     *   observe behaviour;
     *
     *--------------------------------------------------------------------*/

    #if 0

    #include <stdio.h>

    int main (int argc, char const * argv [])

    {
    	char const * text = configstring (argv [1], argv [2], argv [3], argv [4]);
    	printf ("file=[%s] part=[%s] item=[%s] text=[%s]\n", argv [1], argv [2], argv [3], text);
    	return (0);
    }

    #endif

    /*====================================================================*
     *
     *--------------------------------------------------------------------*/

    #endif

[PREV] [HOME] [NEXT]