|
|
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.6 ! root 57: const char CfgOpts_rcsid[] = "Hatari $Id: cfgopts.c,v 1.18 2008/05/19 20:34:07 thothy Exp $";
1.1.1.2 root 58:
59: #include <stdio.h>
60: #include <stdlib.h>
1.1.1.5 root 61: #include <unistd.h>
62:
1.1.1.2 root 63: #include "main.h"
64: #include "cfgopts.h"
1.1.1.6 ! root 65: #include "str.h"
1.1.1.2 root 66:
67:
1.1.1.5 root 68: /**
69: * ---------------------------------------------------------------------/
70: * / reads from an input configuration (INI) file.
71: * /---------------------------------------------------------------------
72: * >>------[ input_config() ]-------------[ 08-02-95 14:02PM ]------/
73: * / return value:
74: * / int ; number of records read or -1 on error
75: * / parameters:
76: * / char *filename ; filename of INI style file
77: * / struct Config_Tag configs[]; Configuration structure
78: * / char *header ; INI header name (i.e. "[TEST]")
79: * /-------------------------------------------------------------------<<
80: */
1.1.1.3 root 81: int input_config(const char *filename, const struct Config_Tag configs[], const char *header)
1.1.1.2 root 82: {
1.1.1.3 root 83: const struct Config_Tag *ptr;
84: int count=0, lineno=0;
85: FILE *file;
86: char *fptr,*tok,*next;
87: char line[1024];
88:
89: file = fopen(filename,"r");
90: if (file == NULL)
91: return -1; /* return error designation. */
92:
93: if (header != NULL)
94: {
95: do
96: {
1.1.1.6 ! root 97: fptr = Str_Trim(fgets(line, sizeof(line), file)); /* get input line */
1.1.1.3 root 98: }
99: while ( memcmp(line,header,strlen(header)) && !feof(file));
100: }
101:
102: if ( !feof(file) )
103: do
104: {
1.1.1.6 ! root 105: fptr = Str_Trim(fgets(line, sizeof(line), file)); /* get input line */
1.1.1.3 root 106: if (fptr == NULL)
107: continue;
108: lineno++;
109: if (line[0] == '#')
110: continue; /* skip comments */
111: if (line[0] == '[')
112: continue; /* skip next header */
1.1.1.6 ! root 113: tok = Str_Trim(strtok(line, "=\n\r")); /* get first token */
1.1.1.3 root 114: if (tok != NULL)
115: {
1.1.1.6 ! root 116: next = Str_Trim(strtok(NULL, "=\n\r")); /* get actual config information */
1.1.1.3 root 117: for (ptr = configs; ptr->buf; ++ptr) /* scan for token */
118: {
119: if (!strcmp(tok, ptr->code)) /* got a match? */
120: {
121: switch (ptr->type) /* check type */
122: {
123: case Bool_Tag:
124: if (!strcasecmp(next,"FALSE"))
1.1.1.6 ! root 125: *((bool *)(ptr->buf)) = false;
1.1.1.3 root 126: else if (!strcasecmp(next,"TRUE"))
1.1.1.6 ! root 127: *((bool *)(ptr->buf)) = true;
1.1.1.3 root 128: ++count;
129: break;
130:
131: case Char_Tag:
132: sscanf(next, "%c", (char *)(ptr->buf));
133: ++count;
134: break;
135:
136: case Short_Tag:
137: sscanf(next, "%hd", (short *)(ptr->buf));
138: ++count;
139: break;
140:
141: case Int_Tag:
142: sscanf(next, "%d", (int *)(ptr->buf));
143: ++count;
144: break;
145:
146: case Long_Tag:
147: sscanf(next, "%ld", (long *)(ptr->buf));
148: ++count;
149: break;
150:
151: case Float_Tag:
152: sscanf(next, "%g", (float *)ptr->buf);
153: ++count;
154: break;
155:
156: case Double_Tag:
157: sscanf(next, "%lg", (double *)ptr->buf);
158: ++count;
159: break;
160:
161: case String_Tag:
162: if(next)
163: strcpy((char *)ptr->buf, next);
164: else
165: *(char *)ptr->buf = 0;
166: ++count;
167: break;
168:
169: case Error_Tag:
170: default:
171: printf("Error in Config file %s on line %d\n", filename, lineno);
172: break;
173: }
174: }
175:
176: }
177: }
178: }
179: while ( fptr!=NULL && line[0]!='[');
1.1.1.2 root 180:
1.1.1.3 root 181: fclose(file);
182: return count;
1.1.1.2 root 183: }
184:
185:
1.1.1.5 root 186: /**
187: * Write out an settings line
188: */
1.1.1.3 root 189: static int write_token(FILE *outfile, const struct Config_Tag *ptr)
1.1.1.2 root 190: {
1.1.1.3 root 191: fprintf(outfile,"%s = ",ptr->code);
1.1.1.2 root 192:
1.1.1.3 root 193: switch (ptr->type) /* check type */
194: {
195: case Bool_Tag:
1.1.1.6 ! root 196: fprintf(outfile,"%s\n", *((bool *)(ptr->buf)) ? "TRUE" : "FALSE");
1.1.1.3 root 197: break;
198:
199: case Char_Tag:
200: fprintf(outfile, "%c\n", *((char *)(ptr->buf)));
201: break;
202:
203: case Short_Tag:
204: fprintf(outfile, "%hd\n", *((short *)(ptr->buf)));
205: break;
206:
207: case Int_Tag:
208: fprintf(outfile, "%d\n", *((int *)(ptr->buf)));
209: break;
210:
211: case Long_Tag:
212: fprintf(outfile, "%ld\n", *((long *)(ptr->buf)));
213: break;
214:
215: case Float_Tag:
216: fprintf(outfile, "%g\n", *((float *)ptr->buf));
217: break;
218:
219: case Double_Tag:
220: fprintf(outfile, "%g\n", *((double *)ptr->buf));
221: break;
222:
223: case String_Tag:
224: fprintf(outfile, "%s\n",(char *)ptr->buf);
225: break;
226:
227: case Error_Tag:
228: default:
229: fprintf(stderr, "Error in Config structure (Contact author).\n");
230: return -1;
231: }
1.1.1.2 root 232:
1.1.1.3 root 233: return 0;
1.1.1.2 root 234: }
235:
236:
1.1.1.5 root 237: /**
238: * Write given section header and tokens for that
239: * Return number of written tokens
240: */
241: static int write_header_tokens(FILE *fp, const struct Config_Tag *ptr, const char *header)
242: {
243: int count = 0;
244:
245: if (header != NULL)
246: {
247: fprintf(fp, "%s\n", header);
248: }
249:
250: for (; ptr->buf; ++ptr) /* scan for token */
251: {
252: if (write_token(fp, ptr) == 0)
253: ++count;
254: }
255:
256: fprintf(fp, "\n");
257:
258: return count;
259: }
260:
261:
262: /**
263: * ---------------------------------------------------------------------/
264: * / updates an input configuration (INI) file from a structure.
265: * /---------------------------------------------------------------------
266: * >>------[ update_config() ]-------------[ 08-02-95 14:02PM ]------/
267: * / return value:
268: * / int ; Number of records read & updated
269: * / parameters:
270: * / char *filename ; filename of INI file
271: * / struct Config_Tag configs[]; Configuration structure
272: * / char *header ; INI header name (i.e. "[TEST]")
273: * /-------------------------------------------------------------------<<
274: */
1.1.1.3 root 275: int update_config(const char *filename, const struct Config_Tag configs[], const char *header)
1.1.1.2 root 276: {
1.1.1.3 root 277: const struct Config_Tag *ptr;
1.1.1.5 root 278: int count=0, lineno=0, retval;
1.1.1.3 root 279: FILE *cfgfile, *tempfile;
280: char *fptr, *tok, *next;
281: char line[1024];
1.1.1.6 ! root 282: bool bUseTempCfg = false;
1.1.1.5 root 283: const char *sTempCfgName = "_temp_.cfg";
1.1.1.3 root 284:
285: cfgfile = fopen(filename, "r");
286:
287: /* If the cfg file does not yet exists, we can create it directly: */
288: if (cfgfile == NULL)
289: {
290: cfgfile = fopen(filename, "w");
291: if (cfgfile == NULL)
292: return -1; /* return error designation. */
1.1.1.5 root 293: count = write_header_tokens(cfgfile, configs, header);
1.1.1.3 root 294: fclose(cfgfile);
295: return count;
296: }
297:
298: tempfile = tmpfile(); /* Open a temporary file for output */
1.1.1.5 root 299: if (tempfile == NULL)
300: {
301: /* tmpfile() failed, let's try a normal open */
302: tempfile = fopen(sTempCfgName, "w+");
303: bUseTempCfg = TRUE;
304: }
1.1.1.3 root 305: if (tempfile == NULL)
306: {
1.1.1.5 root 307: perror("update_config");
1.1.1.3 root 308: fclose(cfgfile);
1.1.1.5 root 309: return -1; /* return error designation. */
1.1.1.3 root 310: }
311:
312: if (header != NULL)
313: {
1.1.1.5 root 314: int headerlen = strlen(header);
1.1.1.3 root 315: do
316: {
1.1.1.6 ! root 317: fptr = Str_Trim(fgets(line, sizeof(line), cfgfile)); /* get input line */
1.1.1.3 root 318: if (feof(cfgfile))
319: break;
1.1.1.6 ! root 320: fprintf(tempfile, "%s\n", line);
1.1.1.3 root 321: }
1.1.1.5 root 322: while(memcmp(line, header, headerlen));
1.1.1.3 root 323: }
324:
325: if (feof(cfgfile))
326: {
1.1.1.5 root 327: count += write_header_tokens(tempfile, configs, header);
1.1.1.3 root 328: }
329: else
330: {
1.1.1.5 root 331: char *savedtokenflags = NULL; /* Array to log the saved tokens */
332: int numtokens = 0; /* Total number of tokens to save */
1.1.1.3 root 333:
334: /* Find total number of tokens: */
335: for (ptr=configs; ptr->buf; ++ptr)
336: {
337: numtokens += 1;
338: }
1.1.1.5 root 339: if (numtokens)
340: {
341: savedtokenflags = malloc(numtokens * sizeof(char));
342: if (savedtokenflags)
343: memset(savedtokenflags, 0, numtokens * sizeof(char));
344: }
1.1.1.3 root 345:
346: for(;;)
347: {
1.1.1.6 ! root 348: fptr = Str_Trim(fgets(line, sizeof(line), cfgfile)); /* get input line */
1.1.1.3 root 349: if (fptr == NULL)
350: break;
351: lineno++;
352: if (line[0] == '#')
353: {
1.1.1.6 ! root 354: fprintf(tempfile, "%s\n", line);
1.1.1.3 root 355: continue; /* skip comments */
356: }
357: if (line[0] == '[' || feof(cfgfile))
358: {
359: break;
360: }
361:
1.1.1.6 ! root 362: tok = Str_Trim(strtok(line, "=\n\r")); /* get first token */
1.1.1.3 root 363: if (tok != NULL)
364: {
365: int i = 0;
366: next = strtok(line, "=\n\r"); /* get actual config information */
367: for (ptr = configs; ptr->buf; ++ptr, i++) /* scan for token */
368: {
369: if (!strcmp(tok, ptr->code)) /* got a match? */
370: {
371: if (write_token(tempfile, ptr) == 0)
372: {
373: if (savedtokenflags)
374: savedtokenflags[i] = TRUE;
375: count += 1;
376: }
377: }
378: }
379: }
380: }
381:
382: /* Write remaining (new?) tokens that were not in the configuration file, yet */
383: if (count != numtokens && savedtokenflags != NULL)
384: {
385: int i;
386: for (ptr = configs, i = 0; ptr->buf; ++ptr, i++)
387: {
388: if (!savedtokenflags[i])
389: {
390: if (write_token(tempfile, ptr) == 0)
391: {
392: count += 1;
393: fprintf(stderr, "Wrote new token %s -> %s \n", header, ptr->code);
394: }
395: }
396: }
397: }
398:
399: if (savedtokenflags)
1.1.1.5 root 400: {
1.1.1.3 root 401: free(savedtokenflags);
1.1.1.5 root 402: savedtokenflags = NULL;
403: }
1.1.1.3 root 404:
405: if (!feof(cfgfile) && fptr != NULL)
1.1.1.6 ! root 406: fprintf(tempfile, "\n%s\n", line);
1.1.1.3 root 407:
408: for(;;)
409: {
1.1.1.6 ! root 410: fptr = Str_Trim(fgets(line, sizeof(line), cfgfile)); /* get input line */
1.1.1.3 root 411: if (feof(cfgfile))
412: break;
1.1.1.6 ! root 413: fprintf(tempfile, "%s\n", line);
1.1.1.3 root 414: }
415: }
416:
417:
418: /* Re-open the config file for writing: */
419: fclose(cfgfile);
420: cfgfile = fopen(filename, "wb");
1.1.1.5 root 421: if (cfgfile == NULL || fseek(tempfile, 0, SEEK_SET) != 0)
1.1.1.3 root 422: {
1.1.1.5 root 423: retval = -1;
424: goto cleanup;
1.1.1.3 root 425: }
426:
427: /* Now copy the temporary file to the configuration file: */
1.1.1.5 root 428: retval = count;
429: while(!(feof(tempfile) || ferror(cfgfile)))
1.1.1.3 root 430: {
431: size_t copycount;
432: copycount = fread(line, sizeof(char), sizeof(line), tempfile);
1.1.1.5 root 433: if (copycount == 0)
434: break;
1.1.1.3 root 435: if (fwrite(line, sizeof(char), copycount, cfgfile) != copycount)
436: {
1.1.1.5 root 437: retval = -1;
438: break;
1.1.1.3 root 439: }
440: }
1.1.1.5 root 441: cleanup:
442: if (cfgfile)
443: {
444: if (ferror(cfgfile))
445: perror("update_config");
446: fclose(cfgfile);
447: }
448: if (tempfile)
449: {
450: /* tmpfile() is removed automatically on close */
451: fclose(tempfile);
452: if (bUseTempCfg)
453: unlink(sTempCfgName);
454: }
455: return retval;
1.1.1.2 root 456: }
457:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.