From fc145e3930071de7c13380abdbe2a6fe09234dc1 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 28 Sep 2015 21:45:50 +0200 Subject: [PATCH] Embed iniparser --- Makefile | 26 +- config.c | 2 +- iniparser/LICENSE | 21 + iniparser/dictionary.c | 380 +++++++++++++ dictionary.h => iniparser/dictionary.h | 16 +- iniparser/iniparser.c | 749 +++++++++++++++++++++++++ iniparser.h => iniparser/iniparser.h | 49 +- iouyap | Bin 0 -> 86611 bytes iouyap.c | 8 +- iouyap.h | 2 +- netmap.h | 1 + y.tab.h | 82 +++ 12 files changed, 1293 insertions(+), 43 deletions(-) create mode 100644 iniparser/LICENSE create mode 100644 iniparser/dictionary.c rename dictionary.h => iniparser/dictionary.h (95%) create mode 100644 iniparser/iniparser.c rename iniparser.h => iniparser/iniparser.h (88%) create mode 100755 iouyap create mode 100644 y.tab.h diff --git a/Makefile b/Makefile index f57503a..edea49f 100644 --- a/Makefile +++ b/Makefile @@ -18,37 +18,35 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -SHELL = /bin/sh -BINDIR = /usr/local/bin +NAME = iouyap -srcdir = . +BINDIR = /usr/local/bin CC = gcc #-O3 -CDEBUG = -g -DDEBUG -CFLAGS = $(CDEBUG) -Wall +CFLAGS = -Wall -LDLIBS = -liniparser -lpthread -LDFLAGS = +LDLIBS = -lpthread YACC = bison -y YFLAGS = -d LEX = flex -LFLAGS = -objects = netmap_parse.o netmap_scan.o netmap.o config.o iouyap.o +SRCDIR = .,iniparser + +OBJECTS = netmap_parse.o netmap_scan.o netmap.o config.o iouyap.o iniparser/iniparser.o iniparser/dictionary.o -all : iouyap +$(OBJECTS) = iouyap.h netmap.h config.h iniparser/iniparser.h iniparser/dictionary.h -iouyap : $(objects) +all: $(NAME) -$(objects) : iouyap.h netmap.h config.h +$(NAME): $(OBJECTS) .PHONY : clean clean : - -rm iouyap y.tab.* *.o + rm -f iouyap y.tab.* *.o */*.o -install : iouyap +install : $(NAME) chmod +x iouyap sudo cp iouyap $(BINDIR) sudo setcap cap_net_admin,cap_net_raw=ep iouyap diff --git a/config.c b/config.c index 81351a5..172ec49 100644 --- a/config.c +++ b/config.c @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -#include +#include "iniparser/iniparser.h" #include "config.h" diff --git a/iniparser/LICENSE b/iniparser/LICENSE new file mode 100644 index 0000000..5a3a80b --- /dev/null +++ b/iniparser/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2000-2011 by Nicolas Devillard. +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + diff --git a/iniparser/dictionary.c b/iniparser/dictionary.c new file mode 100644 index 0000000..9da3070 --- /dev/null +++ b/iniparser/dictionary.c @@ -0,0 +1,380 @@ +/*-------------------------------------------------------------------------*/ +/** + @file dictionary.c + @author N. Devillard + @brief Implements a dictionary for string variables. + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ +#include "dictionary.h" + +#include +#include +#include +#include + +/** Maximum value size for integers and doubles. */ +#define MAXVALSZ 1024 + +/** Minimal allocated number of entries in a dictionary */ +#define DICTMINSZ 128 + +/** Invalid key token */ +#define DICT_INVALID_KEY ((char*)-1) + +/*--------------------------------------------------------------------------- + Private functions + ---------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ +/** + @brief Duplicate a string + @param s String to duplicate + @return Pointer to a newly allocated string, to be freed with free() + + This is a replacement for strdup(). This implementation is provided + for systems that do not have it. + */ +/*--------------------------------------------------------------------------*/ +static char * xstrdup(const char * s) +{ + char * t ; + size_t len ; + if (!s) + return NULL ; + + len = strlen(s) + 1 ; + t = (char*) malloc(len) ; + if (t) { + memcpy(t, s, len) ; + } + return t ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Double the size of the dictionary + @param d Dictionary to grow + @return This function returns non-zero in case of failure + */ +/*--------------------------------------------------------------------------*/ +static int dictionary_grow(dictionary * d) +{ + char ** new_val ; + char ** new_key ; + unsigned * new_hash ; + + new_val = (char**) calloc(d->size * 2, sizeof *d->val); + new_key = (char**) calloc(d->size * 2, sizeof *d->key); + new_hash = (unsigned*) calloc(d->size * 2, sizeof *d->hash); + if (!new_val || !new_key || !new_hash) { + /* An allocation failed, leave the dictionary unchanged */ + if (new_val) + free(new_val); + if (new_key) + free(new_key); + if (new_hash) + free(new_hash); + return -1 ; + } + /* Initialize the newly allocated space */ + memcpy(new_val, d->val, d->size * sizeof(char *)); + memcpy(new_key, d->key, d->size * sizeof(char *)); + memcpy(new_hash, d->hash, d->size * sizeof(unsigned)); + /* Delete previous data */ + free(d->val); + free(d->key); + free(d->hash); + /* Actually update the dictionary */ + d->size *= 2 ; + d->val = new_val; + d->key = new_key; + d->hash = new_hash; + return 0 ; +} + +/*--------------------------------------------------------------------------- + Function codes + ---------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ +/** + @brief Compute the hash key for a string. + @param key Character string to use for key. + @return 1 unsigned int on at least 32 bits. + + This hash function has been taken from an Article in Dr Dobbs Journal. + This is normally a collision-free function, distributing keys evenly. + The key is stored anyway in the struct so that collision can be avoided + by comparing the key itself in last resort. + */ +/*--------------------------------------------------------------------------*/ +unsigned dictionary_hash(const char * key) +{ + size_t len ; + unsigned hash ; + size_t i ; + + if (!key) + return 0 ; + + len = strlen(key); + for (hash=0, i=0 ; i>6) ; + } + hash += (hash <<3); + hash ^= (hash >>11); + hash += (hash <<15); + return hash ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Create a new dictionary object. + @param size Optional initial size of the dictionary. + @return 1 newly allocated dictionary objet. + + This function allocates a new dictionary object of given size and returns + it. If you do not know in advance (roughly) the number of entries in the + dictionary, give size=0. + */ +/*----------CCC----------------------------------------------------------------*/ +dictionary * dictionary_new(size_t size) +{ + dictionary * d ; + + /* If no size was specified, allocate space for DICTMINSZ */ + if (sizesize = size ; + d->val = (char**) calloc(size, sizeof *d->val); + d->key = (char**) calloc(size, sizeof *d->key); + d->hash = (unsigned*) calloc(size, sizeof *d->hash); + } + return d ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a dictionary object + @param d dictionary object to deallocate. + @return void + + Deallocate a dictionary object and all memory associated to it. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_del(dictionary * d) +{ + ssize_t i ; + + if (d==NULL) return ; + for (i=0 ; isize ; i++) { + if (d->key[i]!=NULL) + free(d->key[i]); + if (d->val[i]!=NULL) + free(d->val[i]); + } + free(d->val); + free(d->key); + free(d->hash); + free(d); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get a value from a dictionary. + @param d dictionary object to search. + @param key Key to look for in the dictionary. + @param def Default value to return if key not found. + @return 1 pointer to internally allocated character string. + + This function locates a key in a dictionary and returns a pointer to its + value, or the passed 'def' pointer if no such key can be found in + dictionary. The returned character pointer points to data internal to the + dictionary object, you should not try to free it or modify it. + */ +/*--------------------------------------------------------------------------*/ +const char * dictionary_get(const dictionary * d, const char * key, const char * def) +{ + unsigned hash ; + ssize_t i ; + + hash = dictionary_hash(key); + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + /* Compare hash */ + if (hash==d->hash[i]) { + /* Compare string, to avoid hash collisions */ + if (!strcmp(key, d->key[i])) { + return d->val[i] ; + } + } + } + return def ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Set a value in a dictionary. + @param d dictionary object to modify. + @param key Key to modify or add. + @param val Value to add. + @return int 0 if Ok, anything else otherwise + + If the given key is found in the dictionary, the associated value is + replaced by the provided one. If the key cannot be found in the + dictionary, it is added to it. + + It is Ok to provide a NULL value for val, but NULL values for the dictionary + or the key are considered as errors: the function will return immediately + in such a case. + + Notice that if you dictionary_set a variable to NULL, a call to + dictionary_get will return a NULL value: the variable will be found, and + its value (NULL) is returned. In other words, setting the variable + content to NULL is equivalent to deleting the variable from the + dictionary. It is not possible (in this implementation) to have a key in + the dictionary without value. + + This function returns non-zero in case of failure. + */ +/*--------------------------------------------------------------------------*/ +int dictionary_set(dictionary * d, const char * key, const char * val) +{ + ssize_t i ; + unsigned hash ; + + if (d==NULL || key==NULL) return -1 ; + + /* Compute hash for this key */ + hash = dictionary_hash(key) ; + /* Find if value is already in dictionary */ + if (d->n>0) { + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + if (hash==d->hash[i]) { /* Same hash value */ + if (!strcmp(key, d->key[i])) { /* Same key */ + /* Found a value: modify and return */ + if (d->val[i]!=NULL) + free(d->val[i]); + d->val[i] = (val ? xstrdup(val) : NULL); + /* Value has been modified: return */ + return 0 ; + } + } + } + } + /* Add a new value */ + /* See if dictionary needs to grow */ + if (d->n==d->size) { + /* Reached maximum size: reallocate dictionary */ + if (dictionary_grow(d) != 0) + return -1; + } + + /* Insert key in the first empty slot. Start at d->n and wrap at + d->size. Because d->n < d->size this will necessarily + terminate. */ + for (i=d->n ; d->key[i] ; ) { + if(++i == d->size) i = 0; + } + /* Copy key */ + d->key[i] = xstrdup(key); + d->val[i] = (val ? xstrdup(val) : NULL) ; + d->hash[i] = hash; + d->n ++ ; + return 0 ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a key in a dictionary + @param d dictionary object to modify. + @param key Key to remove. + @return void + + This function deletes a key in a dictionary. Nothing is done if the + key cannot be found. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_unset(dictionary * d, const char * key) +{ + unsigned hash ; + ssize_t i ; + + if (key == NULL || d == NULL) { + return; + } + + hash = dictionary_hash(key); + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + /* Compare hash */ + if (hash==d->hash[i]) { + /* Compare string, to avoid hash collisions */ + if (!strcmp(key, d->key[i])) { + /* Found key */ + break ; + } + } + } + if (i>=d->size) + /* Key not found */ + return ; + + free(d->key[i]); + d->key[i] = NULL ; + if (d->val[i]!=NULL) { + free(d->val[i]); + d->val[i] = NULL ; + } + d->hash[i] = 0 ; + d->n -- ; + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump + @param f Opened file pointer. + @return void + + Dumps a dictionary onto an opened file pointer. Key pairs are printed out + as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as + output file pointers. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_dump(const dictionary * d, FILE * out) +{ + ssize_t i ; + + if (d==NULL || out==NULL) return ; + if (d->n<1) { + fprintf(out, "empty dictionary\n"); + return ; + } + for (i=0 ; isize ; i++) { + if (d->key[i]) { + fprintf(out, "%20s\t[%s]\n", + d->key[i], + d->val[i] ? d->val[i] : "UNDEF"); + } + } + return ; +} diff --git a/dictionary.h b/iniparser/dictionary.h similarity index 95% rename from dictionary.h rename to iniparser/dictionary.h index 34c4b82..d04b6ce 100644 --- a/dictionary.h +++ b/iniparser/dictionary.h @@ -23,6 +23,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /*--------------------------------------------------------------------------- New types ---------------------------------------------------------------------------*/ @@ -40,7 +44,7 @@ /*-------------------------------------------------------------------------*/ typedef struct _dictionary_ { int n ; /** Number of entries in dictionary */ - int size ; /** Storage size */ + ssize_t size ; /** Storage size */ char ** val ; /** List of string values */ char ** key ; /** List of string keys */ unsigned * hash ; /** List of hash values for keys */ @@ -76,7 +80,7 @@ unsigned dictionary_hash(const char * key); dictionary, give size=0. */ /*--------------------------------------------------------------------------*/ -dictionary * dictionary_new(int size); +dictionary * dictionary_new(size_t size); /*-------------------------------------------------------------------------*/ /** @@ -103,7 +107,7 @@ void dictionary_del(dictionary * vd); dictionary object, you should not try to free it or modify it. */ /*--------------------------------------------------------------------------*/ -char * dictionary_get(dictionary * d, const char * key, char * def); +const char * dictionary_get(const dictionary * d, const char * key, const char * def); /*-------------------------------------------------------------------------*/ @@ -160,6 +164,10 @@ void dictionary_unset(dictionary * d, const char * key); output file pointers. */ /*--------------------------------------------------------------------------*/ -void dictionary_dump(dictionary * d, FILE * out); +void dictionary_dump(const dictionary * d, FILE * out); + +#ifdef __cplusplus +} +#endif #endif diff --git a/iniparser/iniparser.c b/iniparser/iniparser.c new file mode 100644 index 0000000..1eb1004 --- /dev/null +++ b/iniparser/iniparser.c @@ -0,0 +1,749 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file iniparser.c + @author N. Devillard + @brief Parser for ini files. +*/ +/*--------------------------------------------------------------------------*/ +/*---------------------------- Includes ------------------------------------*/ +#include +#include "iniparser.h" + +/*---------------------------- Defines -------------------------------------*/ +#define ASCIILINESZ (1024) +#define INI_INVALID_KEY ((char*)-1) + +/*--------------------------------------------------------------------------- + Private to this module + ---------------------------------------------------------------------------*/ +/** + * This enum stores the status for each parsed line (internal use only). + */ +typedef enum _line_status_ { + LINE_UNPROCESSED, + LINE_ERROR, + LINE_EMPTY, + LINE_COMMENT, + LINE_SECTION, + LINE_VALUE +} line_status ; + +/*-------------------------------------------------------------------------*/ +/** + @brief Convert a string to lowercase. + @param in String to convert. + @param out Output buffer. + @param len Size of the out buffer. + @return ptr to the out buffer or NULL if an error occured. + + This function convert a string into lowercase. + At most len - 1 elements of the input string will be converted. + */ +/*--------------------------------------------------------------------------*/ +static const char * strlwc(const char * in, char *out, unsigned len) +{ + unsigned i ; + + if (in==NULL || out == NULL || len==0) return NULL ; + i=0 ; + while (in[i] != '\0' && i < len-1) { + out[i] = (char)tolower((int)in[i]); + i++ ; + } + out[i] = '\0'; + return out ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Copy string in a newly mallocced area + @param str String to copy. + @return str Copied version of the given string allocated with malloc + + Original strdup is not portable, need to implement our own + */ +/*--------------------------------------------------------------------------*/ +static char * _strdup(const char *s) +{ + char * copy = (char*) malloc(strlen(s)); + strcpy(copy, s); + return copy ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Remove blanks at the beginning and the end of a string. + @param str String to parse and alter. + @return unsigned New size of the string. + */ +/*--------------------------------------------------------------------------*/ +unsigned strstrip(char * s) +{ + char *last = NULL ; + char *dest = s; + + if (s==NULL) return 0; + + last = s + strlen(s); + while (isspace((int)*s) && *s) s++; + while (last > s) { + if (!isspace((int)*(last-1))) + break ; + last -- ; + } + *last = (char)0; + + memmove(dest,s,last - s + 1); + return last - s; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get number of sections in a dictionary + @param d Dictionary to examine + @return int Number of sections found in dictionary + + This function returns the number of sections found in a dictionary. + The test to recognize sections is done on the string stored in the + dictionary: a section name is given as "section" whereas a key is + stored as "section:key", thus the test looks for entries that do not + contain a colon. + + This clearly fails in the case a section name contains a colon, but + this should simply be avoided. + + This function returns -1 in case of error. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getnsec(const dictionary * d) +{ + int i ; + int nsec ; + + if (d==NULL) return -1 ; + nsec=0 ; + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + if (strchr(d->key[i], ':')==NULL) { + nsec ++ ; + } + } + return nsec ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get name for section n in a dictionary. + @param d Dictionary to examine + @param n Section number (from 0 to nsec-1). + @return Pointer to char string + + This function locates the n-th section in a dictionary and returns + its name as a pointer to a string statically allocated inside the + dictionary. Do not free or modify the returned string! + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ +const char * iniparser_getsecname(const dictionary * d, int n) +{ + int i ; + int foundsec ; + + if (d==NULL || n<0) return NULL ; + foundsec=0 ; + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + if (strchr(d->key[i], ':')==NULL) { + foundsec++ ; + if (foundsec>n) + break ; + } + } + if (foundsec<=n) { + return NULL ; + } + return d->key[i] ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump. + @param f Opened file pointer to dump to. + @return void + + This function prints out the contents of a dictionary, one element by + line, onto the provided file pointer. It is OK to specify @c stderr + or @c stdout as output files. This function is meant for debugging + purposes mostly. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump(const dictionary * d, FILE * f) +{ + int i ; + + if (d==NULL || f==NULL) return ; + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + if (d->val[i]!=NULL) { + fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); + } else { + fprintf(f, "[%s]=UNDEF\n", d->key[i]); + } + } + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary to a loadable ini file + @param d Dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given dictionary into a loadable ini file. + It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump_ini(const dictionary * d, FILE * f) +{ + int i ; + int nsec ; + const char * secname ; + + if (d==NULL || f==NULL) return ; + + nsec = iniparser_getnsec(d); + if (nsec<1) { + /* No section in file: dump all keys as they are */ + for (i=0 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + fprintf(f, "%s = %s\n", d->key[i], d->val[i]); + } + return ; + } + for (i=0 ; isize ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, seclen+1)) { + fprintf(f, + "%-30s = %s\n", + d->key[j]+seclen+1, + d->val[j] ? d->val[j] : ""); + } + } + fprintf(f, "\n"); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @return Number of keys in section + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getsecnkeys(const dictionary * d, const char * s) +{ + int seclen, nkeys ; + char keym[ASCIILINESZ+1]; + int j ; + + nkeys = 0; + + if (d==NULL) return nkeys; + if (! iniparser_find_entry(d, s)) return nkeys; + + seclen = (int)strlen(s); + sprintf(keym, "%s:", s); + + for (j=0 ; jsize ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, seclen+1)) + nkeys++; + } + + return nkeys; + +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @param keys Already allocated array to store the keys in + @return The pointer passed as `keys` argument or NULL in case of error + + This function queries a dictionary and finds all keys in a given section. + The keys argument should be an array of pointers which size has been + determined by calling `iniparser_getsecnkeys` function prior to this one. + + Each pointer in the returned char pointer-to-pointer is pointing to + a string allocated in the dictionary; do not free or modify them. + */ +/*--------------------------------------------------------------------------*/ +const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys) +{ + int i, j, seclen ; + char keym[ASCIILINESZ+1]; + + if (d==NULL || keys==NULL) return NULL; + if (! iniparser_find_entry(d, s)) return NULL; + + seclen = (int)strlen(s); + sprintf(keym, "%s:", s); + + i = 0; + + for (j=0 ; jsize ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, seclen+1)) { + keys[i] = d->key[j]; + i++; + } + } + + return keys; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key + @param d Dictionary to search + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +const char * iniparser_getstring(const dictionary * d, const char * key, const char * def) +{ + const char * lc_key ; + const char * sval ; + char tmp_str[ASCIILINESZ+1]; + + if (d==NULL || key==NULL) + return def ; + + lc_key = strlwc(key, tmp_str, sizeof(tmp_str)); + sval = dictionary_get(d, lc_key, def); + return sval ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + "42" -> 42 + "042" -> 34 (octal -> decimal) + "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getint(const dictionary * d, const char * key, int notfound) +{ + const char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return (int)strtol(str, NULL, 0); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a double + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +double iniparser_getdouble(const dictionary * d, const char * key, double notfound) +{ + const char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return atof(str); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a boolean + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getboolean(const dictionary * d, const char * key, int notfound) +{ + int ret ; + const char * c ; + + c = iniparser_getstring(d, key, INI_INVALID_KEY); + if (c==INI_INVALID_KEY) return notfound ; + if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { + ret = 1 ; + } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { + ret = 0 ; + } else { + ret = notfound ; + } + return ret; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Finds out if a given entry exists in a dictionary + @param ini Dictionary to search + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_find_entry(const dictionary * ini, const char * entry) +{ + int found=0 ; + if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { + found = 1 ; + } + return found ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Set an entry in a dictionary. + @param ini Dictionary to modify. + @param entry Entry to modify (entry name) + @param val New value to associate to the entry. + @return int 0 if Ok, -1 otherwise. + + If the given entry can be found in the dictionary, it is modified to + contain the provided value. If it cannot be found, the entry is created. + It is Ok to set val to NULL. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_set(dictionary * ini, const char * entry, const char * val) +{ + char tmp_str[ASCIILINESZ+1]; + return dictionary_set(ini, strlwc(entry, tmp_str, sizeof(tmp_str)), val) ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete an entry in a dictionary + @param ini Dictionary to modify + @param entry Entry to delete (entry name) + @return void + + If the given entry can be found, it is deleted from the dictionary. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_unset(dictionary * ini, const char * entry) +{ + char tmp_str[ASCIILINESZ+1]; + dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str))); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Load a single line from an INI file + @param input_line Input line, may be concatenated multi-line input + @param section Output space to store section + @param key Output space to store key + @param value Output space to store value + @return line_status value + */ +/*--------------------------------------------------------------------------*/ +static line_status iniparser_line( + const char * input_line, + char * section, + char * key, + char * value) +{ + line_status sta ; + char * line = NULL; + size_t len ; + + line = _strdup(input_line); + len = strstrip(line); + + sta = LINE_UNPROCESSED ; + if (len<1) { + /* Empty line */ + sta = LINE_EMPTY ; + } else if (line[0]=='#' || line[0]==';') { + /* Comment line */ + sta = LINE_COMMENT ; + } else if (line[0]=='[' && line[len-1]==']') { + /* Section name */ + sscanf(line, "[%[^]]", section); + strstrip(section); + strlwc(section, section, len); + sta = LINE_SECTION ; + } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 + || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2 + || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { + /* Usual key=value, with or without comments */ + strstrip(key); + strlwc(key, key, len); + strstrip(value); + /* + * sscanf cannot handle '' or "" as empty values + * this is done here + */ + if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { + value[0]=0 ; + } + sta = LINE_VALUE ; + } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 + || sscanf(line, "%[^=] %[=]", key, value) == 2) { + /* + * Special cases: + * key= + * key=; + * key=# + */ + strstrip(key); + strlwc(key, key, len); + value[0]=0 ; + sta = LINE_VALUE ; + } else { + /* Generate syntax error */ + sta = LINE_ERROR ; + } + + free(line); + return sta ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Parse an ini file and return an allocated dictionary object + @param ininame Name of the ini file to read. + @return Pointer to newly allocated dictionary + + This is the parser for ini files. This function is called, providing + the name of the file to be read. It returns a dictionary object that + should not be accessed directly, but through accessor functions + instead. + + The returned dictionary must be freed using iniparser_freedict(). + */ +/*--------------------------------------------------------------------------*/ +dictionary * iniparser_load(const char * ininame) +{ + FILE * in ; + + char line [ASCIILINESZ+1] ; + char section [ASCIILINESZ+1] ; + char key [ASCIILINESZ+1] ; + char tmp [(ASCIILINESZ * 2) + 1] ; + char val [ASCIILINESZ+1] ; + + int last=0 ; + int len ; + int lineno=0 ; + int errs=0; + + dictionary * dict ; + + if ((in=fopen(ininame, "r"))==NULL) { + fprintf(stderr, "iniparser: cannot open %s\n", ininame); + return NULL ; + } + + dict = dictionary_new(0) ; + if (!dict) { + fclose(in); + return NULL ; + } + + memset(line, 0, ASCIILINESZ); + memset(section, 0, ASCIILINESZ); + memset(key, 0, ASCIILINESZ); + memset(val, 0, ASCIILINESZ); + last=0 ; + + while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { + lineno++ ; + len = (int)strlen(line)-1; + if (len==0) + continue; + /* Safety check against buffer overflows */ + if (line[len]!='\n' && !feof(in)) { + fprintf(stderr, + "iniparser: input line too long in %s (%d)\n", + ininame, + lineno); + dictionary_del(dict); + fclose(in); + return NULL ; + } + /* Get rid of \n and spaces at end of line */ + while ((len>=0) && + ((line[len]=='\n') || (isspace(line[len])))) { + line[len]=0 ; + len-- ; + } + if (len < 0) { /* Line was entirely \n and/or spaces */ + len = 0; + } + /* Detect multi-line */ + if (line[len]=='\\') { + /* Multi-line value */ + last=len ; + continue ; + } else { + last=0 ; + } + switch (iniparser_line(line, section, key, val)) { + case LINE_EMPTY: + case LINE_COMMENT: + break ; + + case LINE_SECTION: + errs = dictionary_set(dict, section, NULL); + break ; + + case LINE_VALUE: + sprintf(tmp, "%s:%s", section, key); + errs = dictionary_set(dict, tmp, val) ; + break ; + + case LINE_ERROR: + fprintf(stderr, "iniparser: syntax error in %s (%d):\n", + ininame, + lineno); + fprintf(stderr, "-> %s\n", line); + errs++ ; + break; + + default: + break ; + } + memset(line, 0, ASCIILINESZ); + last=0; + if (errs<0) { + fprintf(stderr, "iniparser: memory allocation failure\n"); + break ; + } + } + if (errs) { + dictionary_del(dict); + dict = NULL ; + } + fclose(in); + return dict ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Free all memory associated to an ini dictionary + @param d Dictionary to free + @return void + + Free all memory associated to an ini dictionary. + It is mandatory to call this function before the dictionary object + gets out of the current context. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_freedict(dictionary * d) +{ + dictionary_del(d); +} diff --git a/iniparser.h b/iniparser/iniparser.h similarity index 88% rename from iniparser.h rename to iniparser/iniparser.h index ecd73bf..0a9da72 100644 --- a/iniparser.h +++ b/iniparser/iniparser.h @@ -27,6 +27,10 @@ #include "dictionary.h" +#ifdef __cplusplus +extern "C" { +#endif + /*-------------------------------------------------------------------------*/ /** @brief Get number of sections in a dictionary @@ -46,7 +50,7 @@ */ /*--------------------------------------------------------------------------*/ -int iniparser_getnsec(dictionary * d); +int iniparser_getnsec(const dictionary * d); /*-------------------------------------------------------------------------*/ @@ -64,7 +68,7 @@ int iniparser_getnsec(dictionary * d); */ /*--------------------------------------------------------------------------*/ -char * iniparser_getsecname(dictionary * d, int n); +const char * iniparser_getsecname(const dictionary * d, int n); /*-------------------------------------------------------------------------*/ @@ -79,7 +83,7 @@ char * iniparser_getsecname(dictionary * d, int n); */ /*--------------------------------------------------------------------------*/ -void iniparser_dump_ini(dictionary * d, FILE * f); +void iniparser_dump_ini(const dictionary * d, FILE * f); /*-------------------------------------------------------------------------*/ /** @@ -94,7 +98,7 @@ void iniparser_dump_ini(dictionary * d, FILE * f); */ /*--------------------------------------------------------------------------*/ -void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f); +void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f); /*-------------------------------------------------------------------------*/ /** @@ -109,7 +113,7 @@ void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f); purposes mostly. */ /*--------------------------------------------------------------------------*/ -void iniparser_dump(dictionary * d, FILE * f); +void iniparser_dump(const dictionary * d, FILE * f); /*-------------------------------------------------------------------------*/ /** @@ -119,23 +123,26 @@ void iniparser_dump(dictionary * d, FILE * f); @return Number of keys in section */ /*--------------------------------------------------------------------------*/ -int iniparser_getsecnkeys(dictionary * d, char * s); +int iniparser_getsecnkeys(const dictionary * d, const char * s); /*-------------------------------------------------------------------------*/ /** @brief Get the number of keys in a section of a dictionary. - @param d Dictionary to examine - @param s Section name of dictionary to examine - @return pointer to statically allocated character strings + @param d Dictionary to examine + @param s Section name of dictionary to examine + @param keys Already allocated array to store the keys in + @return The pointer passed as `keys` argument or NULL in case of error This function queries a dictionary and finds all keys in a given section. + The keys argument should be an array of pointers which size has been + determined by calling `iniparser_getsecnkeys` function prior to this one. + Each pointer in the returned char pointer-to-pointer is pointing to a string allocated in the dictionary; do not free or modify them. - - This function returns NULL in case of error. */ /*--------------------------------------------------------------------------*/ -char ** iniparser_getseckeys(dictionary * d, char * s); +const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys); + /*-------------------------------------------------------------------------*/ /** @@ -152,7 +159,7 @@ char ** iniparser_getseckeys(dictionary * d, char * s); the dictionary, do not free or modify it. */ /*--------------------------------------------------------------------------*/ -char * iniparser_getstring(dictionary * d, const char * key, char * def); +const char * iniparser_getstring(const dictionary * d, const char * key, const char * def); /*-------------------------------------------------------------------------*/ /** @@ -181,7 +188,7 @@ char * iniparser_getstring(dictionary * d, const char * key, char * def); Credits: Thanks to A. Becker for suggesting strtol() */ /*--------------------------------------------------------------------------*/ -int iniparser_getint(dictionary * d, const char * key, int notfound); +int iniparser_getint(const dictionary * d, const char * key, int notfound); /*-------------------------------------------------------------------------*/ /** @@ -196,7 +203,7 @@ int iniparser_getint(dictionary * d, const char * key, int notfound); the notfound value is returned. */ /*--------------------------------------------------------------------------*/ -double iniparser_getdouble(dictionary * d, const char * key, double notfound); +double iniparser_getdouble(const dictionary * d, const char * key, double notfound); /*-------------------------------------------------------------------------*/ /** @@ -230,7 +237,7 @@ double iniparser_getdouble(dictionary * d, const char * key, double notfound); necessarily have to be 0 or 1. */ /*--------------------------------------------------------------------------*/ -int iniparser_getboolean(dictionary * d, const char * key, int notfound); +int iniparser_getboolean(const dictionary * d, const char * key, int notfound); /*-------------------------------------------------------------------------*/ @@ -239,10 +246,10 @@ int iniparser_getboolean(dictionary * d, const char * key, int notfound); @param ini Dictionary to modify. @param entry Entry to modify (entry name) @param val New value to associate to the entry. - @return int 0 if Ok, -1 otherwise. + @return int 0 if Ok, -1 otherwise. If the given entry can be found in the dictionary, it is modified to - contain the provided value. If it cannot be found, -1 is returned. + contain the provided value. If it cannot be found, the entry is created. It is Ok to set val to NULL. */ /*--------------------------------------------------------------------------*/ @@ -273,7 +280,7 @@ void iniparser_unset(dictionary * ini, const char * entry); of querying for the presence of sections in a dictionary. */ /*--------------------------------------------------------------------------*/ -int iniparser_find_entry(dictionary * ini, const char * entry) ; +int iniparser_find_entry(const dictionary * ini, const char * entry) ; /*-------------------------------------------------------------------------*/ /** @@ -304,4 +311,8 @@ dictionary * iniparser_load(const char * ininame); /*--------------------------------------------------------------------------*/ void iniparser_freedict(dictionary * d); +#ifdef __cplusplus +} +#endif + #endif diff --git a/iouyap b/iouyap new file mode 100755 index 0000000000000000000000000000000000000000..5af6bd8317bcc71309ff8a404ccccd5b742d258e GIT binary patch literal 86611 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 #include #include -#include #include #include #include @@ -47,6 +46,7 @@ #include #include "iouyap.h" +#include "iniparser/iniparser.h" #include "netmap.h" #include "config.h" @@ -540,7 +540,7 @@ iou_listener (void *arg) bytes_received, port, sfd); if (bytes_received <= IOU_HDR_SIZE) - continue; + continue; /* Send on the packet, minus the IOU header */ bytes_received -= IOU_HDR_SIZE; @@ -776,7 +776,7 @@ open_pcap_file (foreign_port_t * port, char *file, int no_hdr, int overwrite) if ((fd = open (file, O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR)) < 0) fatal_error ("open"); - + if( getuid() != geteuid() ) if( fchown(fd, getuid (), -1) ) fatal_error ("fchown"); @@ -1032,7 +1032,7 @@ open_iou_uds () unlink (sock_addr.sun_path); if (bind (sfd, (struct sockaddr *) &sock_addr, sizeof sock_addr)) fatal_error ("bind"); - + if( getuid() != geteuid() ) if( chown(sock_addr.sun_path, getuid (), -1) ) fatal_error ("chown"); diff --git a/iouyap.h b/iouyap.h index f357308..be9f05f 100644 --- a/iouyap.h +++ b/iouyap.h @@ -26,7 +26,7 @@ #include #define NAME "iouyap" -#define VERSION "0.95.0" +#define VERSION "0.96.0" #define CONFIG_FILE "iouyap.ini" #define NETMAP_FILE "NETMAP" diff --git a/netmap.h b/netmap.h index 3333880..98bbdad 100644 --- a/netmap.h +++ b/netmap.h @@ -21,6 +21,7 @@ #ifndef NETMAP_H_ #define NETMAP_H_ +#include #include "iouyap.h" diff --git a/y.tab.h b/y.tab.h new file mode 100644 index 0000000..47743fc --- /dev/null +++ b/y.tab.h @@ -0,0 +1,82 @@ +/* A Bison parser, made by GNU Bison 3.0.2. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + + 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 3 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, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_YY_Y_TAB_H_INCLUDED +# define YY_YY_Y_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + INT = 258, + ADDRESS = 259, + ENDSEG = 260, + ENDNODE = 261 + }; +#endif +/* Tokens. */ +#define INT 258 +#define ADDRESS 259 +#define ENDSEG 260 +#define ENDNODE 261 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE YYSTYPE; +union YYSTYPE +{ +#line 55 "netmap_parse.y" /* yacc.c:1909 */ + + int ival; + char *sval; + char pval[64]; + +#line 72 "y.tab.h" /* yacc.c:1909 */ +}; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + +int yyparse (void); + +#endif /* !YY_YY_Y_TAB_H_INCLUDED */