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