Path: blob/master/dependencies/all/iniparser/iniparser.cpp
774 views
/*-------------------------------------------------------------------------*/1/**2@file iniparser.c3@author N. Devillard4@brief Parser for ini files.5*/6/*--------------------------------------------------------------------------*/7/*---------------------------- Includes ------------------------------------*/8#include <ctype.h>9#include <stdarg.h>10#include "iniparser.h"11#include "RSDK/Core/RetroEngine.hpp"1213/*---------------------------- Defines -------------------------------------*/14#define ASCIILINESZ (1024)15#define INI_INVALID_KEY ((char*)-1)1617/*---------------------------------------------------------------------------18Private to this module19---------------------------------------------------------------------------*/20/**21* This enum stores the status for each parsed line (internal use only).22*/23typedef enum _line_status_ {24LINE_UNPROCESSED,25LINE_ERROR,26LINE_EMPTY,27LINE_COMMENT,28LINE_SECTION,29LINE_VALUE30} line_status ;3132/*-------------------------------------------------------------------------*/33/**34@brief Convert a string to lowercase.35@param in String to convert.36@param out Output buffer.37@param len Size of the out buffer.38@return ptr to the out buffer or NULL if an error occured.3940This function convert a string into lowercase.41At most len - 1 elements of the input string will be converted.42*/43/*--------------------------------------------------------------------------*/44static const char * strlwc(const char * in, char *out, unsigned len)45{46unsigned i ;47//modified to NOT tolower4849if (in==NULL || out == NULL || len==0) return NULL ;50i=0 ;51while (in[i] != '\0' && i < len-1) {52//out[i] = (char)tolower((int)in[i]);53out[i] = in[i];54i++ ;55}56out[i] = '\0';57return out ;58}5960/*-------------------------------------------------------------------------*/61/**62@brief Duplicate a string63@param s String to duplicate64@return Pointer to a newly allocated string, to be freed with free()6566This is a replacement for strdup(). This implementation is provided67for systems that do not have it.68*/69/*--------------------------------------------------------------------------*/70static char * xstrdup(const char * s)71{72char * t ;73size_t len ;74if (!s)75return NULL ;7677len = strlen(s) + 1 ;78t = (char*) malloc(len) ;79if (t) {80memcpy(t, s, len) ;81}82return t ;83}8485static unsigned strstrip(char * s)86{87char *last = NULL ;88char *dest = s;8990if (s==NULL) return 0;9192last = s + strlen(s);93while (isspace((int)*s) && *s) s++;94while (last > s) {95if (!isspace((int)*(last-1)))96break ;97last -- ;98}99*last = (char)0;100101memmove(dest,s,last - s + 1);102return (unsigned)(last - s);103}104105static int default_error_callback(const char *format, ...)106{107int ret;108va_list argptr;109va_start(argptr, format);110ret = vfprintf(stderr, format, argptr);111va_end(argptr);112return ret;113}114115static int (*iniparser_error_callback)(const char*, ...) = default_error_callback;116117void iniparser_set_error_callback(int (*errback)(const char *, ...))118{119if (errback) {120iniparser_error_callback = errback;121} else {122iniparser_error_callback = default_error_callback;123}124}125126/*-------------------------------------------------------------------------*/127/**128@brief Get number of sections in a dictionary129@param d Dictionary to examine130@return int Number of sections found in dictionary131132This function returns the number of sections found in a dictionary.133The test to recognize sections is done on the string stored in the134dictionary: a section name is given as "section" whereas a key is135stored as "section:key", thus the test looks for entries that do not136contain a colon.137138This clearly fails in the case a section name contains a colon, but139this should simply be avoided.140141This function returns -1 in case of error.142*/143/*--------------------------------------------------------------------------*/144int iniparser_getnsec(const dictionary * d)145{146int i ;147int nsec ;148149if (d==NULL) return -1 ;150nsec=0 ;151for (i=0 ; i<d->size ; i++) {152if (d->key[i]==NULL)153continue ;154if (strchr(d->key[i], ':')==NULL) {155nsec ++ ;156}157}158return nsec ;159}160161/*-------------------------------------------------------------------------*/162/**163@brief Get name for section n in a dictionary.164@param d Dictionary to examine165@param n Section number (from 0 to nsec-1).166@return Pointer to char string167168This function locates the n-th section in a dictionary and returns169its name as a pointer to a string statically allocated inside the170dictionary. Do not free or modify the returned string!171172This function returns NULL in case of error.173*/174/*--------------------------------------------------------------------------*/175const char * iniparser_getsecname(const dictionary * d, int n)176{177int i ;178int foundsec ;179180if (d==NULL || n<0) return NULL ;181foundsec=0 ;182for (i=0 ; i<d->size ; i++) {183if (d->key[i]==NULL)184continue ;185if (strchr(d->key[i], ':')==NULL) {186foundsec++ ;187if (foundsec>n)188break ;189}190}191if (foundsec<=n) {192return NULL ;193}194return d->key[i] ;195}196197void iniparser_dump(const dictionary * d, FILE * f)198{199int i ;200201if (d==NULL || f==NULL) return ;202for (i=0 ; i<d->size ; i++) {203if (d->key[i]==NULL)204continue ;205if (d->val[i]!=NULL) {206fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);207} else {208fprintf(f, "[%s]=UNDEF\n", d->key[i]);209}210}211return ;212}213214void iniparser_dump_ini(const dictionary * d, FILE * f)215{216int i ;217int nsec ;218const char * secname ;219220if (d==NULL || f==NULL) return ;221222nsec = iniparser_getnsec(d);223if (nsec<1) {224/* No section in file: dump all keys as they are */225for (i=0 ; i<d->size ; i++) {226if (d->key[i]==NULL)227continue ;228fprintf(f, "%s = %s\n", d->key[i], d->val[i]);229}230return ;231}232for (i=0 ; i<nsec ; i++) {233secname = iniparser_getsecname(d, i) ;234iniparser_dumpsection_ini(d, secname, f);235}236fprintf(f, "\n");237return ;238}239240void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f)241{242int j ;243char keym[ASCIILINESZ+1];244int seclen ;245246if (d==NULL || f==NULL) return ;247if (! iniparser_find_entry(d, s)) return ;248249seclen = (int)strlen(s);250fprintf(f, "\n[%s]\n", s);251sprintf(keym, "%s:", s);252for (j=0 ; j<d->size ; j++) {253if (d->key[j]==NULL)254continue ;255if (!strncmp(d->key[j], keym, seclen+1)) {256fprintf(f,257"%-30s = %s\n",258d->key[j]+seclen+1,259d->val[j] ? d->val[j] : "");260}261}262fprintf(f, "\n");263return ;264}265266/*-------------------------------------------------------------------------*/267/**268@brief Get the number of keys in a section of a dictionary.269@param d Dictionary to examine270@param s Section name of dictionary to examine271@return Number of keys in section272*/273/*--------------------------------------------------------------------------*/274int iniparser_getsecnkeys(const dictionary * d, const char * s)275{276int seclen, nkeys ;277char keym[ASCIILINESZ+1];278int j ;279280nkeys = 0;281282if (d==NULL) return nkeys;283if (! iniparser_find_entry(d, s)) return nkeys;284285seclen = (int)strlen(s);286strlwc(s, keym, sizeof(keym));287keym[seclen] = ':';288289for (j=0 ; j<d->size ; j++) {290if (d->key[j]==NULL)291continue ;292if (!strncmp(d->key[j], keym, seclen+1))293nkeys++;294}295296return nkeys;297298}299300/*-------------------------------------------------------------------------*/301/**302@brief Get the number of keys in a section of a dictionary.303@param d Dictionary to examine304@param s Section name of dictionary to examine305@param keys Already allocated array to store the keys in306@return The pointer passed as `keys` argument or NULL in case of error307308This function queries a dictionary and finds all keys in a given section.309The keys argument should be an array of pointers which size has been310determined by calling `iniparser_getsecnkeys` function prior to this one.311312Each pointer in the returned char pointer-to-pointer is pointing to313a string allocated in the dictionary; do not free or modify them.314*/315/*--------------------------------------------------------------------------*/316const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys)317{318int i, j, seclen ;319char keym[ASCIILINESZ+1];320321if (d==NULL || keys==NULL) return NULL;322if (! iniparser_find_entry(d, s)) return NULL;323324seclen = (int)strlen(s);325strlwc(s, keym, sizeof(keym));326keym[seclen] = ':';327328i = 0;329330for (j=0 ; j<d->size ; j++) {331if (d->key[j]==NULL)332continue ;333if (!strncmp(d->key[j], keym, seclen+1)) {334keys[i] = d->key[j];335i++;336}337}338339return keys;340}341342/*-------------------------------------------------------------------------*/343/**344@brief Get the string associated to a key345@param d Dictionary to search346@param key Key string to look for347@param def Default value to return if key not found.348@return pointer to statically allocated character string349350This function queries a dictionary for a key. A key as read from an351ini file is given as "section:key". If the key cannot be found,352the pointer passed as 'def' is returned.353The returned char pointer is pointing to a string allocated in354the dictionary, do not free or modify it.355*/356/*--------------------------------------------------------------------------*/357const char * iniparser_getstring(const dictionary * d, const char * key, const char * def)358{359const char * lc_key ;360const char * sval ;361char tmp_str[ASCIILINESZ+1];362363if (d==NULL || key==NULL)364return def ;365366lc_key = strlwc(key, tmp_str, sizeof(tmp_str));367sval = dictionary_get(d, lc_key, def);368return sval ;369}370371/*-------------------------------------------------------------------------*/372/**373@brief Get the string associated to a key, convert to an long int374@param d Dictionary to search375@param key Key string to look for376@param notfound Value to return in case of error377@return long integer378379This function queries a dictionary for a key. A key as read from an380ini file is given as "section:key". If the key cannot be found,381the notfound value is returned.382383Supported values for integers include the usual C notation384so decimal, octal (starting with 0) and hexadecimal (starting with 0x)385are supported. Examples:386387"42" -> 42388"042" -> 34 (octal -> decimal)389"0x42" -> 66 (hexa -> decimal)390391Warning: the conversion may overflow in various ways. Conversion is392totally outsourced to strtol(), see the associated man page for overflow393handling.394395Credits: Thanks to A. Becker for suggesting strtol()396*/397/*--------------------------------------------------------------------------*/398long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound)399{400const char * str ;401402str = iniparser_getstring(d, key, INI_INVALID_KEY);403if (str==INI_INVALID_KEY) return notfound ;404return strtol(str, NULL, 0);405}406407408/*-------------------------------------------------------------------------*/409/**410@brief Get the string associated to a key, convert to an int411@param d Dictionary to search412@param key Key string to look for413@param notfound Value to return in case of error414@return integer415416This function queries a dictionary for a key. A key as read from an417ini file is given as "section:key". If the key cannot be found,418the notfound value is returned.419420Supported values for integers include the usual C notation421so decimal, octal (starting with 0) and hexadecimal (starting with 0x)422are supported. Examples:423424"42" -> 42425"042" -> 34 (octal -> decimal)426"0x42" -> 66 (hexa -> decimal)427428Warning: the conversion may overflow in various ways. Conversion is429totally outsourced to strtol(), see the associated man page for overflow430handling.431432Credits: Thanks to A. Becker for suggesting strtol()433*/434/*--------------------------------------------------------------------------*/435int iniparser_getint(const dictionary * d, const char * key, int notfound)436{437return (int)iniparser_getlongint(d, key, notfound);438}439440/*-------------------------------------------------------------------------*/441/**442@brief Get the string associated to a key, convert to a double443@param d Dictionary to search444@param key Key string to look for445@param notfound Value to return in case of error446@return double447448This function queries a dictionary for a key. A key as read from an449ini file is given as "section:key". If the key cannot be found,450the notfound value is returned.451*/452/*--------------------------------------------------------------------------*/453double iniparser_getdouble(const dictionary * d, const char * key, double notfound)454{455const char * str ;456457str = iniparser_getstring(d, key, INI_INVALID_KEY);458if (str==INI_INVALID_KEY) return notfound ;459return atof(str);460}461462/*-------------------------------------------------------------------------*/463/**464@brief Get the string associated to a key, convert to a boolean465@param d Dictionary to search466@param key Key string to look for467@param notfound Value to return in case of error468@return integer469470This function queries a dictionary for a key. A key as read from an471ini file is given as "section:key". If the key cannot be found,472the notfound value is returned.473474A true boolean is found if one of the following is matched:475476- A string starting with 'y'477- A string starting with 'Y'478- A string starting with 't'479- A string starting with 'T'480- A string starting with '1'481482A false boolean is found if one of the following is matched:483484- A string starting with 'n'485- A string starting with 'N'486- A string starting with 'f'487- A string starting with 'F'488- A string starting with '0'489490The notfound value returned if no boolean is identified, does not491necessarily have to be 0 or 1.492*/493/*--------------------------------------------------------------------------*/494int iniparser_getboolean(const dictionary * d, const char * key, int notfound)495{496int ret ;497const char * c ;498499c = iniparser_getstring(d, key, INI_INVALID_KEY);500if (c==INI_INVALID_KEY) return notfound ;501if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {502ret = 1 ;503} else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {504ret = 0 ;505} else {506ret = notfound ;507}508return ret;509}510511int iniparser_find_entry(const dictionary * ini, const char * entry)512{513int found=0 ;514if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {515found = 1 ;516}517return found ;518}519520int iniparser_set(dictionary * ini, const char * entry, const char * val)521{522char tmp_str[ASCIILINESZ+1];523return dictionary_set(ini, strlwc(entry, tmp_str, sizeof(tmp_str)), val) ;524}525526void iniparser_unset(dictionary * ini, const char * entry)527{528char tmp_str[ASCIILINESZ+1];529dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str)));530}531532static line_status iniparser_line(533const char * input_line,534char * section,535char * key,536char * value)537{538line_status sta ;539char * line = NULL;540size_t len ;541542line = xstrdup(input_line);543len = strstrip(line);544545sta = LINE_UNPROCESSED ;546if (len<1) {547/* Empty line */548sta = LINE_EMPTY ;549} else if (line[0]=='#' || line[0]==';') {550/* Comment line */551sta = LINE_COMMENT ;552} else if (line[0]=='[' && line[len-1]==']') {553/* Section name */554sscanf(line, "[%[^]]", section);555strstrip(section);556strlwc(section, section, (unsigned)len);557sta = LINE_SECTION ;558} else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2559|| sscanf (line, "%[^=] = '%[^\']'", key, value) == 2) {560/* Usual key=value with quotes, with or without comments */561strstrip(key);562strlwc(key, key, (unsigned)len);563/* Don't strip spaces from values surrounded with quotes */564sta = LINE_VALUE ;565} else if (sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {566/* Usual key=value without quotes, with or without comments */567strstrip(key);568strlwc(key, key, (unsigned)len);569strstrip(value);570/*571* sscanf cannot handle '' or "" as empty values572* this is done here573*/574if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {575value[0]=0 ;576}577sta = LINE_VALUE ;578} else if (sscanf(line, "%[^=] = %[;#]", key, value)==2579|| sscanf(line, "%[^=] %[=]", key, value) == 2) {580/*581* Special cases:582* key=583* key=;584* key=#585*/586strstrip(key);587strlwc(key, key, (unsigned)len);588value[0]=0 ;589sta = LINE_VALUE ;590} else {591/* Generate syntax error */592sta = LINE_ERROR ;593}594595free(line);596return sta ;597}598599/*-------------------------------------------------------------------------*/600/**601@brief Parse an ini file and return an allocated dictionary object602@param ininame Name of the ini file to read.603@return Pointer to newly allocated dictionary604605This is the parser for ini files. This function is called, providing606the name of the file to be read. It returns a dictionary object that607should not be accessed directly, but through accessor functions608instead.609610The returned dictionary must be freed using iniparser_freedict().611*/612/*--------------------------------------------------------------------------*/613614#ifndef __ANDROID__615#undef fOpen616#define fOpen fopen617#endif618619dictionary * iniparser_load(const char * ininame)620{621FILE * in ;622623char line [ASCIILINESZ+1] ;624char section [ASCIILINESZ+1] ;625char key [ASCIILINESZ+1] ;626char tmp [(ASCIILINESZ * 2) + 2] ;627char val [ASCIILINESZ+1] ;628629int last=0 ;630int len ;631int lineno=0 ;632int errs=0;633int mem_err=0;634635dictionary * dict ;636637if ((in=fOpen(ininame, "r"))==NULL) {638iniparser_error_callback("iniparser: cannot open %s\n", ininame);639return NULL ;640}641642dict = dictionary_new(0) ;643if (!dict) {644fclose(in);645return NULL ;646}647648memset(line, 0, ASCIILINESZ);649memset(section, 0, ASCIILINESZ);650memset(key, 0, ASCIILINESZ);651memset(val, 0, ASCIILINESZ);652last=0 ;653654while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {655lineno++ ;656len = (int)strlen(line)-1;657if (len<=0)658continue;659/* Safety check against buffer overflows */660if (line[len]!='\n' && !feof(in)) {661iniparser_error_callback(662"iniparser: input line too long in %s (%d)\n",663ininame,664lineno);665dictionary_del(dict);666fclose(in);667return NULL ;668}669/* Get rid of \n and spaces at end of line */670while ((len>=0) &&671((line[len]=='\n') || (isspace(line[len])))) {672line[len]=0 ;673len-- ;674}675if (len < 0) { /* Line was entirely \n and/or spaces */676len = 0;677}678/* Detect multi-line */679if (line[len]=='\\') {680/* Multi-line value */681last=len ;682continue ;683} else {684last=0 ;685}686switch (iniparser_line(line, section, key, val)) {687case LINE_EMPTY:688case LINE_COMMENT:689break ;690691case LINE_SECTION:692mem_err = dictionary_set(dict, section, NULL);693break ;694695case LINE_VALUE:696sprintf(tmp, "%s:%s", section, key);697mem_err = dictionary_set(dict, tmp, val);698break ;699700case LINE_ERROR:701iniparser_error_callback(702"iniparser: syntax error in %s (%d):\n-> %s\n",703ininame,704lineno,705line);706errs++ ;707break;708709default:710break ;711}712memset(line, 0, ASCIILINESZ);713last=0;714if (mem_err<0) {715iniparser_error_callback("iniparser: memory allocation failure\n");716break ;717}718}719if (errs) {720dictionary_del(dict);721dict = NULL ;722}723fclose(in);724return dict ;725}726727void iniparser_freedict(dictionary * d)728{729dictionary_del(d);730}731732733