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