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