|
|
1.1.1.2 root 1: /*
2: * Hatari - cfgopts.c
3: *
4: * The functions in this file are used to load and save the ASCII
5: * configuration file.
6: * Original information text follows:
7: */
8: /*<<---------------[ cfgopts.c ]------------------------/
9: / /
10: / Functional /
11: / Description: Configuration file I/O /
12: / /
13: / Input : Configuration file name /
14: / Configuration parameters in a structure /
15: / /
16: / Process : Interpret information by parameter and read or /
17: / write back to the configuration file. /
18: / /
19: / Ouput : updated configuration file or updated structure. /
20: / /
21: / Programmer : Jeffry J. Brickley /
22: / /
23: / /
24: /---------------------------------------------------------------------*/
25:
26: /*---------------------------------------------------------------------/
27: /
28: / Description: CfgOpts is based on GETOPTS by Bob Stout. It will
29: / process a configuration file based one words and
30: / store it in a structure pointing to physical data
31: / area for each storage item.
32: / i.e. ???.CFG:
33: / Port=1
34: / work_space=C:\temp
35: / menus=TRUE
36: / user=Jeffry Brickley
37: / will write to the following structure:
38: / struct Config_Tag configs[] = {
39: / {"port", Int_Tag, &port_number},
40: / {"work_space", String_Tag, &work_space},
41: / {"menus", Bool_Tag, &menu_flag},
42: / {"user", String_Tag, &User_name},
43: / {NULL, Error_Tag, NULL}
44: / };
45: / Note that the structure must always be terminated by a NULL row as
46: / was the same with GETOPTS. This however is slightly more
47: / complicated than scaning the command line (but not by much) for
48: / data as there can be more variety in words than letters and an
49: / number of data items limited only by memory.
50: /
51: / Like the original code from which this was taken, this is released
52: / to the Public Domain. I cannot make any guarentees other than these
53: / work for me and I find them usefull. Feel free to pass these on to
54: / a friend, but please do not charge him....
55: /
56: /---------------------------------------------------------------------*/
1.1.1.5 ! root 57: const char CfgOpts_rcsid[] = "Hatari $Id: cfgopts.c,v 1.15 2007/12/18 20:35:04 thothy Exp $";
! 58:
! 59: #include <config.h>
1.1.1.2 root 60:
61: #include <stdio.h>
62: #include <stdlib.h>
63: #include <string.h>
1.1.1.5 ! root 64: #include <unistd.h>
! 65:
! 66: #if HAVE_STRINGS_H
1.1.1.4 root 67: #include <strings.h>
1.1.1.5 ! root 68: #endif
1.1.1.2 root 69:
70: #include "main.h"
71: #include "cfgopts.h"
72:
73:
74:
1.1.1.5 ! root 75: /**
! 76: * --- Remove spaces from beginning and end of a string ---
! 77: */
1.1.1.2 root 78: static char *trim(char *buffer)
79: {
1.1.1.3 root 80: const char SPACE = ' ';
81: const char TABULA = '\t';
1.1.1.2 root 82:
1.1.1.3 root 83: if (buffer != NULL)
84: {
85: int i, linelen;
86:
87: linelen = strlen(buffer);
88:
89: for (i = 0; i < linelen; i++)
90: {
91: if (buffer[i] != SPACE && buffer[i] != TABULA)
92: break;
93: }
94:
95: if (i > 0 && i < linelen)
96: {
97: linelen -= i;
98: memmove(buffer, buffer + i, linelen);
99: }
100:
101: for (i = linelen; i > 0; i--)
102: {
103: int j = i - 1;
104: if (buffer[j] != SPACE && buffer[j] != TABULA)
105: break;
106: }
1.1.1.2 root 107:
1.1.1.3 root 108: buffer[i] = '\0';
109: }
1.1.1.2 root 110:
1.1.1.3 root 111: return buffer;
1.1.1.2 root 112: }
113:
114:
1.1.1.5 ! root 115: /**
! 116: * ---------------------------------------------------------------------/
! 117: * / reads from an input configuration (INI) file.
! 118: * /---------------------------------------------------------------------
! 119: * >>------[ input_config() ]-------------[ 08-02-95 14:02PM ]------/
! 120: * / return value:
! 121: * / int ; number of records read or -1 on error
! 122: * / parameters:
! 123: * / char *filename ; filename of INI style file
! 124: * / struct Config_Tag configs[]; Configuration structure
! 125: * / char *header ; INI header name (i.e. "[TEST]")
! 126: * /-------------------------------------------------------------------<<
! 127: */
1.1.1.3 root 128: int input_config(const char *filename, const struct Config_Tag configs[], const char *header)
1.1.1.2 root 129: {
1.1.1.3 root 130: const struct Config_Tag *ptr;
131: int count=0, lineno=0;
132: FILE *file;
133: char *fptr,*tok,*next;
134: char line[1024];
135:
136: file = fopen(filename,"r");
137: if (file == NULL)
138: return -1; /* return error designation. */
139:
140: if (header != NULL)
141: {
142: do
143: {
144: fptr = trim(fgets(line, sizeof(line), file)); /* get input line */
145: }
146: while ( memcmp(line,header,strlen(header)) && !feof(file));
147: }
148:
149: if ( !feof(file) )
150: do
151: {
152: fptr = trim(fgets(line, sizeof(line), file)); /* get input line */
153: if (fptr == NULL)
154: continue;
155: lineno++;
156: if (line[0] == '#')
157: continue; /* skip comments */
158: if (line[0] == '[')
159: continue; /* skip next header */
160: tok = trim(strtok(line, "=\n\r")); /* get first token */
161: if (tok != NULL)
162: {
163: next = trim(strtok(NULL, "=\n\r")); /* get actual config information */
164: for (ptr = configs; ptr->buf; ++ptr) /* scan for token */
165: {
166: if (!strcmp(tok, ptr->code)) /* got a match? */
167: {
168: switch (ptr->type) /* check type */
169: {
170: case Bool_Tag:
171: if (!strcasecmp(next,"FALSE"))
172: *((BOOL *)(ptr->buf)) = FALSE;
173: else if (!strcasecmp(next,"TRUE"))
174: *((BOOL *)(ptr->buf)) = TRUE;
175: ++count;
176: break;
177:
178: case Char_Tag:
179: sscanf(next, "%c", (char *)(ptr->buf));
180: ++count;
181: break;
182:
183: case Short_Tag:
184: sscanf(next, "%hd", (short *)(ptr->buf));
185: ++count;
186: break;
187:
188: case Int_Tag:
189: sscanf(next, "%d", (int *)(ptr->buf));
190: ++count;
191: break;
192:
193: case Long_Tag:
194: sscanf(next, "%ld", (long *)(ptr->buf));
195: ++count;
196: break;
197:
198: case Float_Tag:
199: sscanf(next, "%g", (float *)ptr->buf);
200: ++count;
201: break;
202:
203: case Double_Tag:
204: sscanf(next, "%lg", (double *)ptr->buf);
205: ++count;
206: break;
207:
208: case String_Tag:
209: if(next)
210: strcpy((char *)ptr->buf, next);
211: else
212: *(char *)ptr->buf = 0;
213: ++count;
214: break;
215:
216: case Error_Tag:
217: default:
218: printf("Error in Config file %s on line %d\n", filename, lineno);
219: break;
220: }
221: }
222:
223: }
224: }
225: }
226: while ( fptr!=NULL && line[0]!='[');
1.1.1.2 root 227:
1.1.1.3 root 228: fclose(file);
229: return count;
1.1.1.2 root 230: }
231:
232:
1.1.1.5 ! root 233: /**
! 234: * Write out an settings line
! 235: */
1.1.1.3 root 236: static int write_token(FILE *outfile, const struct Config_Tag *ptr)
1.1.1.2 root 237: {
1.1.1.3 root 238: fprintf(outfile,"%s = ",ptr->code);
1.1.1.2 root 239:
1.1.1.3 root 240: switch (ptr->type) /* check type */
241: {
242: case Bool_Tag:
243: fprintf(outfile,"%s\n", *((BOOL *)(ptr->buf)) ? "TRUE" : "FALSE");
244: break;
245:
246: case Char_Tag:
247: fprintf(outfile, "%c\n", *((char *)(ptr->buf)));
248: break;
249:
250: case Short_Tag:
251: fprintf(outfile, "%hd\n", *((short *)(ptr->buf)));
252: break;
253:
254: case Int_Tag:
255: fprintf(outfile, "%d\n", *((int *)(ptr->buf)));
256: break;
257:
258: case Long_Tag:
259: fprintf(outfile, "%ld\n", *((long *)(ptr->buf)));
260: break;
261:
262: case Float_Tag:
263: fprintf(outfile, "%g\n", *((float *)ptr->buf));
264: break;
265:
266: case Double_Tag:
267: fprintf(outfile, "%g\n", *((double *)ptr->buf));
268: break;
269:
270: case String_Tag:
271: fprintf(outfile, "%s\n",(char *)ptr->buf);
272: break;
273:
274: case Error_Tag:
275: default:
276: fprintf(stderr, "Error in Config structure (Contact author).\n");
277: return -1;
278: }
1.1.1.2 root 279:
1.1.1.3 root 280: return 0;
1.1.1.2 root 281: }
282:
283:
1.1.1.5 ! root 284: /**
! 285: * Write given section header and tokens for that
! 286: * Return number of written tokens
! 287: */
! 288: static int write_header_tokens(FILE *fp, const struct Config_Tag *ptr, const char *header)
! 289: {
! 290: int count = 0;
! 291:
! 292: if (header != NULL)
! 293: {
! 294: fprintf(fp, "%s\n", header);
! 295: }
! 296:
! 297: for (; ptr->buf; ++ptr) /* scan for token */
! 298: {
! 299: if (write_token(fp, ptr) == 0)
! 300: ++count;
! 301: }
! 302:
! 303: fprintf(fp, "\n");
! 304:
! 305: return count;
! 306: }
! 307:
! 308:
! 309: /**
! 310: * ---------------------------------------------------------------------/
! 311: * / updates an input configuration (INI) file from a structure.
! 312: * /---------------------------------------------------------------------
! 313: * >>------[ update_config() ]-------------[ 08-02-95 14:02PM ]------/
! 314: * / return value:
! 315: * / int ; Number of records read & updated
! 316: * / parameters:
! 317: * / char *filename ; filename of INI file
! 318: * / struct Config_Tag configs[]; Configuration structure
! 319: * / char *header ; INI header name (i.e. "[TEST]")
! 320: * /-------------------------------------------------------------------<<
! 321: */
1.1.1.3 root 322: int update_config(const char *filename, const struct Config_Tag configs[], const char *header)
1.1.1.2 root 323: {
1.1.1.3 root 324: const struct Config_Tag *ptr;
1.1.1.5 ! root 325: int count=0, lineno=0, retval;
1.1.1.3 root 326: FILE *cfgfile, *tempfile;
327: char *fptr, *tok, *next;
328: char line[1024];
1.1.1.5 ! root 329: BOOL bUseTempCfg = FALSE;
! 330: const char *sTempCfgName = "_temp_.cfg";
1.1.1.3 root 331:
332: cfgfile = fopen(filename, "r");
333:
334: /* If the cfg file does not yet exists, we can create it directly: */
335: if (cfgfile == NULL)
336: {
337: cfgfile = fopen(filename, "w");
338: if (cfgfile == NULL)
339: return -1; /* return error designation. */
1.1.1.5 ! root 340: count = write_header_tokens(cfgfile, configs, header);
1.1.1.3 root 341: fclose(cfgfile);
342: return count;
343: }
344:
345: tempfile = tmpfile(); /* Open a temporary file for output */
1.1.1.5 ! root 346: if (tempfile == NULL)
! 347: {
! 348: /* tmpfile() failed, let's try a normal open */
! 349: tempfile = fopen(sTempCfgName, "w+");
! 350: bUseTempCfg = TRUE;
! 351: }
1.1.1.3 root 352: if (tempfile == NULL)
353: {
1.1.1.5 ! root 354: perror("update_config");
1.1.1.3 root 355: fclose(cfgfile);
1.1.1.5 ! root 356: return -1; /* return error designation. */
1.1.1.3 root 357: }
358:
359: if (header != NULL)
360: {
1.1.1.5 ! root 361: int headerlen = strlen(header);
1.1.1.3 root 362: do
363: {
364: fptr = trim(fgets(line, sizeof(line), cfgfile)); /* get input line */
365: if (feof(cfgfile))
366: break;
367: fprintf(tempfile, "%s", line);
368: }
1.1.1.5 ! root 369: while(memcmp(line, header, headerlen));
1.1.1.3 root 370: }
371:
372: if (feof(cfgfile))
373: {
1.1.1.5 ! root 374: count += write_header_tokens(tempfile, configs, header);
1.1.1.3 root 375: }
376: else
377: {
1.1.1.5 ! root 378: char *savedtokenflags = NULL; /* Array to log the saved tokens */
! 379: int numtokens = 0; /* Total number of tokens to save */
1.1.1.3 root 380:
381: /* Find total number of tokens: */
382: for (ptr=configs; ptr->buf; ++ptr)
383: {
384: numtokens += 1;
385: }
1.1.1.5 ! root 386: if (numtokens)
! 387: {
! 388: savedtokenflags = malloc(numtokens * sizeof(char));
! 389: if (savedtokenflags)
! 390: memset(savedtokenflags, 0, numtokens * sizeof(char));
! 391: }
1.1.1.3 root 392:
393: for(;;)
394: {
395: fptr = trim(fgets(line, sizeof(line), cfgfile)); /* get input line */
396: if (fptr == NULL)
397: break;
398: lineno++;
399: if (line[0] == '#')
400: {
401: fprintf(tempfile, "%s", line);
402: continue; /* skip comments */
403: }
404: if (line[0] == '[' || feof(cfgfile))
405: {
406: break;
407: }
408:
409: tok = trim(strtok(line, "=\n\r")); /* get first token */
410: if (tok != NULL)
411: {
412: int i = 0;
413: next = strtok(line, "=\n\r"); /* get actual config information */
414: for (ptr = configs; ptr->buf; ++ptr, i++) /* scan for token */
415: {
416: if (!strcmp(tok, ptr->code)) /* got a match? */
417: {
418: if (write_token(tempfile, ptr) == 0)
419: {
420: if (savedtokenflags)
421: savedtokenflags[i] = TRUE;
422: count += 1;
423: }
424: }
425: }
426: }
427: }
428:
429: /* Write remaining (new?) tokens that were not in the configuration file, yet */
430: if (count != numtokens && savedtokenflags != NULL)
431: {
432: int i;
433: for (ptr = configs, i = 0; ptr->buf; ++ptr, i++)
434: {
435: if (!savedtokenflags[i])
436: {
437: if (write_token(tempfile, ptr) == 0)
438: {
439: count += 1;
440: fprintf(stderr, "Wrote new token %s -> %s \n", header, ptr->code);
441: }
442: }
443: }
444: }
445:
446: if (savedtokenflags)
1.1.1.5 ! root 447: {
1.1.1.3 root 448: free(savedtokenflags);
1.1.1.5 ! root 449: savedtokenflags = NULL;
! 450: }
1.1.1.3 root 451:
452: if (!feof(cfgfile) && fptr != NULL)
453: fprintf(tempfile, "\n%s", line);
454:
455: for(;;)
456: {
457: fptr = trim(fgets(line, sizeof(line), cfgfile)); /* get input line */
458: if (feof(cfgfile))
459: break;
460: fprintf(tempfile, "%s", line);
461: }
462: }
463:
464:
465: /* Re-open the config file for writing: */
466: fclose(cfgfile);
467: cfgfile = fopen(filename, "wb");
1.1.1.5 ! root 468: if (cfgfile == NULL || fseek(tempfile, 0, SEEK_SET) != 0)
1.1.1.3 root 469: {
1.1.1.5 ! root 470: retval = -1;
! 471: goto cleanup;
1.1.1.3 root 472: }
473:
474: /* Now copy the temporary file to the configuration file: */
1.1.1.5 ! root 475: retval = count;
! 476: while(!(feof(tempfile) || ferror(cfgfile)))
1.1.1.3 root 477: {
478: size_t copycount;
479: copycount = fread(line, sizeof(char), sizeof(line), tempfile);
1.1.1.5 ! root 480: if (copycount == 0)
! 481: break;
1.1.1.3 root 482: if (fwrite(line, sizeof(char), copycount, cfgfile) != copycount)
483: {
1.1.1.5 ! root 484: retval = -1;
! 485: break;
1.1.1.3 root 486: }
487: }
1.1.1.5 ! root 488: cleanup:
! 489: if (cfgfile)
! 490: {
! 491: if (ferror(cfgfile))
! 492: perror("update_config");
! 493: fclose(cfgfile);
! 494: }
! 495: if (tempfile)
! 496: {
! 497: /* tmpfile() is removed automatically on close */
! 498: fclose(tempfile);
! 499: if (bUseTempCfg)
! 500: unlink(sTempCfgName);
! 501: }
! 502: return retval;
1.1.1.2 root 503: }
504:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.