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