|
|
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.