|
|
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: }
99: while ( memcmp(line,header,strlen(header)) && !feof(file));
100: }
101:
102: if ( !feof(file) )
103: do
104: {
105: fptr = Str_Trim(fgets(line, sizeof(line), file)); /* get input line */
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 */
113: tok = Str_Trim(strtok(line, "=\n\r")); /* get first token */
114: if (tok != NULL)
115: {
116: next = Str_Trim(strtok(NULL, "=\n\r")); /* get actual config information */
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"))
125: *((bool *)(ptr->buf)) = false;
126: else if (!strcasecmp(next,"TRUE"))
127: *((bool *)(ptr->buf)) = true;
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]!='[');
180:
181: fclose(file);
182: return count;
183: }
184:
185:
186: /**
187: * Write out an settings line
188: */
189: static int write_token(FILE *outfile, const struct Config_Tag *ptr)
190: {
191: fprintf(outfile,"%s = ",ptr->code);
192:
193: switch (ptr->type) /* check type */
194: {
195: case Bool_Tag:
196: fprintf(outfile,"%s\n", *((bool *)(ptr->buf)) ? "TRUE" : "FALSE");
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: }
232:
233: return 0;
234: }
235:
236:
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: */
275: int update_config(const char *filename, const struct Config_Tag configs[], const char *header)
276: {
277: const struct Config_Tag *ptr;
278: int count=0, lineno=0, retval;
279: FILE *cfgfile, *tempfile;
280: char *fptr, *tok, *next;
281: char line[1024];
282: bool bUseTempCfg = false;
283: const char *sTempCfgName = "_temp_.cfg";
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. */
293: count = write_header_tokens(cfgfile, configs, header);
294: fclose(cfgfile);
295: return count;
296: }
297:
298: tempfile = tmpfile(); /* Open a temporary file for output */
299: if (tempfile == NULL)
300: {
301: /* tmpfile() failed, let's try a normal open */
302: tempfile = fopen(sTempCfgName, "w+");
303: bUseTempCfg = true;
304: }
305: if (tempfile == NULL)
306: {
307: perror("update_config");
308: fclose(cfgfile);
309: return -1; /* return error designation. */
310: }
311:
312: if (header != NULL)
313: {
314: int headerlen = strlen(header);
315: do
316: {
317: fptr = Str_Trim(fgets(line, sizeof(line), cfgfile)); /* get input line */
318: if (feof(cfgfile))
319: break;
320: fprintf(tempfile, "%s\n", line);
321: }
322: while(memcmp(line, header, headerlen));
323: }
324:
325: if (feof(cfgfile))
326: {
327: count += write_header_tokens(tempfile, configs, header);
328: }
329: else
330: {
331: char *savedtokenflags = NULL; /* Array to log the saved tokens */
332: int numtokens = 0; /* Total number of tokens to save */
333:
334: /* Find total number of tokens: */
335: for (ptr=configs; ptr->buf; ++ptr)
336: {
337: numtokens += 1;
338: }
339: if (numtokens)
340: {
341: savedtokenflags = malloc(numtokens * sizeof(char));
342: if (savedtokenflags)
343: memset(savedtokenflags, 0, numtokens * sizeof(char));
344: }
345:
346: for(;;)
347: {
348: fptr = Str_Trim(fgets(line, sizeof(line), cfgfile)); /* get input line */
349: if (fptr == NULL)
350: break;
351: lineno++;
352: if (line[0] == '#')
353: {
354: fprintf(tempfile, "%s\n", line);
355: continue; /* skip comments */
356: }
357: if (line[0] == '[' || feof(cfgfile))
358: {
359: break;
360: }
361:
362: tok = Str_Trim(strtok(line, "=\n\r")); /* get first token */
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)
400: {
401: free(savedtokenflags);
402: savedtokenflags = NULL;
403: }
404:
405: if (!feof(cfgfile) && fptr != NULL)
406: fprintf(tempfile, "\n%s\n", line);
407:
408: for(;;)
409: {
410: fptr = Str_Trim(fgets(line, sizeof(line), cfgfile)); /* get input line */
411: if (feof(cfgfile))
412: break;
413: fprintf(tempfile, "%s\n", line);
414: }
415: }
416:
417:
418: /* Re-open the config file for writing: */
419: fclose(cfgfile);
420: cfgfile = fopen(filename, "wb");
421: if (cfgfile == NULL || fseek(tempfile, 0, SEEK_SET) != 0)
422: {
423: retval = -1;
424: goto cleanup;
425: }
426:
427: /* Now copy the temporary file to the configuration file: */
428: retval = count;
429: while(!(feof(tempfile) || ferror(cfgfile)))
430: {
431: size_t copycount;
432: copycount = fread(line, sizeof(char), sizeof(line), tempfile);
433: if (copycount == 0)
434: break;
435: if (fwrite(line, sizeof(char), copycount, cfgfile) != copycount)
436: {
437: retval = -1;
438: break;
439: }
440: }
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;
456: }
457:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.