--- sbbs/src/xpdev/ini_file.c 2018/04/24 16:41:24 1.1.1.1 +++ sbbs/src/xpdev/ini_file.c 2018/04/24 16:45:40 1.1.1.2 @@ -1,14 +1,14 @@ /* ini_file.c */ -/* Functions to parse ini files */ +/* Functions to create and parse .ini files */ -/* $Id: ini_file.c,v 1.1.1.1 2018/04/24 16:41:24 root Exp $ */ +/* $Id: ini_file.c,v 1.1.1.2 2018/04/24 16:45:40 root Exp $ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * - * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html * + * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public License * @@ -39,7 +39,8 @@ #include /* strlen */ #include /* isdigit */ #include /* fmod */ -#include "datewrap.h" /* isoDateTime_t */ +#include "xpdatetime.h" /* isoDateTime_t */ +#include "datewrap.h" /* ctime_r */ #include "dirwrap.h" /* fexist */ #include "filewrap.h" /* chsize */ #include "ini_file.h" @@ -254,16 +255,22 @@ static char* read_value(FILE* fp, const return(NULL); } -static size_t get_value(str_list_t list, const char* section, const char* key, char* value) +static size_t get_value(str_list_t list, const char* section, const char* key, char* value, char** vpp) { - char str[INI_MAX_LINE_LEN]; + char str[INI_MAX_LINE_LEN]; char* p; char* vp; size_t i; - value[0]=0; + if(value!=NULL) + value[0]=0; + if(vpp!=NULL) + *vpp=NULL; + if(list==NULL) + return 0; + for(i=find_section(list, section); list[i]!=NULL; i++) { - SAFECOPY(str, list[i]); + SAFECOPY(str,list[i]); if(is_eof(str)) break; if((p=key_name(str,&vp))==NULL) @@ -272,7 +279,10 @@ static size_t get_value(str_list_t list, break; if(stricmp(p,key)!=0) continue; - sprintf(value,"%.*s",INI_MAX_VALUE_LEN-1,vp); + if(value!=NULL) + sprintf(value,"%.*s",INI_MAX_VALUE_LEN-1,vp); + if(vpp!=NULL) + *vpp=list[i] + (vp - str); return(i); } @@ -290,12 +300,40 @@ BOOL iniSectionExists(str_list_t list, c return(list[i]!=NULL); } +str_list_t iniGetSection(str_list_t list, const char *section) +{ + size_t i; + str_list_t retval; + char *p; + + if(list==NULL) + return(NULL); + + if((retval=strListInit())==NULL) + return(NULL); + + i=find_section(list,section); + if(list[i]!=NULL) { + strListPush(&retval, list[i]); + for(i++;list[i]!=NULL;i++) { + p=list[i]; + SKIP_WHITESPACE(p); + if(*p==INI_OPEN_SECTION_CHAR) + break; + strListPush(&retval, list[i]); + } + } + return(retval); +} + BOOL iniKeyExists(str_list_t list, const char* section, const char* key) { - char val[INI_MAX_VALUE_LEN]; size_t i; - i=get_value(list, section, key, val); + if(list==NULL) + return(FALSE); + + i=get_value(list, section, key, NULL, NULL); if(list[i]==NULL || *(list[i])==INI_OPEN_SECTION_CHAR) return(FALSE); @@ -305,21 +343,21 @@ BOOL iniKeyExists(str_list_t list, const BOOL iniValueExists(str_list_t list, const char* section, const char* key) { - char val[INI_MAX_VALUE_LEN]; + char* vp=NULL; - get_value(list, section, key, val); + get_value(list, section, key, NULL, &vp); - return(val[0]!=0); + return(vp!=NULL && *vp!=0); } BOOL iniRemoveKey(str_list_t* list, const char* section, const char* key) { - char val[INI_MAX_VALUE_LEN]; size_t i; + char* vp=NULL; - i=get_value(*list, section, key, val); + i=get_value(*list, section, key, NULL, &vp); - if((*list)[i]==NULL || *(*list)[i]==INI_OPEN_SECTION_CHAR) + if(vp==NULL) return(FALSE); return(strListDelete(list,i)); @@ -327,18 +365,14 @@ BOOL iniRemoveKey(str_list_t* list, cons BOOL iniRemoveValue(str_list_t* list, const char* section, const char* key) { - char val[INI_MAX_VALUE_LEN]; - size_t i; - char* p; char* vp=NULL; - i=get_value(*list, section, key, val); + get_value(*list, section, key, NULL, &vp); - p=key_name((*list)[i], &vp); if(vp==NULL) return(FALSE); - strcat(p,"="); + *vp=0; return(TRUE); } @@ -435,7 +469,7 @@ char* iniSetString(str_list_t* list, con value=""; safe_snprintf(str, sizeof(str), "%s%-*s%s%s" , style->key_prefix, style->key_len, key, style->value_separator, value); - i=get_value(*list, section, key, curval); + i=get_value(*list, section, key, curval, NULL); if((*list)[i]==NULL || *(*list)[i]==INI_OPEN_SECTION_CHAR) { while(i && *(*list)[i-1]==0) i--; /* Insert before blank lines, not after */ return strListInsert(list, str, i); @@ -493,37 +527,40 @@ char* iniSetFloat(str_list_t* list, cons } char* iniSetBytes(str_list_t* list, const char* section, const char* key, ulong unit - ,ulong value, ini_style_t* style) + ,int64_t value, ini_style_t* style) { char str[INI_MAX_VALUE_LEN]; double bytes; - switch(unit) { - case 1024*1024*1024: - SAFEPRINTF(str,"%luG",value); - break; - case 1024*1024: - SAFEPRINTF(str,"%luM",value); - break; - case 1024: - SAFEPRINTF(str,"%luK",value); - break; - default: - if(unit<1) - unit=1; - bytes=value*unit; - - if(fmod(bytes,1024.0*1024.0*1024.0*1024.0)==0) - SAFEPRINTF(str,"%gT",bytes/(1024.0*1024.0*1024.0*1024.0)); - else if(fmod(bytes,1024*1024*1024)==0) - SAFEPRINTF(str,"%gG",bytes/(1024*1024*1024)); - else if(fmod(bytes,1024*1024)==0) - SAFEPRINTF(str,"%gM",bytes/(1024*1024)); - else if(fmod(bytes,1024)==0) - SAFEPRINTF(str,"%gK",bytes/1024); - else - SAFEPRINTF(str,"%lu",(ulong)bytes); - } + if(value==0) + SAFECOPY(str,"0"); + else + switch(unit) { + case 1024*1024*1024: + SAFEPRINTF(str,"%"PRIi64"G",value); + break; + case 1024*1024: + SAFEPRINTF(str,"%"PRIi64"M",value); + break; + case 1024: + SAFEPRINTF(str,"%"PRIi64"K",value); + break; + default: + if(unit<1) + unit=1; + bytes=(double)(value*unit); + + if(fmod(bytes,1024.0*1024.0*1024.0*1024.0)==0) + SAFEPRINTF(str,"%gT",bytes/(1024.0*1024.0*1024.0*1024.0)); + else if(fmod(bytes,1024*1024*1024)==0) + SAFEPRINTF(str,"%gG",bytes/(1024*1024*1024)); + else if(fmod(bytes,1024*1024)==0) + SAFEPRINTF(str,"%gM",bytes/(1024*1024)); + else if(fmod(bytes,1024)==0) + SAFEPRINTF(str,"%gK",bytes/1024); + else + SAFEPRINTF(str,"%"PRIi64, (int64_t)bytes); + } return iniSetString(list, section, key, str, style); } @@ -553,7 +590,7 @@ char* iniSetDateTime(str_list_t* list, c if(value==0) SAFECOPY(str,"Never"); - else if((p=CTIME_R(&value,tstr))==NULL) + else if((p=ctime_r(&value,tstr))==NULL) SAFEPRINTF(str,"0x%lx",value); else if(!include_time) /* reformat into "Mon DD YYYY" */ safe_snprintf(str,sizeof(str),"%.3s %.2s %.4s" ,p+4,p+8,p+20); @@ -572,6 +609,33 @@ char* iniSetEnum(str_list_t* list, const return iniSetLongInt(list, section, key, value, style); } +char* iniSetEnumList(str_list_t* list, const char* section, const char* key + ,const char* sep, str_list_t names, unsigned* val_list, unsigned count, ini_style_t* style) +{ + char value[INI_MAX_VALUE_LEN]; + size_t i; + size_t name_count; + + value[0]=0; + + if(sep==NULL) + sep=","; + + if(val_list!=NULL) { + name_count = strListCount(names); + for(i=0; i < count; i++) { + if(value[0]) + strcat(value,sep); + if(val_list[i] < name_count) + strcat(value, names[val_list[i]]); + else + sprintf(value + strlen(value), "%u", val_list[i]); + } + } + + return iniSetString(list, section, key, value, style); +} + char* iniSetNamedInt(str_list_t* list, const char* section, const char* key, named_long_t* names ,long value, ini_style_t* style) { @@ -627,47 +691,77 @@ char* iniSetStringList(str_list_t* list, ,const char* sep, str_list_t val_list, ini_style_t* style) { char value[INI_MAX_VALUE_LEN]; - size_t i; - - value[0]=0; if(sep==NULL) sep=","; - if(val_list!=NULL) - for(i=0; val_list[i]!=NULL; i++) { - if(value[0]) - strcat(value,sep); - strcat(value,val_list[i]); - } + return iniSetString(list, section, key, strListCombine(val_list, value, sizeof(value), sep), style); +} - return iniSetString(list, section, key, value, style); +static char* default_value(const char* deflt, char* value) +{ + if(deflt!=NULL && deflt!=value && value!=NULL) + sprintf(value,"%.*s",INI_MAX_VALUE_LEN-1,deflt); + + return((char*)deflt); } char* iniReadString(FILE* fp, const char* section, const char* key, const char* deflt, char* value) { - if(read_value(fp,section,key,value)==NULL || *value==0 /* blank */) { - if(deflt!=NULL) - sprintf(value,"%.*s",INI_MAX_VALUE_LEN-1,deflt); - return((char*)deflt); - } + if(read_value(fp,section,key,value)==NULL || *value==0 /* blank */) + return default_value(deflt,value); return(value); } char* iniGetString(str_list_t list, const char* section, const char* key, const char* deflt, char* value) { - get_value(list, section, key, value); + char* vp=NULL; - if(*value==0 /* blank value or missing key */) { - if(deflt!=NULL) - sprintf(value,"%.*s",INI_MAX_VALUE_LEN-1,deflt); - return((char*)deflt); - } + get_value(list, section, key, value, &vp); + + if(vp==NULL || *vp==0 /* blank value or missing key */) + return default_value(deflt,value); + + return(vp); +} + +char* iniPopKey(str_list_t* list, const char* section, const char* key, char* value) +{ + size_t i; + + if(list==NULL || *list==NULL) + return NULL; + + i=get_value(*list, section, key, value, NULL); + + if((*list)[i]==NULL) + return NULL; + + strListDelete(list,i); + + return(value); +} + +char* iniReadExistingString(FILE* fp, const char* section, const char* key, const char* deflt, char* value) +{ + if(read_value(fp,section,key,value)==NULL) + return(NULL); + + if(*value==0 /* blank */) + return default_value(deflt,value); return(value); } +char* iniGetExistingString(str_list_t list, const char* section, const char* key, const char* deflt, char* value) +{ + if(!iniKeyExists(list, section, key)) + return(NULL); + + return iniGetString(list, section, key, deflt, value); +} + static str_list_t splitList(char* list, const char* sep) { char* token; @@ -716,7 +810,7 @@ str_list_t iniGetStringList(str_list_t l { char value[INI_MAX_VALUE_LEN]; - get_value(list, section, key, value); + get_value(list, section, key, value, NULL); if(*value==0 /* blank value or missing key */) { if(deflt==NULL) @@ -910,21 +1004,20 @@ iniReadNamedStringList(FILE* fp, const c char* value; char str[INI_MAX_LINE_LEN]; ulong items=0; - named_string_t** lp; + named_string_t** lp=NULL; named_string_t** np; - if((lp=(named_string_t**)malloc(sizeof(named_string_t*)))==NULL) - return(NULL); - - *lp=NULL; - if(fp==NULL) - return(lp); + return(NULL); rewind(fp); if(!seek_section(fp,section)) - return(lp); + return(NULL); + + /* New behavior, if section exists but is empty, return single element array (terminator only) */ + if((lp=(named_string_t**)malloc(sizeof(named_string_t*)))==NULL) + return(NULL); while(!feof(fp)) { if(fgets(str,sizeof(str),fp)==NULL) @@ -959,18 +1052,21 @@ iniGetNamedStringList(str_list_t list, c char* value; char str[INI_MAX_LINE_LEN]; ulong i,items=0; - named_string_t** lp; + named_string_t** lp=NULL; named_string_t** np; - if((lp=(named_string_t**)malloc(sizeof(named_string_t*)))==NULL) + if(list==NULL) return(NULL); - *lp=NULL; + i=find_section(list,section); + if(list[i]==NULL) + return(NULL); - if(list==NULL) - return(lp); + /* New behavior, if section exists but is empty, return single element array (terminator only) */ + if((lp=(named_string_t**)malloc(sizeof(named_string_t*)))==NULL) + return(NULL); - for(i=find_section(list,section);list[i]!=NULL;i++) { + for(;list[i]!=NULL;i++) { SAFECOPY(str,list[i]); if(is_eof(str)) break; @@ -1040,14 +1136,14 @@ long iniReadInteger(FILE* fp, const char long iniGetInteger(str_list_t list, const char* section, const char* key, long deflt) { - char value[INI_MAX_VALUE_LEN]; + char* vp=NULL; - get_value(list, section, key, value); + get_value(list, section, key, NULL, &vp); - if(*value==0) /* blank value or missing key */ + if(vp==NULL || *vp==0) /* blank value or missing key */ return(deflt); - return(parseInteger(value)); + return(parseInteger(vp)); } ushort iniReadShortInt(FILE* fp, const char* section, const char* key, ushort deflt) @@ -1076,17 +1172,17 @@ ulong iniReadLongInt(FILE* fp, const cha ulong iniGetLongInt(str_list_t list, const char* section, const char* key, ulong deflt) { - char value[INI_MAX_VALUE_LEN]; + char* vp=NULL; - get_value(list, section, key, value); + get_value(list, section, key, NULL, &vp); - if(*value==0) /* blank value or missing key */ + if(vp==NULL || *vp==0) /* blank value or missing key */ return(deflt); - return(parseLongInteger(value)); + return(parseLongInteger(vp)); } -static ulong parseBytes(const char* value, ulong unit) +static int64_t parseBytes(const char* value, ulong unit) { char* p=NULL; double bytes; @@ -1094,21 +1190,30 @@ static ulong parseBytes(const char* valu bytes=strtod(value,&p); if(p!=NULL) { switch(toupper(*p)) { + case 'E': + bytes*=1024; + /* fall-through */ + case 'P': + bytes*=1024; + /* fall-through */ case 'T': bytes*=1024; + /* fall-through */ case 'G': bytes*=1024; + /* fall-through */ case 'M': bytes*=1024; + /* fall-through */ case 'K': bytes*=1024; break; } } - return((ulong)(unit>1 ? (bytes/unit):bytes)); + return((int64_t)(unit>1 ? (bytes/unit):bytes)); } -ulong iniReadBytes(FILE* fp, const char* section, const char* key, ulong unit, ulong deflt) +int64_t iniReadBytes(FILE* fp, const char* section, const char* key, ulong unit, int64_t deflt) { char* value; char buf[INI_MAX_VALUE_LEN]; @@ -1122,16 +1227,16 @@ ulong iniReadBytes(FILE* fp, const char* return(parseBytes(value,unit)); } -ulong iniGetBytes(str_list_t list, const char* section, const char* key, ulong unit, ulong deflt) +int64_t iniGetBytes(str_list_t list, const char* section, const char* key, ulong unit, int64_t deflt) { - char value[INI_MAX_VALUE_LEN]; + char* vp=NULL; - get_value(list, section, key, value); + get_value(list, section, key, NULL, &vp); - if(*value==0) /* blank value or missing key */ + if(vp==NULL || *vp==0) /* blank value or missing key */ return(deflt); - return(parseBytes(value,unit)); + return(parseBytes(vp,unit)); } #if !defined(NO_SOCKET_SUPPORT) @@ -1207,14 +1312,14 @@ ulong iniReadIpAddress(FILE* fp, const c ulong iniGetIpAddress(str_list_t list, const char* section, const char* key, ulong deflt) { - char value[INI_MAX_VALUE_LEN]; + char* vp=NULL; - get_value(list, section, key, value); + get_value(list, section, key, NULL, &vp); - if(*value==0) /* blank value or missing key */ + if(vp==NULL || *vp==0) /* blank value or missing key */ return(deflt); - return(parseIpAddress(value)); + return(parseIpAddress(vp)); } #endif /* !NO_SOCKET_SUPPORT */ @@ -1278,14 +1383,14 @@ double iniReadFloat(FILE* fp, const char double iniGetFloat(str_list_t list, const char* section, const char* key, double deflt) { - char value[INI_MAX_VALUE_LEN]; + char* vp=NULL; - get_value(list, section, key, value); + get_value(list, section, key, NULL, &vp); - if(*value==0) /* blank value or missing key */ + if(vp==NULL || *vp==0) /* blank value or missing key */ return(deflt); - return(atof(value)); + return(atof(vp)); } BOOL iniReadBool(FILE* fp, const char* section, const char* key, BOOL deflt) @@ -1304,14 +1409,14 @@ BOOL iniReadBool(FILE* fp, const char* s BOOL iniGetBool(str_list_t list, const char* section, const char* key, BOOL deflt) { - char value[INI_MAX_VALUE_LEN]; + char* vp=NULL; - get_value(list, section, key, value); + get_value(list, section, key, NULL, &vp); - if(*value==0) /* blank value or missing key */ + if(vp==NULL || *vp==0) /* blank value or missing key */ return(deflt); - return(parseBool(value)); + return(parseBool(vp)); } static BOOL validDate(struct tm* tm) @@ -1444,31 +1549,77 @@ time_t iniReadDateTime(FILE* fp, const c time_t iniGetDateTime(str_list_t list, const char* section, const char* key, time_t deflt) { - char value[INI_MAX_VALUE_LEN]; + char* vp=NULL; - get_value(list, section, key, value); + get_value(list, section, key, NULL, &vp); - if(*value==0) /* blank value or missing key */ + if(vp==NULL || *vp==0) /* blank value or missing key */ return(deflt); - return(parseDateTime(value)); + return(parseDateTime(vp)); } static unsigned parseEnum(const char* value, str_list_t names) { - unsigned i; - + unsigned i,count; + char val[INI_MAX_VALUE_LEN]; + char* p=val; + + /* Strip trailing words (enums must be a single word with no white-space) */ + /* to support comments following enum values */ + SAFECOPY(val,value); + FIND_WHITESPACE(p); + *p=0; + + if((count=strListCount(names)) == 0) + return 0; + /* Look for exact matches first */ - for(i=0; names[i]!=NULL; i++) - if(stricmp(names[i],value)==0) + for(i=0; i=count) + i=count-1; + return i; +} + +unsigned* parseEnumList(const char* values, const char* sep, str_list_t names, unsigned* count) +{ + char* vals; + str_list_t list; + unsigned* enum_list; + size_t i; + + *count=0; + + if(values==NULL) + return NULL; + + if((vals=strdup(values)) == NULL) + return NULL; + + list=splitList(vals, sep); + + free(vals); + + if((*count=strListCount(list)) < 1) + return NULL; + + if((enum_list=(unsigned *)malloc((*count)*sizeof(unsigned)))!=NULL) { + for(i=0;i<*count;i++) + enum_list[i]=parseEnum(list[i], names); + } + + strListFree(&list); + + return enum_list; } unsigned iniReadEnum(FILE* fp, const char* section, const char* key, str_list_t names, unsigned deflt) @@ -1485,16 +1636,56 @@ unsigned iniReadEnum(FILE* fp, const cha return(parseEnum(value,names)); } +unsigned* iniReadEnumList(FILE* fp, const char* section, const char* key + ,str_list_t names, unsigned* cp + ,const char* sep, const char* deflt) +{ + char* value; + char buf[INI_MAX_VALUE_LEN]; + unsigned count; + + if(cp==NULL) + cp=&count; + + *cp=0; + + if((value=read_value(fp,section,key,buf))==NULL || *value==0 /* blank */) + value=(char*)deflt; + + return(parseEnumList(value, sep, names, cp)); +} + unsigned iniGetEnum(str_list_t list, const char* section, const char* key, str_list_t names, unsigned deflt) { - char value[INI_MAX_VALUE_LEN]; + char* vp=NULL; - get_value(list, section, key, value); + get_value(list, section, key, NULL, &vp); - if(*value==0) /* blank value or missing key */ + if(vp==NULL || *vp==0) /* blank value or missing key */ return(deflt); - return(parseEnum(value,names)); + return(parseEnum(vp,names)); +} + +unsigned* iniGetEnumList(str_list_t list, const char* section, const char* key + ,str_list_t names, unsigned* cp, const char* sep, const char* deflt) +{ + char* vp=NULL; + unsigned count; + + if(cp==NULL) + cp=&count; + + *cp=0; + + get_value(list, section, key, NULL, &vp); + + if(vp==NULL || *vp==0 /* blank value or missing key */) { + if(deflt==NULL) + return(NULL); + vp=(char*)deflt; + } + return(parseEnumList(vp, sep, names, cp)); } static long parseNamedInt(const char* value, named_long_t* names) @@ -1532,14 +1723,14 @@ long iniReadNamedInt(FILE* fp, const cha long iniGetNamedInt(str_list_t list, const char* section, const char* key ,named_long_t* names, long deflt) { - char value[INI_MAX_VALUE_LEN]; + char* vp=NULL; - get_value(list, section, key, value); + get_value(list, section, key, NULL, &vp); - if(*value==0) /* blank value or missing key */ + if(vp==NULL || *vp==0) /* blank value or missing key */ return(deflt); - return(parseNamedInt(value,names)); + return(parseNamedInt(vp,names)); } static double parseNamedFloat(const char* value, named_double_t* names) @@ -1577,14 +1768,14 @@ double iniReadNamedFloat(FILE* fp, const double iniGetNamedFloat(str_list_t list, const char* section, const char* key ,named_double_t* names, double deflt) { - char value[INI_MAX_VALUE_LEN]; + char* vp=NULL; - get_value(list, section, key, value); + get_value(list, section, key, NULL, &vp); - if(*value==0) /* blank value or missing key */ + if(vp==NULL || *vp==0) /* blank value or missing key */ return(deflt); - return(parseNamedFloat(value,names)); + return(parseNamedFloat(vp,names)); } static ulong parseBitField(char* value, ini_bitdesc_t* bitdesc) @@ -1634,14 +1825,14 @@ ulong iniReadBitField(FILE* fp, const ch ulong iniGetBitField(str_list_t list, const char* section, const char* key, ini_bitdesc_t* bitdesc, ulong deflt) { - char value[INI_MAX_VALUE_LEN]; + char* vp=NULL;; - get_value(list, section, key, value); + get_value(list, section, key, NULL, &vp); - if(*value==0) /* blank value or missing key */ + if(vp==NULL || *vp==0) /* blank value or missing key */ return(deflt); - return(parseBitField(value,bitdesc)); + return(parseBitField(vp,bitdesc)); } FILE* iniOpenFile(const char* fname, BOOL create)