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