|
|
1.1 ! root 1: /* ini_file.c */ ! 2: ! 3: /* Functions to parse ini files */ ! 4: ! 5: /* $Id: ini_file.c,v 1.96 2006/08/14 22:55:48 rswindell Exp $ */ ! 6: ! 7: /**************************************************************************** ! 8: * @format.tab-size 4 (Plain Text/Source Code File Header) * ! 9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * ! 10: * * ! 11: * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html * ! 12: * * ! 13: * This library is free software; you can redistribute it and/or * ! 14: * modify it under the terms of the GNU Lesser General Public License * ! 15: * as published by the Free Software Foundation; either version 2 * ! 16: * of the License, or (at your option) any later version. * ! 17: * See the GNU Lesser General Public License for more details: lgpl.txt or * ! 18: * http://www.fsf.org/copyleft/lesser.html * ! 19: * * ! 20: * Anonymous FTP access to the most recent released source is available at * ! 21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * ! 22: * * ! 23: * Anonymous CVS access to the development source and modification history * ! 24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: * ! 25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login * ! 26: * (just hit return, no password is necessary) * ! 27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src * ! 28: * * ! 29: * For Synchronet coding style and modification guidelines, see * ! 30: * http://www.synchro.net/source.html * ! 31: * * ! 32: * You are encouraged to submit any modifications (preferably in Unix diff * ! 33: * format) via e-mail to [email protected] * ! 34: * * ! 35: * Note: If this box doesn't appear square, then you need to fix your tabs. * ! 36: ****************************************************************************/ ! 37: ! 38: #include <stdlib.h> /* strtol */ ! 39: #include <string.h> /* strlen */ ! 40: #include <ctype.h> /* isdigit */ ! 41: #include <math.h> /* fmod */ ! 42: #include "datewrap.h" /* isoDateTime_t */ ! 43: #include "dirwrap.h" /* fexist */ ! 44: #include "filewrap.h" /* chsize */ ! 45: #include "ini_file.h" ! 46: ! 47: /* Maximum length of entire line, includes '\0' */ ! 48: #define INI_MAX_LINE_LEN (INI_MAX_VALUE_LEN*2) ! 49: #define INI_COMMENT_CHAR ';' ! 50: #define INI_OPEN_SECTION_CHAR '[' ! 51: #define INI_CLOSE_SECTION_CHAR ']' ! 52: #define INI_SECTION_NAME_SEP "|" ! 53: #define INI_BIT_SEP '|' ! 54: #define INI_NEW_SECTION ((char*)~0) ! 55: #define INI_EOF_DIRECTIVE "!eof" ! 56: #define INI_INCLUDE_DIRECTIVE "!include" ! 57: #define INI_INCLUDE_MAX 10000 ! 58: ! 59: static ini_style_t default_style; ! 60: ! 61: void iniSetDefaultStyle(ini_style_t style) ! 62: { ! 63: default_style = style; ! 64: } ! 65: ! 66: /* These correlate with the LOG_* definitions in syslog.h/gen_defs.h */ ! 67: static char* logLevelStringList[] ! 68: = {"Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Informational", "Debugging", NULL}; ! 69: ! 70: str_list_t iniLogLevelStringList(void) ! 71: { ! 72: return(logLevelStringList); ! 73: } ! 74: ! 75: static BOOL is_eof(char* str) ! 76: { ! 77: return(*str=='!' && stricmp(truncsp(str),INI_EOF_DIRECTIVE)==0); ! 78: } ! 79: ! 80: static char* section_name(char* p) ! 81: { ! 82: char* tp; ! 83: ! 84: SKIP_WHITESPACE(p); ! 85: if(*p!=INI_OPEN_SECTION_CHAR) ! 86: return(NULL); ! 87: p++; ! 88: SKIP_WHITESPACE(p); ! 89: tp=strrchr(p,INI_CLOSE_SECTION_CHAR); ! 90: if(tp==NULL) ! 91: return(NULL); ! 92: *tp=0; ! 93: truncsp(p); ! 94: ! 95: return(p); ! 96: } ! 97: ! 98: static BOOL section_match(const char* name, const char* compare) ! 99: { ! 100: BOOL found=FALSE; ! 101: str_list_t names=strListSplitCopy(NULL,name,INI_SECTION_NAME_SEP); ! 102: str_list_t comps=strListSplitCopy(NULL,compare,INI_SECTION_NAME_SEP); ! 103: size_t i,j; ! 104: char* n; ! 105: char* c; ! 106: ! 107: /* Ignore trailing whitepsace */ ! 108: for(i=0; names[i]!=NULL; i++) ! 109: truncsp(names[i]); ! 110: for(i=0; comps[i]!=NULL; i++) ! 111: truncsp(comps[i]); ! 112: ! 113: /* Search for matches */ ! 114: for(i=0; names[i]!=NULL && !found; i++) ! 115: for(j=0; comps[j]!=NULL && !found; j++) { ! 116: n=names[i]; ! 117: SKIP_WHITESPACE(n); ! 118: c=comps[j]; ! 119: SKIP_WHITESPACE(c); ! 120: if(stricmp(n,c)==0) ! 121: found=TRUE; ! 122: } ! 123: ! 124: strListFree(&names); ! 125: strListFree(&comps); ! 126: ! 127: return(found); ! 128: } ! 129: ! 130: static BOOL seek_section(FILE* fp, const char* section) ! 131: { ! 132: char* p; ! 133: char str[INI_MAX_LINE_LEN]; ! 134: ! 135: rewind(fp); ! 136: ! 137: if(section==ROOT_SECTION) ! 138: return(TRUE); ! 139: ! 140: while(!feof(fp)) { ! 141: if(fgets(str,sizeof(str),fp)==NULL) ! 142: break; ! 143: if(is_eof(str)) ! 144: break; ! 145: if((p=section_name(str))==NULL) ! 146: continue; ! 147: if(section_match(p,section)) ! 148: return(TRUE); ! 149: } ! 150: return(FALSE); ! 151: } ! 152: ! 153: static size_t find_section_index(str_list_t list, const char* section) ! 154: { ! 155: char* p; ! 156: char str[INI_MAX_VALUE_LEN]; ! 157: size_t i; ! 158: ! 159: for(i=0; list[i]!=NULL; i++) { ! 160: SAFECOPY(str,list[i]); ! 161: if(is_eof(str)) ! 162: return(strListCount(list)); ! 163: if((p=section_name(str))!=NULL && section_match(p,section)) ! 164: return(i); ! 165: } ! 166: ! 167: return(i); ! 168: } ! 169: ! 170: static size_t find_section(str_list_t list, const char* section) ! 171: { ! 172: size_t i; ! 173: ! 174: if(section==ROOT_SECTION) ! 175: return(0); ! 176: ! 177: i=find_section_index(list,section); ! 178: if(list[i]!=NULL) ! 179: i++; ! 180: return(i); ! 181: } ! 182: ! 183: static char* key_name(char* p, char** vp) ! 184: { ! 185: char* equal; ! 186: char* colon; ! 187: ! 188: *vp=NULL; ! 189: ! 190: if(p==NULL) ! 191: return(NULL); ! 192: ! 193: /* Parse value name */ ! 194: SKIP_WHITESPACE(p); ! 195: if(*p==INI_COMMENT_CHAR) ! 196: return(NULL); ! 197: if(*p==INI_OPEN_SECTION_CHAR) ! 198: return(INI_NEW_SECTION); ! 199: equal=strchr(p,'='); ! 200: colon=strchr(p,':'); ! 201: if(colon==NULL || (equal!=NULL && equal<colon)) { ! 202: *vp=equal; ! 203: colon=NULL; ! 204: } else ! 205: *vp=colon; ! 206: ! 207: if(*vp==NULL) ! 208: return(NULL); ! 209: ! 210: *(*vp)=0; ! 211: truncsp(p); ! 212: ! 213: /* Parse value */ ! 214: (*vp)++; ! 215: SKIP_WHITESPACE(*vp); ! 216: if(colon!=NULL) ! 217: truncnl(*vp); /* "key : value" - truncate new-line chars only */ ! 218: else ! 219: truncsp(*vp); /* "key = value" - truncate all white-space chars */ ! 220: ! 221: return(p); ! 222: } ! 223: ! 224: static char* read_value(FILE* fp, const char* section, const char* key, char* value) ! 225: { ! 226: char* p; ! 227: char* vp=NULL; ! 228: char str[INI_MAX_LINE_LEN]; ! 229: ! 230: if(fp==NULL) ! 231: return(NULL); ! 232: ! 233: if(!seek_section(fp,section)) ! 234: return(NULL); ! 235: ! 236: while(!feof(fp)) { ! 237: if(fgets(str,sizeof(str),fp)==NULL) ! 238: break; ! 239: if(is_eof(str)) ! 240: break; ! 241: if((p=key_name(str,&vp))==NULL) ! 242: continue; ! 243: if(p==INI_NEW_SECTION) ! 244: break; ! 245: if(stricmp(p,key)!=0) ! 246: continue; ! 247: if(vp==NULL) ! 248: break; ! 249: /* key found */ ! 250: sprintf(value,"%.*s",INI_MAX_VALUE_LEN-1,vp); ! 251: return(value); ! 252: } ! 253: ! 254: return(NULL); ! 255: } ! 256: ! 257: static size_t get_value(str_list_t list, const char* section, const char* key, char* value) ! 258: { ! 259: char str[INI_MAX_LINE_LEN]; ! 260: char* p; ! 261: char* vp; ! 262: size_t i; ! 263: ! 264: value[0]=0; ! 265: for(i=find_section(list, section); list[i]!=NULL; i++) { ! 266: SAFECOPY(str, list[i]); ! 267: if(is_eof(str)) ! 268: break; ! 269: if((p=key_name(str,&vp))==NULL) ! 270: continue; ! 271: if(p==INI_NEW_SECTION) ! 272: break; ! 273: if(stricmp(p,key)!=0) ! 274: continue; ! 275: sprintf(value,"%.*s",INI_MAX_VALUE_LEN-1,vp); ! 276: return(i); ! 277: } ! 278: ! 279: return(i); ! 280: } ! 281: ! 282: BOOL iniSectionExists(str_list_t list, const char* section) ! 283: { ! 284: size_t i; ! 285: ! 286: if(section==ROOT_SECTION) ! 287: return(TRUE); ! 288: ! 289: i=find_section_index(list,section); ! 290: return(list[i]!=NULL); ! 291: } ! 292: ! 293: BOOL iniKeyExists(str_list_t list, const char* section, const char* key) ! 294: { ! 295: char val[INI_MAX_VALUE_LEN]; ! 296: size_t i; ! 297: ! 298: i=get_value(list, section, key, val); ! 299: ! 300: if(list[i]==NULL || *(list[i])==INI_OPEN_SECTION_CHAR) ! 301: return(FALSE); ! 302: ! 303: return(TRUE); ! 304: } ! 305: ! 306: BOOL iniValueExists(str_list_t list, const char* section, const char* key) ! 307: { ! 308: char val[INI_MAX_VALUE_LEN]; ! 309: ! 310: get_value(list, section, key, val); ! 311: ! 312: return(val[0]!=0); ! 313: } ! 314: ! 315: BOOL iniRemoveKey(str_list_t* list, const char* section, const char* key) ! 316: { ! 317: char val[INI_MAX_VALUE_LEN]; ! 318: size_t i; ! 319: ! 320: i=get_value(*list, section, key, val); ! 321: ! 322: if((*list)[i]==NULL || *(*list)[i]==INI_OPEN_SECTION_CHAR) ! 323: return(FALSE); ! 324: ! 325: return(strListDelete(list,i)); ! 326: } ! 327: ! 328: BOOL iniRemoveValue(str_list_t* list, const char* section, const char* key) ! 329: { ! 330: char val[INI_MAX_VALUE_LEN]; ! 331: size_t i; ! 332: char* p; ! 333: char* vp=NULL; ! 334: ! 335: i=get_value(*list, section, key, val); ! 336: ! 337: p=key_name((*list)[i], &vp); ! 338: if(vp==NULL) ! 339: return(FALSE); ! 340: ! 341: strcat(p,"="); ! 342: return(TRUE); ! 343: } ! 344: ! 345: BOOL iniRemoveSection(str_list_t* list, const char* section) ! 346: { ! 347: size_t i; ! 348: ! 349: i=find_section_index(*list,section); ! 350: if((*list)[i]==NULL) /* not found */ ! 351: return(FALSE); ! 352: do { ! 353: strListDelete(list,i); ! 354: } while((*list)[i]!=NULL && *(*list)[i]!=INI_OPEN_SECTION_CHAR); ! 355: ! 356: return(TRUE); ! 357: } ! 358: ! 359: BOOL iniRenameSection(str_list_t* list, const char* section, const char* newname) ! 360: { ! 361: char str[INI_MAX_LINE_LEN]; ! 362: size_t i; ! 363: ! 364: if(section==ROOT_SECTION) ! 365: return(FALSE); ! 366: ! 367: i=find_section_index(*list,newname); ! 368: if((*list)[i]!=NULL) /* duplicate */ ! 369: return(FALSE); ! 370: ! 371: i=find_section_index(*list,section); ! 372: if((*list)[i]==NULL) /* not found */ ! 373: return(FALSE); ! 374: ! 375: SAFEPRINTF(str,"[%s]",newname); ! 376: return(strListReplace(*list, i, str)!=NULL); ! 377: } ! 378: ! 379: static size_t ini_add_section(str_list_t* list, const char* section ! 380: ,ini_style_t* style, size_t index) ! 381: { ! 382: char str[INI_MAX_LINE_LEN]; ! 383: ! 384: if(section==ROOT_SECTION) ! 385: return(0); ! 386: ! 387: if((*list)[index]!=NULL) ! 388: return(index); ! 389: ! 390: if(style==NULL) ! 391: style=&default_style; ! 392: if(index > 0 && style->section_separator!=NULL) ! 393: strListAppend(list, style->section_separator, index++); ! 394: SAFEPRINTF(str,"[%s]",section); ! 395: strListAppend(list, str, index); ! 396: ! 397: return(index); ! 398: } ! 399: ! 400: size_t iniAddSection(str_list_t* list, const char* section, ini_style_t* style) ! 401: { ! 402: if(section==ROOT_SECTION) ! 403: return(0); ! 404: ! 405: return ini_add_section(list,section,style,find_section_index(*list, section)); ! 406: } ! 407: ! 408: size_t iniAppendSection(str_list_t* list, const char* section, ini_style_t* style) ! 409: { ! 410: if(section==ROOT_SECTION) ! 411: return(0); ! 412: ! 413: return ini_add_section(list,section,style,strListCount(*list)); ! 414: } ! 415: ! 416: char* iniSetString(str_list_t* list, const char* section, const char* key, const char* value ! 417: ,ini_style_t* style) ! 418: { ! 419: char str[INI_MAX_LINE_LEN]; ! 420: char curval[INI_MAX_VALUE_LEN]; ! 421: size_t i; ! 422: ! 423: if(style==NULL) ! 424: style=&default_style; ! 425: ! 426: iniAddSection(list, section, style); ! 427: ! 428: if(key==NULL) ! 429: return(NULL); ! 430: if(style->key_prefix==NULL) ! 431: style->key_prefix=""; ! 432: if(style->value_separator==NULL) ! 433: style->value_separator="="; ! 434: if(value==NULL) ! 435: value=""; ! 436: safe_snprintf(str, sizeof(str), "%s%-*s%s%s" ! 437: , style->key_prefix, style->key_len, key, style->value_separator, value); ! 438: i=get_value(*list, section, key, curval); ! 439: if((*list)[i]==NULL || *(*list)[i]==INI_OPEN_SECTION_CHAR) { ! 440: while(i && *(*list)[i-1]==0) i--; /* Insert before blank lines, not after */ ! 441: return strListInsert(list, str, i); ! 442: } ! 443: ! 444: if(strcmp(curval,value)==0) ! 445: return((*list)[i]); /* no change */ ! 446: ! 447: return strListReplace(*list, i, str); ! 448: } ! 449: ! 450: char* iniSetInteger(str_list_t* list, const char* section, const char* key, long value ! 451: ,ini_style_t* style) ! 452: { ! 453: char str[INI_MAX_VALUE_LEN]; ! 454: ! 455: SAFEPRINTF(str,"%ld",value); ! 456: return iniSetString(list, section, key, str, style); ! 457: } ! 458: ! 459: char* iniSetShortInt(str_list_t* list, const char* section, const char* key, ushort value ! 460: ,ini_style_t* style) ! 461: { ! 462: char str[INI_MAX_VALUE_LEN]; ! 463: ! 464: SAFEPRINTF(str,"%hu",value); ! 465: return iniSetString(list, section, key, str, style); ! 466: } ! 467: ! 468: char* iniSetLongInt(str_list_t* list, const char* section, const char* key, ulong value ! 469: ,ini_style_t* style) ! 470: { ! 471: char str[INI_MAX_VALUE_LEN]; ! 472: ! 473: SAFEPRINTF(str,"%lu",value); ! 474: return iniSetString(list, section, key, str, style); ! 475: } ! 476: ! 477: char* iniSetHexInt(str_list_t* list, const char* section, const char* key, ulong value ! 478: ,ini_style_t* style) ! 479: { ! 480: char str[INI_MAX_VALUE_LEN]; ! 481: ! 482: SAFEPRINTF(str,"0x%lx",value); ! 483: return iniSetString(list, section, key, str, style); ! 484: } ! 485: ! 486: char* iniSetFloat(str_list_t* list, const char* section, const char* key, double value ! 487: ,ini_style_t* style) ! 488: { ! 489: char str[INI_MAX_VALUE_LEN]; ! 490: ! 491: SAFEPRINTF(str,"%g",value); ! 492: return iniSetString(list, section, key, str, style); ! 493: } ! 494: ! 495: char* iniSetBytes(str_list_t* list, const char* section, const char* key, ulong unit ! 496: ,ulong value, ini_style_t* style) ! 497: { ! 498: char str[INI_MAX_VALUE_LEN]; ! 499: double bytes; ! 500: ! 501: switch(unit) { ! 502: case 1024*1024*1024: ! 503: SAFEPRINTF(str,"%luG",value); ! 504: break; ! 505: case 1024*1024: ! 506: SAFEPRINTF(str,"%luM",value); ! 507: break; ! 508: case 1024: ! 509: SAFEPRINTF(str,"%luK",value); ! 510: break; ! 511: default: ! 512: if(unit<1) ! 513: unit=1; ! 514: bytes=value*unit; ! 515: ! 516: if(fmod(bytes,1024.0*1024.0*1024.0*1024.0)==0) ! 517: SAFEPRINTF(str,"%gT",bytes/(1024.0*1024.0*1024.0*1024.0)); ! 518: else if(fmod(bytes,1024*1024*1024)==0) ! 519: SAFEPRINTF(str,"%gG",bytes/(1024*1024*1024)); ! 520: else if(fmod(bytes,1024*1024)==0) ! 521: SAFEPRINTF(str,"%gM",bytes/(1024*1024)); ! 522: else if(fmod(bytes,1024)==0) ! 523: SAFEPRINTF(str,"%gK",bytes/1024); ! 524: else ! 525: SAFEPRINTF(str,"%lu",(ulong)bytes); ! 526: } ! 527: ! 528: return iniSetString(list, section, key, str, style); ! 529: } ! 530: ! 531: #if !defined(NO_SOCKET_SUPPORT) ! 532: char* iniSetIpAddress(str_list_t* list, const char* section, const char* key, ulong value ! 533: ,ini_style_t* style) ! 534: { ! 535: struct in_addr in_addr; ! 536: in_addr.s_addr=htonl(value); ! 537: return iniSetString(list, section, key, inet_ntoa(in_addr), style); ! 538: } ! 539: #endif ! 540: ! 541: char* iniSetBool(str_list_t* list, const char* section, const char* key, BOOL value ! 542: ,ini_style_t* style) ! 543: { ! 544: return iniSetString(list, section, key, value ? "true":"false", style); ! 545: } ! 546: ! 547: char* iniSetDateTime(str_list_t* list, const char* section, const char* key ! 548: ,BOOL include_time, time_t value, ini_style_t* style) ! 549: { ! 550: char str[INI_MAX_VALUE_LEN]; ! 551: char tstr[32]; ! 552: char* p; ! 553: ! 554: if(value==0) ! 555: SAFECOPY(str,"Never"); ! 556: else if((p=CTIME_R(&value,tstr))==NULL) ! 557: SAFEPRINTF(str,"0x%lx",value); ! 558: else if(!include_time) /* reformat into "Mon DD YYYY" */ ! 559: safe_snprintf(str,sizeof(str),"%.3s %.2s %.4s" ,p+4,p+8,p+20); ! 560: else /* reformat into "Mon DD YYYY HH:MM:SS" */ ! 561: safe_snprintf(str,sizeof(str),"%.3s %.2s %.4s %.8s" ,p+4,p+8,p+20,p+11); ! 562: ! 563: return iniSetString(list, section, key, str, style); ! 564: } ! 565: ! 566: char* iniSetEnum(str_list_t* list, const char* section, const char* key, str_list_t names, unsigned value ! 567: ,ini_style_t* style) ! 568: { ! 569: if(value < strListCount(names)) ! 570: return iniSetString(list, section, key, names[value], style); ! 571: ! 572: return iniSetLongInt(list, section, key, value, style); ! 573: } ! 574: ! 575: char* iniSetNamedInt(str_list_t* list, const char* section, const char* key, named_long_t* names ! 576: ,long value, ini_style_t* style) ! 577: { ! 578: size_t i; ! 579: ! 580: for(i=0;names[i].name!=NULL;i++) ! 581: if(names[i].value==value) ! 582: return iniSetString(list, section, key, names[i].name, style); ! 583: ! 584: return iniSetInteger(list, section, key, value, style); ! 585: } ! 586: ! 587: char* iniSetNamedFloat(str_list_t* list, const char* section, const char* key, named_double_t* names ! 588: ,double value, ini_style_t* style) ! 589: { ! 590: size_t i; ! 591: ! 592: for(i=0;names[i].name!=NULL;i++) ! 593: if(names[i].value==value) ! 594: return iniSetString(list, section, key, names[i].name, style); ! 595: ! 596: return iniSetFloat(list, section, key, value, style); ! 597: } ! 598: ! 599: char* iniSetBitField(str_list_t* list, const char* section, const char* key ! 600: ,ini_bitdesc_t* bitdesc, ulong value, ini_style_t* style) ! 601: { ! 602: char str[INI_MAX_VALUE_LEN]; ! 603: int i; ! 604: ! 605: if(style==NULL) ! 606: style=&default_style; ! 607: if(style->bit_separator==NULL) ! 608: style->bit_separator="|"; ! 609: str[0]=0; ! 610: for(i=0;bitdesc[i].name;i++) { ! 611: if((value&bitdesc[i].bit)==0) ! 612: continue; ! 613: if(str[0]) ! 614: strcat(str,style->bit_separator); ! 615: strcat(str,bitdesc[i].name); ! 616: value&=~bitdesc[i].bit; ! 617: } ! 618: if(value) { /* left over bits? */ ! 619: if(str[0]) ! 620: strcat(str,style->bit_separator); ! 621: sprintf(str+strlen(str), "0x%lX", value); ! 622: } ! 623: return iniSetString(list, section, key, str, style); ! 624: } ! 625: ! 626: char* iniSetStringList(str_list_t* list, const char* section, const char* key ! 627: ,const char* sep, str_list_t val_list, ini_style_t* style) ! 628: { ! 629: char value[INI_MAX_VALUE_LEN]; ! 630: size_t i; ! 631: ! 632: value[0]=0; ! 633: ! 634: if(sep==NULL) ! 635: sep=","; ! 636: ! 637: if(val_list!=NULL) ! 638: for(i=0; val_list[i]!=NULL; i++) { ! 639: if(value[0]) ! 640: strcat(value,sep); ! 641: strcat(value,val_list[i]); ! 642: } ! 643: ! 644: return iniSetString(list, section, key, value, style); ! 645: } ! 646: ! 647: char* iniReadString(FILE* fp, const char* section, const char* key, const char* deflt, char* value) ! 648: { ! 649: if(read_value(fp,section,key,value)==NULL || *value==0 /* blank */) { ! 650: if(deflt!=NULL) ! 651: sprintf(value,"%.*s",INI_MAX_VALUE_LEN-1,deflt); ! 652: return((char*)deflt); ! 653: } ! 654: ! 655: return(value); ! 656: } ! 657: ! 658: char* iniGetString(str_list_t list, const char* section, const char* key, const char* deflt, char* value) ! 659: { ! 660: get_value(list, section, key, value); ! 661: ! 662: if(*value==0 /* blank value or missing key */) { ! 663: if(deflt!=NULL) ! 664: sprintf(value,"%.*s",INI_MAX_VALUE_LEN-1,deflt); ! 665: return((char*)deflt); ! 666: } ! 667: ! 668: return(value); ! 669: } ! 670: ! 671: static str_list_t splitList(char* list, const char* sep) ! 672: { ! 673: char* token; ! 674: char* tmp; ! 675: ulong items=0; ! 676: str_list_t lp; ! 677: ! 678: if((lp=strListInit())==NULL) ! 679: return(NULL); ! 680: ! 681: if(sep==NULL) ! 682: sep=","; ! 683: ! 684: token=strtok_r(list,sep,&tmp); ! 685: while(token!=NULL) { ! 686: SKIP_WHITESPACE(token); ! 687: truncsp(token); ! 688: if(strListAppend(&lp,token,items++)==NULL) ! 689: break; ! 690: token=strtok_r(NULL,sep,&tmp); ! 691: } ! 692: ! 693: return(lp); ! 694: } ! 695: ! 696: str_list_t iniReadStringList(FILE* fp, const char* section, const char* key ! 697: ,const char* sep, const char* deflt) ! 698: { ! 699: char* value; ! 700: char buf[INI_MAX_VALUE_LEN]; ! 701: char list[INI_MAX_VALUE_LEN]; ! 702: ! 703: if((value=read_value(fp,section,key,buf))==NULL || *value==0 /* blank */) ! 704: value=(char*)deflt; ! 705: ! 706: if(value==NULL) ! 707: return(NULL); ! 708: ! 709: SAFECOPY(list,value); ! 710: ! 711: return(splitList(list,sep)); ! 712: } ! 713: ! 714: str_list_t iniGetStringList(str_list_t list, const char* section, const char* key ! 715: ,const char* sep, const char* deflt) ! 716: { ! 717: char value[INI_MAX_VALUE_LEN]; ! 718: ! 719: get_value(list, section, key, value); ! 720: ! 721: if(*value==0 /* blank value or missing key */) { ! 722: if(deflt==NULL) ! 723: return(NULL); ! 724: SAFECOPY(value,deflt); ! 725: } ! 726: ! 727: return(splitList(value,sep)); ! 728: } ! 729: ! 730: void* iniFreeStringList(str_list_t list) ! 731: { ! 732: strListFree(&list); ! 733: return(list); ! 734: } ! 735: ! 736: void* iniFreeNamedStringList(named_string_t** list) ! 737: { ! 738: ulong i; ! 739: ! 740: if(list==NULL) ! 741: return(NULL); ! 742: ! 743: for(i=0;list[i]!=NULL;i++) { ! 744: if(list[i]->name!=NULL) ! 745: free(list[i]->name); ! 746: if(list[i]->value!=NULL) ! 747: free(list[i]->value); ! 748: free(list[i]); ! 749: } ! 750: ! 751: free(list); ! 752: return(NULL); ! 753: } ! 754: ! 755: str_list_t iniReadSectionList(FILE* fp, const char* prefix) ! 756: { ! 757: char* p; ! 758: char str[INI_MAX_LINE_LEN]; ! 759: ulong items=0; ! 760: str_list_t lp; ! 761: ! 762: if((lp=strListInit())==NULL) ! 763: return(NULL); ! 764: ! 765: if(fp==NULL) ! 766: return(lp); ! 767: ! 768: rewind(fp); ! 769: ! 770: while(!feof(fp)) { ! 771: if(fgets(str,sizeof(str),fp)==NULL) ! 772: break; ! 773: if(is_eof(str)) ! 774: break; ! 775: if((p=section_name(str))==NULL) ! 776: continue; ! 777: if(prefix!=NULL) ! 778: if(strnicmp(p,prefix,strlen(prefix))!=0) ! 779: continue; ! 780: if(strListAppend(&lp,p,items++)==NULL) ! 781: break; ! 782: } ! 783: ! 784: return(lp); ! 785: } ! 786: ! 787: str_list_t iniGetSectionList(str_list_t list, const char* prefix) ! 788: { ! 789: char* p; ! 790: char str[INI_MAX_LINE_LEN]; ! 791: ulong i,items=0; ! 792: str_list_t lp; ! 793: ! 794: if((lp=strListInit())==NULL) ! 795: return(NULL); ! 796: ! 797: if(list==NULL) ! 798: return(lp); ! 799: ! 800: for(i=0; list[i]!=NULL; i++) { ! 801: SAFECOPY(str,list[i]); ! 802: if(is_eof(str)) ! 803: break; ! 804: if((p=section_name(str))==NULL) ! 805: continue; ! 806: if(prefix!=NULL) ! 807: if(strnicmp(p,prefix,strlen(prefix))!=0) ! 808: continue; ! 809: if(strListAppend(&lp,p,items++)==NULL) ! 810: break; ! 811: } ! 812: ! 813: return(lp); ! 814: } ! 815: ! 816: size_t iniGetSectionCount(str_list_t list, const char* prefix) ! 817: { ! 818: char* p; ! 819: char str[INI_MAX_LINE_LEN]; ! 820: size_t i,items=0; ! 821: ! 822: if(list==NULL) ! 823: return(0); ! 824: ! 825: for(i=0; list[i]!=NULL; i++) { ! 826: SAFECOPY(str,list[i]); ! 827: if(is_eof(str)) ! 828: break; ! 829: if((p=section_name(str))==NULL) ! 830: continue; ! 831: if(prefix!=NULL) ! 832: if(strnicmp(p,prefix,strlen(prefix))!=0) ! 833: continue; ! 834: items++; ! 835: } ! 836: ! 837: return(items); ! 838: } ! 839: ! 840: ! 841: str_list_t iniReadKeyList(FILE* fp, const char* section) ! 842: { ! 843: char* p; ! 844: char* vp; ! 845: char str[INI_MAX_LINE_LEN]; ! 846: ulong items=0; ! 847: str_list_t lp; ! 848: ! 849: if((lp=strListInit())==NULL) ! 850: return(NULL); ! 851: ! 852: if(fp==NULL) ! 853: return(lp); ! 854: ! 855: rewind(fp); ! 856: ! 857: if(!seek_section(fp,section)) ! 858: return(lp); ! 859: ! 860: while(!feof(fp)) { ! 861: if(fgets(str,sizeof(str),fp)==NULL) ! 862: break; ! 863: if(is_eof(str)) ! 864: break; ! 865: if((p=key_name(str,&vp))==NULL) ! 866: continue; ! 867: if(p==INI_NEW_SECTION) ! 868: break; ! 869: if(strListAppend(&lp,p,items++)==NULL) ! 870: break; ! 871: } ! 872: ! 873: return(lp); ! 874: } ! 875: ! 876: str_list_t iniGetKeyList(str_list_t list, const char* section) ! 877: { ! 878: char* p; ! 879: char* vp; ! 880: char str[INI_MAX_LINE_LEN]; ! 881: ulong i,items=0; ! 882: str_list_t lp; ! 883: ! 884: if((lp=strListInit())==NULL) ! 885: return(NULL); ! 886: ! 887: if(list==NULL) ! 888: return(lp); ! 889: ! 890: for(i=find_section(list,section);list[i]!=NULL;i++) { ! 891: SAFECOPY(str,list[i]); ! 892: if(is_eof(str)) ! 893: break; ! 894: if((p=key_name(str,&vp))==NULL) ! 895: continue; ! 896: if(p==INI_NEW_SECTION) ! 897: break; ! 898: if(strListAppend(&lp,p,items++)==NULL) ! 899: break; ! 900: } ! 901: ! 902: return(lp); ! 903: } ! 904: ! 905: ! 906: named_string_t** ! 907: iniReadNamedStringList(FILE* fp, const char* section) ! 908: { ! 909: char* name; ! 910: char* value; ! 911: char str[INI_MAX_LINE_LEN]; ! 912: ulong items=0; ! 913: named_string_t** lp; ! 914: named_string_t** np; ! 915: ! 916: if((lp=(named_string_t**)malloc(sizeof(named_string_t*)))==NULL) ! 917: return(NULL); ! 918: ! 919: *lp=NULL; ! 920: ! 921: if(fp==NULL) ! 922: return(lp); ! 923: ! 924: rewind(fp); ! 925: ! 926: if(!seek_section(fp,section)) ! 927: return(lp); ! 928: ! 929: while(!feof(fp)) { ! 930: if(fgets(str,sizeof(str),fp)==NULL) ! 931: break; ! 932: if(is_eof(str)) ! 933: break; ! 934: if((name=key_name(str,&value))==NULL) ! 935: continue; ! 936: if(name==INI_NEW_SECTION) ! 937: break; ! 938: if((np=(named_string_t**)realloc(lp,sizeof(named_string_t*)*(items+2)))==NULL) ! 939: break; ! 940: lp=np; ! 941: if((lp[items]=(named_string_t*)malloc(sizeof(named_string_t)))==NULL) ! 942: break; ! 943: if((lp[items]->name=strdup(name))==NULL) ! 944: break; ! 945: if((lp[items]->value=strdup(value))==NULL) ! 946: break; ! 947: items++; ! 948: } ! 949: ! 950: lp[items]=NULL; /* terminate list */ ! 951: ! 952: return(lp); ! 953: } ! 954: ! 955: named_string_t** ! 956: iniGetNamedStringList(str_list_t list, const char* section) ! 957: { ! 958: char* name; ! 959: char* value; ! 960: char str[INI_MAX_LINE_LEN]; ! 961: ulong i,items=0; ! 962: named_string_t** lp; ! 963: named_string_t** np; ! 964: ! 965: if((lp=(named_string_t**)malloc(sizeof(named_string_t*)))==NULL) ! 966: return(NULL); ! 967: ! 968: *lp=NULL; ! 969: ! 970: if(list==NULL) ! 971: return(lp); ! 972: ! 973: for(i=find_section(list,section);list[i]!=NULL;i++) { ! 974: SAFECOPY(str,list[i]); ! 975: if(is_eof(str)) ! 976: break; ! 977: if((name=key_name(str,&value))==NULL) ! 978: continue; ! 979: if(name==INI_NEW_SECTION) ! 980: break; ! 981: if((np=(named_string_t**)realloc(lp,sizeof(named_string_t*)*(items+2)))==NULL) ! 982: break; ! 983: lp=np; ! 984: if((lp[items]=(named_string_t*)malloc(sizeof(named_string_t)))==NULL) ! 985: break; ! 986: if((lp[items]->name=strdup(name))==NULL) ! 987: break; ! 988: if((lp[items]->value=strdup(value))==NULL) ! 989: break; ! 990: items++; ! 991: } ! 992: ! 993: lp[items]=NULL; /* terminate list */ ! 994: ! 995: return(lp); ! 996: } ! 997: ! 998: ! 999: /* These functions read a single key of the specified type */ ! 1000: ! 1001: static BOOL isTrue(const char* value) ! 1002: { ! 1003: return(stricmp(value,"TRUE")==0 || stricmp(value,"YES")==0 || stricmp(value,"ON")==0); ! 1004: } ! 1005: ! 1006: static long parseInteger(const char* value) ! 1007: { ! 1008: if(isTrue(value)) ! 1009: return(TRUE); ! 1010: ! 1011: return(strtol(value,NULL,0)); ! 1012: } ! 1013: ! 1014: static ulong parseLongInteger(const char* value) ! 1015: { ! 1016: if(isTrue(value)) ! 1017: return(TRUE); ! 1018: ! 1019: return(strtoul(value,NULL,0)); ! 1020: } ! 1021: ! 1022: static BOOL parseBool(const char* value) ! 1023: { ! 1024: return(INT_TO_BOOL(parseInteger(value))); ! 1025: } ! 1026: ! 1027: long iniReadInteger(FILE* fp, const char* section, const char* key, long deflt) ! 1028: { ! 1029: char* value; ! 1030: char buf[INI_MAX_VALUE_LEN]; ! 1031: ! 1032: if((value=read_value(fp,section,key,buf))==NULL) ! 1033: return(deflt); ! 1034: ! 1035: if(*value==0) /* blank value */ ! 1036: return(deflt); ! 1037: ! 1038: return(parseInteger(value)); ! 1039: } ! 1040: ! 1041: long iniGetInteger(str_list_t list, const char* section, const char* key, long deflt) ! 1042: { ! 1043: char value[INI_MAX_VALUE_LEN]; ! 1044: ! 1045: get_value(list, section, key, value); ! 1046: ! 1047: if(*value==0) /* blank value or missing key */ ! 1048: return(deflt); ! 1049: ! 1050: return(parseInteger(value)); ! 1051: } ! 1052: ! 1053: ushort iniReadShortInt(FILE* fp, const char* section, const char* key, ushort deflt) ! 1054: { ! 1055: return((ushort)iniReadInteger(fp, section, key, deflt)); ! 1056: } ! 1057: ! 1058: ushort iniGetShortInt(str_list_t list, const char* section, const char* key, ushort deflt) ! 1059: { ! 1060: return((ushort)iniGetInteger(list, section, key, deflt)); ! 1061: } ! 1062: ! 1063: ulong iniReadLongInt(FILE* fp, const char* section, const char* key, ulong deflt) ! 1064: { ! 1065: char* value; ! 1066: char buf[INI_MAX_VALUE_LEN]; ! 1067: ! 1068: if((value=read_value(fp,section,key,buf))==NULL) ! 1069: return(deflt); ! 1070: ! 1071: if(*value==0) /* blank value */ ! 1072: return(deflt); ! 1073: ! 1074: return(parseLongInteger(value)); ! 1075: } ! 1076: ! 1077: ulong iniGetLongInt(str_list_t list, const char* section, const char* key, ulong deflt) ! 1078: { ! 1079: char value[INI_MAX_VALUE_LEN]; ! 1080: ! 1081: get_value(list, section, key, value); ! 1082: ! 1083: if(*value==0) /* blank value or missing key */ ! 1084: return(deflt); ! 1085: ! 1086: return(parseLongInteger(value)); ! 1087: } ! 1088: ! 1089: static ulong parseBytes(const char* value, ulong unit) ! 1090: { ! 1091: char* p=NULL; ! 1092: double bytes; ! 1093: ! 1094: bytes=strtod(value,&p); ! 1095: if(p!=NULL) { ! 1096: switch(toupper(*p)) { ! 1097: case 'T': ! 1098: bytes*=1024; ! 1099: case 'G': ! 1100: bytes*=1024; ! 1101: case 'M': ! 1102: bytes*=1024; ! 1103: case 'K': ! 1104: bytes*=1024; ! 1105: break; ! 1106: } ! 1107: } ! 1108: return((ulong)(unit>1 ? (bytes/unit):bytes)); ! 1109: } ! 1110: ! 1111: ulong iniReadBytes(FILE* fp, const char* section, const char* key, ulong unit, ulong deflt) ! 1112: { ! 1113: char* value; ! 1114: char buf[INI_MAX_VALUE_LEN]; ! 1115: ! 1116: if((value=read_value(fp,section,key,buf))==NULL) ! 1117: return(deflt); ! 1118: ! 1119: if(*value==0) /* blank value */ ! 1120: return(deflt); ! 1121: ! 1122: return(parseBytes(value,unit)); ! 1123: } ! 1124: ! 1125: ulong iniGetBytes(str_list_t list, const char* section, const char* key, ulong unit, ulong deflt) ! 1126: { ! 1127: char value[INI_MAX_VALUE_LEN]; ! 1128: ! 1129: get_value(list, section, key, value); ! 1130: ! 1131: if(*value==0) /* blank value or missing key */ ! 1132: return(deflt); ! 1133: ! 1134: return(parseBytes(value,unit)); ! 1135: } ! 1136: ! 1137: #if !defined(NO_SOCKET_SUPPORT) ! 1138: ! 1139: int iniGetSocketOptions(str_list_t list, const char* section, SOCKET sock ! 1140: ,char* error, size_t errlen) ! 1141: { ! 1142: int i; ! 1143: int result; ! 1144: char* name; ! 1145: BYTE* vp; ! 1146: socklen_t len; ! 1147: int option; ! 1148: int level; ! 1149: int value; ! 1150: LINGER linger; ! 1151: socket_option_t* socket_options=getSocketOptionList(); ! 1152: ! 1153: for(i=0;socket_options[i].name!=NULL;i++) { ! 1154: name = socket_options[i].name; ! 1155: if(!iniValueExists(list, section, name)) ! 1156: continue; ! 1157: value=iniGetInteger(list, section, name, 0); ! 1158: ! 1159: vp=(BYTE*)&value; ! 1160: len=sizeof(value); ! 1161: ! 1162: level = socket_options[i].level; ! 1163: option = socket_options[i].value; ! 1164: ! 1165: if(option == SO_LINGER) { ! 1166: if(value) { ! 1167: linger.l_onoff = TRUE; ! 1168: linger.l_linger = value; ! 1169: } else { ! 1170: ZERO_VAR(linger); ! 1171: } ! 1172: vp=(BYTE*)&linger; ! 1173: len=sizeof(linger); ! 1174: } ! 1175: ! 1176: if((result=setsockopt(sock,level,option,vp,len)) != 0) { ! 1177: safe_snprintf(error,errlen,"%d setting socket option (%s, %d) to %d" ! 1178: ,ERROR_VALUE, name, option, value); ! 1179: return(result); ! 1180: } ! 1181: } ! 1182: ! 1183: return(0); ! 1184: } ! 1185: ! 1186: static ulong parseIpAddress(const char* value) ! 1187: { ! 1188: if(strchr(value,'.')==NULL) ! 1189: return(strtol(value,NULL,0)); ! 1190: ! 1191: return(ntohl(inet_addr(value))); ! 1192: } ! 1193: ! 1194: ulong iniReadIpAddress(FILE* fp, const char* section, const char* key, ulong deflt) ! 1195: { ! 1196: char buf[INI_MAX_VALUE_LEN]; ! 1197: char* value; ! 1198: ! 1199: if((value=read_value(fp,section,key,buf))==NULL) ! 1200: return(deflt); ! 1201: ! 1202: if(*value==0) /* blank value */ ! 1203: return(deflt); ! 1204: ! 1205: return(parseIpAddress(value)); ! 1206: } ! 1207: ! 1208: ulong iniGetIpAddress(str_list_t list, const char* section, const char* key, ulong deflt) ! 1209: { ! 1210: char value[INI_MAX_VALUE_LEN]; ! 1211: ! 1212: get_value(list, section, key, value); ! 1213: ! 1214: if(*value==0) /* blank value or missing key */ ! 1215: return(deflt); ! 1216: ! 1217: return(parseIpAddress(value)); ! 1218: } ! 1219: ! 1220: #endif /* !NO_SOCKET_SUPPORT */ ! 1221: ! 1222: char* iniFileName(char* dest, size_t maxlen, const char* indir, const char* infname) ! 1223: { ! 1224: char dir[MAX_PATH+1]; ! 1225: char fname[MAX_PATH+1]; ! 1226: char ext[MAX_PATH+1]; ! 1227: char* p; ! 1228: ! 1229: SAFECOPY(dir,indir); ! 1230: backslash(dir); ! 1231: SAFECOPY(fname,infname); ! 1232: ext[0]=0; ! 1233: if((p=getfext(fname))!=NULL) { ! 1234: SAFECOPY(ext,p); ! 1235: *p=0; ! 1236: } ! 1237: ! 1238: #if !defined(NO_SOCKET_SUPPORT) ! 1239: { ! 1240: char hostname[128]; ! 1241: ! 1242: if(gethostname(hostname,sizeof(hostname))==0) { ! 1243: safe_snprintf(dest,maxlen,"%s%s.%s%s",dir,fname,hostname,ext); ! 1244: if(fexistcase(dest)) /* path/file.host.domain.ini */ ! 1245: return(dest); ! 1246: if((p=strchr(hostname,'.'))!=NULL) { ! 1247: *p=0; ! 1248: safe_snprintf(dest,maxlen,"%s%s.%s%s",dir,fname,hostname,ext); ! 1249: if(fexistcase(dest)) /* path/file.host.ini */ ! 1250: return(dest); ! 1251: } ! 1252: } ! 1253: } ! 1254: #endif ! 1255: ! 1256: safe_snprintf(dest,maxlen,"%s%s.%s%s",dir,fname,PLATFORM_DESC,ext); ! 1257: if(fexistcase(dest)) /* path/file.platform.ini */ ! 1258: return(dest); ! 1259: ! 1260: safe_snprintf(dest,maxlen,"%s%s%s",dir,fname,ext); ! 1261: fexistcase(dest); /* path/file.ini */ ! 1262: return(dest); ! 1263: } ! 1264: ! 1265: double iniReadFloat(FILE* fp, const char* section, const char* key, double deflt) ! 1266: { ! 1267: char buf[INI_MAX_VALUE_LEN]; ! 1268: char* value; ! 1269: ! 1270: if((value=read_value(fp,section,key,buf))==NULL) ! 1271: return(deflt); ! 1272: ! 1273: if(*value==0) /* blank value */ ! 1274: return(deflt); ! 1275: ! 1276: return(atof(value)); ! 1277: } ! 1278: ! 1279: double iniGetFloat(str_list_t list, const char* section, const char* key, double deflt) ! 1280: { ! 1281: char value[INI_MAX_VALUE_LEN]; ! 1282: ! 1283: get_value(list, section, key, value); ! 1284: ! 1285: if(*value==0) /* blank value or missing key */ ! 1286: return(deflt); ! 1287: ! 1288: return(atof(value)); ! 1289: } ! 1290: ! 1291: BOOL iniReadBool(FILE* fp, const char* section, const char* key, BOOL deflt) ! 1292: { ! 1293: char buf[INI_MAX_VALUE_LEN]; ! 1294: char* value; ! 1295: ! 1296: if((value=read_value(fp,section,key,buf))==NULL) ! 1297: return(deflt); ! 1298: ! 1299: if(*value==0) /* blank value */ ! 1300: return(deflt); ! 1301: ! 1302: return(parseBool(value)); ! 1303: } ! 1304: ! 1305: BOOL iniGetBool(str_list_t list, const char* section, const char* key, BOOL deflt) ! 1306: { ! 1307: char value[INI_MAX_VALUE_LEN]; ! 1308: ! 1309: get_value(list, section, key, value); ! 1310: ! 1311: if(*value==0) /* blank value or missing key */ ! 1312: return(deflt); ! 1313: ! 1314: return(parseBool(value)); ! 1315: } ! 1316: ! 1317: static BOOL validDate(struct tm* tm) ! 1318: { ! 1319: return(tm->tm_mon && tm->tm_mon<=12 ! 1320: && tm->tm_mday && tm->tm_mday<=31); ! 1321: } ! 1322: ! 1323: static time_t fixedDateTime(struct tm* tm, const char* tstr, char pm) ! 1324: { ! 1325: if(tm->tm_year<70) ! 1326: tm->tm_year+=100; /* 05 == 2005 (not 1905) and 70 == 1970 (not 2070) */ ! 1327: else if(tm->tm_year>1900) ! 1328: tm->tm_year-=1900; ! 1329: if(tm->tm_mon) ! 1330: tm->tm_mon--; /* zero-based month field */ ! 1331: ! 1332: /* hh:mm:ss [p] */ ! 1333: sscanf(tstr,"%u:%u:%u",&tm->tm_hour,&tm->tm_min,&tm->tm_sec); ! 1334: if(tm->tm_hour < 12 && (toupper(pm)=='P' || strchr(tstr,'p') || strchr(tstr,'P'))) ! 1335: tm->tm_hour += 12; /* pm, correct for 24 hour clock */ ! 1336: ! 1337: tm->tm_isdst=-1; /* auto-detect */ ! 1338: ! 1339: return(mktime(tm)); ! 1340: } ! 1341: ! 1342: static int getMonth(const char* month) ! 1343: { ! 1344: char *mon[]={"Jan","Feb","Mar","Apr","May","Jun" ! 1345: ,"Jul","Aug","Sep","Oct","Nov","Dec",NULL}; ! 1346: int i; ! 1347: ! 1348: for(i=0;mon[i]!=NULL;i++) ! 1349: if(strnicmp(month,mon[i],3)==0) ! 1350: return(i+1); ! 1351: ! 1352: return(atoi(month)); ! 1353: } ! 1354: ! 1355: static time_t parseDateTime(const char* value) ! 1356: { ! 1357: char month[INI_MAX_VALUE_LEN]; ! 1358: char tstr[INI_MAX_VALUE_LEN]; ! 1359: char pm=0; ! 1360: time_t t; ! 1361: struct tm tm; ! 1362: struct tm curr_tm; ! 1363: isoDate_t isoDate; ! 1364: isoTime_t isoTime; ! 1365: ! 1366: ZERO_VAR(tm); ! 1367: tstr[0]=0; ! 1368: ! 1369: /* Use current month and year as default */ ! 1370: t=time(NULL); ! 1371: if(localtime_r(&t,&curr_tm)!=NULL) { ! 1372: tm.tm_mon=curr_tm.tm_mon+1; /* convert to one-based (reversed later) */ ! 1373: tm.tm_year=curr_tm.tm_year; ! 1374: } ! 1375: ! 1376: /* CCYYMMDDTHHMMSS <--- ISO-8601 date and time format */ ! 1377: if(sscanf(value,"%uT%u" ! 1378: ,&isoDate,&isoTime)>=2) ! 1379: return(isoDateTime_to_time(isoDate,isoTime)); ! 1380: ! 1381: /* DD.MM.[CC]YY [time] [p] <-- Euro/Canadian numeric date format */ ! 1382: if(sscanf(value,"%u.%u.%u %s %c" ! 1383: ,&tm.tm_mday,&tm.tm_mon,&tm.tm_year,tstr,&pm)>=2 ! 1384: && validDate(&tm)) ! 1385: return(fixedDateTime(&tm,tstr,pm)); ! 1386: ! 1387: /* MM/DD/[CC]YY [time] [p] <-- American numeric date format */ ! 1388: if(sscanf(value,"%u%*c %u%*c %u %s %c" ! 1389: ,&tm.tm_mon,&tm.tm_mday,&tm.tm_year,tstr,&pm)>=2 ! 1390: && validDate(&tm)) ! 1391: return(fixedDateTime(&tm,tstr,pm)); ! 1392: ! 1393: /* DD[-]Mon [CC]YY [time] [p] <-- Perversion of RFC822 date format */ ! 1394: if(sscanf(value,"%u%*c %s %u %s %c" ! 1395: ,&tm.tm_mday,month,&tm.tm_year,tstr,&pm)>=2 ! 1396: && (tm.tm_mon=getMonth(month))!=0 ! 1397: && validDate(&tm)) ! 1398: return(fixedDateTime(&tm,tstr,pm)); ! 1399: ! 1400: /* Wday, DD Mon YYYY [time] <-- IETF standard (RFC2822) date format */ ! 1401: if(sscanf(value,"%*s %u %s %u %s" ! 1402: ,&tm.tm_mday,month,&tm.tm_year,tstr)>=2 ! 1403: && (tm.tm_mon=getMonth(month))!=0 ! 1404: && validDate(&tm)) ! 1405: return(fixedDateTime(&tm,tstr,0)); ! 1406: ! 1407: /* Mon DD[,] [CC]YY [time] [p] <-- Preferred date format */ ! 1408: if(sscanf(value,"%s %u%*c %u %s %c" ! 1409: ,month,&tm.tm_mday,&tm.tm_year,tstr,&pm)>=2 ! 1410: && (tm.tm_mon=getMonth(month))!=0 ! 1411: && validDate(&tm)) ! 1412: return(fixedDateTime(&tm,tstr,pm)); ! 1413: ! 1414: /* Wday Mon DD YYYY [time] <-- JavaScript (SpiderMonkey) Date.toString() format */ ! 1415: if(sscanf(value,"%*s %s %u %u %s" ! 1416: ,month,&tm.tm_mday,&tm.tm_year,tstr)>=2 ! 1417: && (tm.tm_mon=getMonth(month))!=0 ! 1418: && validDate(&tm)) ! 1419: return(fixedDateTime(&tm,tstr,0)); ! 1420: ! 1421: /* Wday Mon DD [time] YYYY <-- ctime() format */ ! 1422: if(sscanf(value,"%*s %s %u %s %u" ! 1423: ,month,&tm.tm_mday,tstr,&tm.tm_year)>=2 ! 1424: && (tm.tm_mon=getMonth(month))!=0 ! 1425: && validDate(&tm)) ! 1426: return(fixedDateTime(&tm,tstr,0)); ! 1427: ! 1428: return(strtoul(value,NULL,0)); ! 1429: } ! 1430: ! 1431: time_t iniReadDateTime(FILE* fp, const char* section, const char* key, time_t deflt) ! 1432: { ! 1433: char buf[INI_MAX_VALUE_LEN]; ! 1434: char* value; ! 1435: ! 1436: if((value=read_value(fp,section,key,buf))==NULL) ! 1437: return(deflt); ! 1438: ! 1439: if(*value==0) /* blank value */ ! 1440: return(deflt); ! 1441: ! 1442: return(parseDateTime(value)); ! 1443: } ! 1444: ! 1445: time_t iniGetDateTime(str_list_t list, const char* section, const char* key, time_t deflt) ! 1446: { ! 1447: char value[INI_MAX_VALUE_LEN]; ! 1448: ! 1449: get_value(list, section, key, value); ! 1450: ! 1451: if(*value==0) /* blank value or missing key */ ! 1452: return(deflt); ! 1453: ! 1454: return(parseDateTime(value)); ! 1455: } ! 1456: ! 1457: static unsigned parseEnum(const char* value, str_list_t names) ! 1458: { ! 1459: unsigned i; ! 1460: ! 1461: /* Look for exact matches first */ ! 1462: for(i=0; names[i]!=NULL; i++) ! 1463: if(stricmp(names[i],value)==0) ! 1464: return(i); ! 1465: ! 1466: /* Look for partial matches second */ ! 1467: for(i=0; names[i]!=NULL; i++) ! 1468: if(strnicmp(names[i],value,strlen(value))==0) ! 1469: return(i); ! 1470: ! 1471: return(strtoul(value,NULL,0)); ! 1472: } ! 1473: ! 1474: unsigned iniReadEnum(FILE* fp, const char* section, const char* key, str_list_t names, unsigned deflt) ! 1475: { ! 1476: char buf[INI_MAX_VALUE_LEN]; ! 1477: char* value; ! 1478: ! 1479: if((value=read_value(fp,section,key,buf))==NULL) ! 1480: return(deflt); ! 1481: ! 1482: if(*value==0) /* blank value */ ! 1483: return(deflt); ! 1484: ! 1485: return(parseEnum(value,names)); ! 1486: } ! 1487: ! 1488: unsigned iniGetEnum(str_list_t list, const char* section, const char* key, str_list_t names, unsigned deflt) ! 1489: { ! 1490: char value[INI_MAX_VALUE_LEN]; ! 1491: ! 1492: get_value(list, section, key, value); ! 1493: ! 1494: if(*value==0) /* blank value or missing key */ ! 1495: return(deflt); ! 1496: ! 1497: return(parseEnum(value,names)); ! 1498: } ! 1499: ! 1500: static long parseNamedInt(const char* value, named_long_t* names) ! 1501: { ! 1502: unsigned i; ! 1503: ! 1504: /* Look for exact matches first */ ! 1505: for(i=0; names[i].name!=NULL; i++) ! 1506: if(stricmp(names[i].name,value)==0) ! 1507: return(names[i].value); ! 1508: ! 1509: /* Look for partial matches second */ ! 1510: for(i=0; names[i].name!=NULL; i++) ! 1511: if(strnicmp(names[i].name,value,strlen(value))==0) ! 1512: return(names[i].value); ! 1513: ! 1514: return(parseInteger(value)); ! 1515: } ! 1516: ! 1517: long iniReadNamedInt(FILE* fp, const char* section, const char* key ! 1518: ,named_long_t* names, long deflt) ! 1519: { ! 1520: char buf[INI_MAX_VALUE_LEN]; ! 1521: char* value; ! 1522: ! 1523: if((value=read_value(fp,section,key,buf))==NULL) ! 1524: return(deflt); ! 1525: ! 1526: if(*value==0) /* blank value */ ! 1527: return(deflt); ! 1528: ! 1529: return(parseNamedInt(value,names)); ! 1530: } ! 1531: ! 1532: long iniGetNamedInt(str_list_t list, const char* section, const char* key ! 1533: ,named_long_t* names, long deflt) ! 1534: { ! 1535: char value[INI_MAX_VALUE_LEN]; ! 1536: ! 1537: get_value(list, section, key, value); ! 1538: ! 1539: if(*value==0) /* blank value or missing key */ ! 1540: return(deflt); ! 1541: ! 1542: return(parseNamedInt(value,names)); ! 1543: } ! 1544: ! 1545: static double parseNamedFloat(const char* value, named_double_t* names) ! 1546: { ! 1547: unsigned i; ! 1548: ! 1549: /* Look for exact matches first */ ! 1550: for(i=0; names[i].name!=NULL; i++) ! 1551: if(stricmp(names[i].name,value)==0) ! 1552: return(names[i].value); ! 1553: ! 1554: /* Look for partial matches second */ ! 1555: for(i=0; names[i].name!=NULL; i++) ! 1556: if(strnicmp(names[i].name,value,strlen(value))==0) ! 1557: return(names[i].value); ! 1558: ! 1559: return(atof(value)); ! 1560: } ! 1561: ! 1562: double iniReadNamedFloat(FILE* fp, const char* section, const char* key ! 1563: ,named_double_t* names, double deflt) ! 1564: { ! 1565: char buf[INI_MAX_VALUE_LEN]; ! 1566: char* value; ! 1567: ! 1568: if((value=read_value(fp,section,key,buf))==NULL) ! 1569: return(deflt); ! 1570: ! 1571: if(*value==0) /* blank value */ ! 1572: return(deflt); ! 1573: ! 1574: return(parseNamedFloat(value,names)); ! 1575: } ! 1576: ! 1577: double iniGetNamedFloat(str_list_t list, const char* section, const char* key ! 1578: ,named_double_t* names, double deflt) ! 1579: { ! 1580: char value[INI_MAX_VALUE_LEN]; ! 1581: ! 1582: get_value(list, section, key, value); ! 1583: ! 1584: if(*value==0) /* blank value or missing key */ ! 1585: return(deflt); ! 1586: ! 1587: return(parseNamedFloat(value,names)); ! 1588: } ! 1589: ! 1590: static ulong parseBitField(char* value, ini_bitdesc_t* bitdesc) ! 1591: { ! 1592: int i; ! 1593: char* p; ! 1594: char* tp; ! 1595: ulong v=0; ! 1596: ! 1597: for(p=value;*p;) { ! 1598: tp=strchr(p,INI_BIT_SEP); ! 1599: if(tp!=NULL) ! 1600: *tp=0; ! 1601: truncsp(p); ! 1602: ! 1603: for(i=0;bitdesc[i].name;i++) ! 1604: if(!stricmp(bitdesc[i].name,p)) ! 1605: break; ! 1606: ! 1607: if(bitdesc[i].name) ! 1608: v|=bitdesc[i].bit; ! 1609: else ! 1610: v|=strtoul(p,NULL,0); ! 1611: ! 1612: if(tp==NULL) ! 1613: break; ! 1614: ! 1615: p=tp+1; ! 1616: SKIP_WHITESPACE(p); ! 1617: } ! 1618: ! 1619: return(v); ! 1620: } ! 1621: ! 1622: ulong iniReadBitField(FILE* fp, const char* section, const char* key, ! 1623: ini_bitdesc_t* bitdesc, ulong deflt) ! 1624: { ! 1625: char* value; ! 1626: char buf[INI_MAX_VALUE_LEN]; ! 1627: ! 1628: if((value=read_value(fp,section,key,buf))==NULL) ! 1629: return(deflt); ! 1630: ! 1631: return(parseBitField(value,bitdesc)); ! 1632: } ! 1633: ! 1634: ulong iniGetBitField(str_list_t list, const char* section, const char* key, ! 1635: ini_bitdesc_t* bitdesc, ulong deflt) ! 1636: { ! 1637: char value[INI_MAX_VALUE_LEN]; ! 1638: ! 1639: get_value(list, section, key, value); ! 1640: ! 1641: if(*value==0) /* blank value or missing key */ ! 1642: return(deflt); ! 1643: ! 1644: return(parseBitField(value,bitdesc)); ! 1645: } ! 1646: ! 1647: FILE* iniOpenFile(const char* fname, BOOL create) ! 1648: { ! 1649: char* mode="r+"; ! 1650: ! 1651: if(create && !fexist(fname)) ! 1652: mode="w+"; ! 1653: ! 1654: return(fopen(fname,mode)); ! 1655: } ! 1656: ! 1657: BOOL iniCloseFile(FILE* fp) ! 1658: { ! 1659: return(fclose(fp)==0); ! 1660: } ! 1661: ! 1662: str_list_t iniReadFile(FILE* fp) ! 1663: { ! 1664: char str[INI_MAX_LINE_LEN]; ! 1665: char* p; ! 1666: size_t i; ! 1667: size_t inc_len; ! 1668: size_t inc_counter=0; ! 1669: str_list_t list; ! 1670: FILE* insert_fp=NULL; ! 1671: ! 1672: if(fp!=NULL) ! 1673: rewind(fp); ! 1674: ! 1675: list = strListReadFile(fp, NULL, INI_MAX_LINE_LEN); ! 1676: if(list==NULL) ! 1677: return(NULL); ! 1678: ! 1679: /* Look for !include directives */ ! 1680: inc_len=strlen(INI_INCLUDE_DIRECTIVE); ! 1681: for(i=0; list[i]!=NULL; i++) { ! 1682: if(strnicmp(list[i],INI_INCLUDE_DIRECTIVE,inc_len)==0) { ! 1683: p=list[i]+inc_len; ! 1684: FIND_WHITESPACE(p); ! 1685: SKIP_WHITESPACE(p); ! 1686: truncsp(p); ! 1687: if(inc_counter >= INI_INCLUDE_MAX) ! 1688: SAFEPRINTF2(str, ";%s - MAXIMUM INCLUDES REACHED: %u", list[i], INI_INCLUDE_MAX); ! 1689: else if((insert_fp=fopen(p,"r"))==NULL) ! 1690: SAFEPRINTF2(str, ";%s - FAILURE: %s", list[i], STRERROR(errno)); ! 1691: else ! 1692: SAFEPRINTF(str, ";%s", list[i]); ! 1693: strListReplace(list, i, str); ! 1694: if(insert_fp!=NULL) { ! 1695: strListInsertFile(insert_fp, &list, i+1, INI_MAX_LINE_LEN); ! 1696: fclose(insert_fp); ! 1697: insert_fp=NULL; ! 1698: inc_counter++; ! 1699: } ! 1700: } ! 1701: } ! 1702: ! 1703: /* truncate new-line chars off end of strings */ ! 1704: for(i=0; list[i]!=NULL; i++) ! 1705: truncnl(list[i]); ! 1706: ! 1707: return(list); ! 1708: } ! 1709: ! 1710: BOOL iniWriteFile(FILE* fp, const str_list_t list) ! 1711: { ! 1712: size_t count; ! 1713: ! 1714: rewind(fp); ! 1715: ! 1716: if(chsize(fileno(fp),0)!=0) /* truncate */ ! 1717: return(FALSE); ! 1718: ! 1719: count = strListWriteFile(fp,list,"\n"); ! 1720: ! 1721: return(count == strListCount(list)); ! 1722: } ! 1723: ! 1724: #ifdef INI_FILE_TEST ! 1725: void main(int argc, char** argv) ! 1726: { ! 1727: int i; ! 1728: size_t l; ! 1729: char str[128]; ! 1730: FILE* fp; ! 1731: str_list_t list; ! 1732: ! 1733: for(i=1;i<argc;i++) { ! 1734: if((fp=iniOpenFile(argv[i],FALSE)) == NULL) ! 1735: continue; ! 1736: if((list=iniReadFile(fp)) != NULL) { ! 1737: printf("%s\n",iniGetString(list," test | bogus ","key","default",str)); ! 1738: strListFree(&list); ! 1739: } ! 1740: fclose(fp); ! 1741: } ! 1742: } ! 1743: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.