bugfixes, new config system and messed code for it
[picodrive.git] / platform / common / config.c
CommitLineData
835e7900 1/*
2 * Human-readable config file management for PicoDrive
3 * (c)
4 */
5
6#include <string.h>
7#include <stdlib.h>
8#include "config.h"
9#include "menu.h"
10#include "emu.h"
c46ffd31 11#include "lprintf.h"
835e7900 12#include <Pico/Pico.h>
13
14extern menu_entry opt_entries[];
15extern menu_entry opt2_entries[];
16extern menu_entry cdopt_entries[];
17extern const int opt_entry_count;
18extern const int opt2_entry_count;
19extern const int cdopt_entry_count;
20
21static menu_entry *cfg_opts[] = { opt_entries, opt2_entries, cdopt_entries };
22static const int *cfg_opt_counts[] = { &opt_entry_count, &opt2_entry_count, &cdopt_entry_count };
23
24#define NL "\n"
25
26
1ca2ea4f 27static void mystrip(char *str)
28{
29 int i, len;
30
31 len = strlen(str);
32 for (i = 0; i < len; i++)
33 if (str[i] != ' ') break;
34 if (i > 0) memmove(str, str + i, len - i + 1);
35 len = strlen(str);
36 for (i = len - 1; i >= 0; i--)
37 if (str[i] != ' ') break;
38 str[i+1] = 0;
39}
40
41
835e7900 42static int seek_sect(FILE *f, const char *section)
43{
44 char line[128], *tmp;
45 int len;
46
47 len = strlen(section);
48 // seek to the section needed
49 while (!feof(f))
50 {
51 tmp = fgets(line, sizeof(line), f);
52 if (tmp == NULL) break;
53
54 if (line[0] != '[') continue; // not section start
55 if (strncmp(line + 1, section, len) == 0 && line[len+1] == ']')
56 return 1; // found it
57 }
58
59 return 0;
60}
61
62
c46ffd31 63static void custom_write(FILE *f, const menu_entry *me, int no_def)
835e7900 64{
65 char *str, str24[24];
66
67 switch (me->id)
68 {
69 case MA_OPT_RENDERER:
c46ffd31 70 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&0x10) &&
835e7900 71 !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x80)) return;
72 if (PicoOpt&0x10)
73 str = "8bit fast";
74 else if (currentConfig.EmuOpt&0x80)
75 str = "16bit accurate";
76 else
77 str = "8bit accurate";
78 fprintf(f, "Renderer = %s", str);
79 break;
80
81 case MA_OPT_SCALING:
c46ffd31 82 if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
835e7900 83 switch (currentConfig.scaling) {
84 default: str = "OFF"; break;
85 case 1: str = "hw horizontal"; break;
86 case 2: str = "hw horiz. + vert."; break;
87 case 3: str = "sw horizontal"; break;
88 }
89 fprintf(f, "Scaling = %s", str);
90 break;
91 case MA_OPT_FRAMESKIP:
c46ffd31 92 if (no_def && defaultConfig.Frameskip == currentConfig.Frameskip) return;
835e7900 93 if (currentConfig.Frameskip < 0)
94 strcpy(str24, "Auto");
95 else sprintf(str24, "%i", currentConfig.Frameskip);
96 fprintf(f, "Frameskip = %s", str24);
97 break;
98 case MA_OPT_SOUND_QUALITY:
c46ffd31 99 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&8) &&
835e7900 100 defaultConfig.s_PsndRate == PsndRate) return;
101 str = (PicoOpt&0x08)?"stereo":"mono";
102 fprintf(f, "Sound Quality = %i %s", PsndRate, str);
103 break;
104 case MA_OPT_REGION:
c46ffd31 105 if (no_def && defaultConfig.s_PicoRegion == PicoRegionOverride &&
835e7900 106 defaultConfig.s_PicoAutoRgnOrder == PicoAutoRgnOrder) return;
107 fprintf(f, "Region = %s", me_region_name(PicoRegionOverride, PicoAutoRgnOrder));
108 break;
109 case MA_OPT_CONFIRM_STATES:
c46ffd31 110 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&(5<<9))) return;
835e7900 111 switch ((currentConfig.EmuOpt >> 9) & 5) {
112 default: str = "OFF"; break;
113 case 1: str = "writes"; break;
114 case 4: str = "loads"; break;
115 case 5: str = "both"; break;
116 }
117 fprintf(f, "Confirm savestate = %s", str);
118 break;
119 case MA_OPT_CPU_CLOCKS:
c46ffd31 120 if (no_def && defaultConfig.CPUclock == currentConfig.CPUclock) return;
835e7900 121 fprintf(f, "GP2X CPU clocks = %i", currentConfig.CPUclock);
122 break;
123 case MA_OPT2_GAMMA:
c46ffd31 124 if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
835e7900 125 fprintf(f, "Gamma correction = %.3f", (double)currentConfig.gamma / 100.0);
126 break;
127 case MA_OPT2_SQUIDGEHACK:
c46ffd31 128 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x0010)) return;
835e7900 129 fprintf(f, "Squidgehack = %i", (currentConfig.EmuOpt&0x0010)>>4);
130 break;
131 case MA_CDOPT_READAHEAD:
c46ffd31 132 if (no_def && defaultConfig.s_PicoCDBuffers == PicoCDBuffers) return;
835e7900 133 sprintf(str24, "%i", PicoCDBuffers * 2);
134 fprintf(f, "ReadAhead buffer = %s", str24);
135 break;
136
137 default:
c46ffd31 138 lprintf("unhandled custom_write: %i\n", me->id);
835e7900 139 return;
140 }
141 fprintf(f, NL);
142}
143
1ca2ea4f 144
145static const char *joyKeyNames[32] =
146{
147 "UP", "DOWN", "LEFT", "RIGHT", "b1", "b2", "b3", "b4",
148 "b5", "b6", "b7", "b8", "b9", "b10", "b11", "b12",
149 "b13", "b14", "b15", "b16", "b17", "b19", "b19", "b20",
150 "b21", "b22", "b23", "b24", "b25", "b26", "b27", "b28"
151};
152
153static void keys_write(FILE *fn, const char *bind_str, const int binds[32],
154 const int def_binds[32], const char *names[32], int no_defaults)
155{
156 int t, i;
157 char act[48];
158
159 for (t = 0; t < 32; t++)
160 {
161 act[0] = act[31] = 0;
162 if (no_defaults && binds[t] == def_binds[t])
163 continue;
164 if (strcmp(names[t], "???") == 0) continue;
165#ifdef __GP2X__
166 if (strcmp(names[t], "SELECT") == 0) continue;
167#endif
168 for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
169 if (me_ctrl_actions[i].mask & binds[t]) {
170 sprintf(act, "player%i ", ((binds[t]>>16)&1)+1);
171 strncpy(act + 8, me_ctrl_actions[i].name, 31);
172 break;
173 }
174 }
175 if (act[0] == 0)
176 {
177 for (i = 0; emuctrl_actions[i].name != NULL; i++)
178 if (emuctrl_actions[i].mask & binds[t]) {
179 strncpy(act, emuctrl_actions[i].name, 31);
180 break;
181 }
182 }
183 mystrip(act);
184
185 fprintf(fn, "%s %s = %s" NL, bind_str, names[t], act);
186 }
187}
188
189
c46ffd31 190static int default_var(const menu_entry *me)
191{
192 switch (me->id)
193 {
194 case MA_OPT_ACC_TIMING:
195 case MA_OPT_ACC_SPRITES:
196 case MA_OPT_ARM940_SOUND:
197 case MA_OPT_6BUTTON_PAD:
198 case MA_OPT2_ENABLE_Z80:
199 case MA_OPT2_ENABLE_YM2612:
200 case MA_OPT2_ENABLE_SN76496:
1ca2ea4f 201 case MA_OPT2_SVP_DYNAREC:
c46ffd31 202 case MA_CDOPT_CDDA:
203 case MA_CDOPT_PCM:
204 case MA_CDOPT_SAVERAM:
205 case MA_CDOPT_SCALEROT_CHIP:
206 case MA_CDOPT_BETTER_SYNC:
207 return defaultConfig.s_PicoOpt;
208
209 case MA_OPT_SHOW_FPS:
210 case MA_OPT_ENABLE_SOUND:
211 case MA_OPT_SRAM_STATES:
212 case MA_OPT2_A_SN_GAMMA:
213 case MA_OPT2_VSYNC:
214 case MA_OPT2_GZIP_STATES:
215 case MA_OPT2_NO_LAST_ROM:
216 case MA_OPT2_RAMTIMINGS:
217 case MA_CDOPT_LEDS:
218 return defaultConfig.EmuOpt;
219
220 case MA_OPT_SAVE_SLOT:
221 default:
222 return 0;
223 }
224}
835e7900 225
226int config_writesect(const char *fname, const char *section)
227{
228 FILE *fo = NULL, *fn = NULL; // old and new
c46ffd31 229 int no_defaults = 0; // avoid saving defaults
835e7900 230 menu_entry *me;
231 int t, i, tlen, ret;
232 char line[128], *tmp;
233
234 if (section != NULL)
235 {
c46ffd31 236 no_defaults = 1;
237
835e7900 238 fo = fopen(fname, "r");
239 if (fo == NULL) {
240 fn = fopen(fname, "w");
241 goto write;
242 }
243
244 ret = seek_sect(fo, section);
245 if (!ret) {
246 // sect not found, we can simply append
247 fclose(fo); fo = NULL;
248 fn = fopen(fname, "a");
249 goto write;
250 }
251
252 // use 2 files..
253 fclose(fo);
254 rename(fname, "tmp.cfg");
255 fo = fopen("tmp.cfg", "r");
256 fn = fopen(fname, "w");
257 if (fo == NULL || fn == NULL) goto write;
258
259 // copy everything until sect
260 tlen = strlen(section);
261 while (!feof(fo))
262 {
263 tmp = fgets(line, sizeof(line), fo);
264 if (tmp == NULL) break;
265
266 if (line[0] == '[' && strncmp(line + 1, section, tlen) == 0 && line[tlen+1] == ']')
267 break;
268 fputs(line, fn);
269 }
270
271 // now skip to next sect
272 while (!feof(fo))
273 {
274 tmp = fgets(line, sizeof(line), fo);
275 if (tmp == NULL) break;
276 if (line[0] == '[') {
277 fseek(fo, -strlen(line), SEEK_CUR);
278 break;
279 }
280 }
281 if (feof(fo))
282 {
283 fclose(fo); fo = NULL;
284 remove("tmp.cfg");
285 }
286 }
287 else
288 {
289 fn = fopen(fname, "w");
290 }
291
292write:
293 if (fn == NULL) {
294 if (fo) fclose(fo);
295 return -1;
296 }
297 if (section != NULL)
298 fprintf(fn, "[%s]" NL, section);
299
300 for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]); t++)
301 {
302 me = cfg_opts[t];
303 tlen = *(cfg_opt_counts[t]);
304 for (i = 0; i < tlen; i++, me++)
305 {
306 if (!me->need_to_save) continue;
307 if ((me->beh != MB_ONOFF && me->beh != MB_RANGE) || me->name == NULL)
c46ffd31 308 custom_write(fn, me, no_defaults);
309 else if (me->beh == MB_ONOFF) {
310 if (!no_defaults || ((*(int *)me->var ^ default_var(me)) & me->mask))
311 fprintf(fn, "%s = %i" NL, me->name, (*(int *)me->var & me->mask) ? 1 : 0);
312 } else if (me->beh == MB_RANGE) {
313 if (!no_defaults || ((*(int *)me->var ^ default_var(me)) & me->mask))
314 fprintf(fn, "%s = %i" NL, me->name, *(int *)me->var);
315 }
835e7900 316 }
317 }
1ca2ea4f 318
319 // save key config
320 keys_write(fn, "bind", currentConfig.KeyBinds, defaultConfig.KeyBinds, keyNames, no_defaults);
321 keys_write(fn, "bind_joy0", currentConfig.JoyBinds[0], defaultConfig.JoyBinds[0], joyKeyNames, 1);
322 keys_write(fn, "bind_joy1", currentConfig.JoyBinds[1], defaultConfig.JoyBinds[1], joyKeyNames, 1);
323 keys_write(fn, "bind_joy2", currentConfig.JoyBinds[2], defaultConfig.JoyBinds[2], joyKeyNames, 1);
324 keys_write(fn, "bind_joy3", currentConfig.JoyBinds[3], defaultConfig.JoyBinds[3], joyKeyNames, 1);
325
835e7900 326 fprintf(fn, NL);
327
328 if (fo != NULL)
329 {
330 // copy whatever is left
331 while (!feof(fo))
332 {
333 tmp = fgets(line, sizeof(line), fo);
334 if (tmp == NULL) break;
335
336 fputs(line, fn);
337 }
338 fclose(fo);
339 remove("tmp.cfg");
340 }
341
342 fclose(fn);
343 return 0;
344}
345
346
835e7900 347int config_writelrom(const char *fname)
348{
349 char line[128], *tmp, *optr = NULL;
350 char *old_data = NULL;
351 int size;
352 FILE *f;
353
1ca2ea4f 354 if (strlen(lastRomFile) == 0) return 0;
835e7900 355
356 f = fopen(fname, "r");
357 if (f != NULL)
358 {
359 fseek(f, 0, SEEK_END);
360 size = ftell(f);
361 fseek(f, 0, SEEK_SET);
362 old_data = malloc(size + size/8);
363 if (old_data != NULL)
364 {
365 optr = old_data;
366 while (!feof(f))
367 {
368 tmp = fgets(line, sizeof(line), f);
369 if (tmp == NULL) break;
370 mystrip(line);
371 if (strncasecmp(line, "LastUsedROM", 11) == 0)
372 continue;
373 sprintf(optr, "%s", line);
374 optr += strlen(optr);
375 }
376 }
377 fclose(f);
378 }
379
380 f = fopen(fname, "w");
381 if (f == NULL) return -1;
382
383 if (old_data != NULL) {
384 fwrite(old_data, 1, optr - old_data, f);
385 free(old_data);
386 }
1ca2ea4f 387 fprintf(f, "LastUsedROM = %s" NL, lastRomFile);
835e7900 388 fclose(f);
389 return 0;
390}
391
1ca2ea4f 392/* --------------------------------------------------------------------------*/
393
394int config_readlrom(const char *fname)
395{
396 char line[128], *tmp;
397 int i, len, ret = -1;
398 FILE *f;
399
400 f = fopen(fname, "r");
401 if (f == NULL) return -1;
402
403 // seek to the section needed
404 while (!feof(f))
405 {
406 tmp = fgets(line, sizeof(line), f);
407 if (tmp == NULL) break;
408
409 if (strncasecmp(line, "LastUsedROM", 11) != 0) continue;
410 len = strlen(line);
411 for (i = 0; i < len; i++)
412 if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
413 tmp = strchr(line, '=');
414 if (tmp == NULL) break;
415 tmp++;
416 mystrip(tmp);
417
418 len = sizeof(lastRomFile);
419 strncpy(lastRomFile, tmp, len);
420 lastRomFile[len-1] = 0;
421 ret = 0;
422 break;
423 }
424 fclose(f);
425 return ret;
426}
427
835e7900 428
429static int custom_read(menu_entry *me, const char *var, const char *val)
430{
431 char *tmp;
432 int tmpi;
433
434 switch (me->id)
435 {
436 case MA_OPT_RENDERER:
437 if (strcasecmp(var, "Renderer") != 0) return 0;
438 if (strcasecmp(val, "8bit fast") == 0) {
439 PicoOpt |= 0x10;
440 }
441 else if (strcasecmp(val, "16bit accurate") == 0) {
442 PicoOpt &= ~0x10;
443 currentConfig.EmuOpt |= 0x80;
444 }
445 else if (strcasecmp(val, "8bit accurate") == 0) {
446 PicoOpt &= ~0x10;
447 currentConfig.EmuOpt &= ~0x80;
448 }
449 else
450 return 0;
451 return 1;
452
453 case MA_OPT_SCALING:
454 if (strcasecmp(var, "Scaling") != 0) return 0;
455 if (strcasecmp(val, "OFF") == 0) {
456 currentConfig.scaling = 0;
457 } else if (strcasecmp(val, "hw horizontal") == 0) {
458 currentConfig.scaling = 1;
459 } else if (strcasecmp(val, "hw horiz. + vert.") == 0) {
460 currentConfig.scaling = 2;
461 } else if (strcasecmp(val, "sw horizontal") == 0) {
462 currentConfig.scaling = 3;
463 } else
464 return 0;
465 return 1;
466
467 case MA_OPT_FRAMESKIP:
468 if (strcasecmp(var, "Frameskip") != 0) return 0;
469 if (strcasecmp(val, "Auto") == 0)
470 currentConfig.Frameskip = -1;
471 else currentConfig.Frameskip = atoi(val);
472 return 1;
473
474 case MA_OPT_SOUND_QUALITY:
475 if (strcasecmp(var, "Sound Quality") != 0) return 0;
476 PsndRate = strtoul(val, &tmp, 10);
477 if (PsndRate < 8000 || PsndRate > 44100)
478 PsndRate = 22050;
479 while (*tmp == ' ') tmp++;
480 if (strcasecmp(tmp, "stereo") == 0) {
481 PicoOpt |= 8;
482 } else if (strcasecmp(tmp, "mono") == 0) {
483 PicoOpt &= ~8;
484 } else
485 return 0;
486 return 1;
487
488 case MA_OPT_REGION:
489 if (strcasecmp(var, "Region") != 0) return 0;
490 if (strncasecmp(val, "Auto: ", 6) == 0)
491 {
492 const char *p = val + 5, *end = val + strlen(val);
493 int i;
494 PicoRegionOverride = PicoAutoRgnOrder = 0;
c46ffd31 495 for (i = 0; p < end && i < 3; i++)
496 {
497 while (*p == ' ') p++;
835e7900 498 if (p[0] == 'J' && p[1] == 'P') {
499 PicoAutoRgnOrder |= 1 << (i*4);
500 } else if (p[0] == 'U' && p[1] == 'S') {
501 PicoAutoRgnOrder |= 4 << (i*4);
502 } else if (p[0] == 'E' && p[1] == 'U') {
503 PicoAutoRgnOrder |= 8 << (i*4);
504 }
c46ffd31 505 while (*p != ' ' && *p != 0) p++;
506 if (*p == 0) break;
835e7900 507 }
508 }
509 else if (strcasecmp(val, "Auto") == 0) {
510 PicoRegionOverride = 0;
511 } else if (strcasecmp(val, "Japan NTSC") == 0) {
512 PicoRegionOverride = 1;
513 } else if (strcasecmp(val, "Japan PAL") == 0) {
514 PicoRegionOverride = 2;
515 } else if (strcasecmp(val, "USA") == 0) {
516 PicoRegionOverride = 4;
517 } else if (strcasecmp(val, "Europe") == 0) {
518 PicoRegionOverride = 8;
519 } else
520 return 0;
521 return 1;
522
523 case MA_OPT_CONFIRM_STATES:
524 if (strcasecmp(var, "Confirm savestate") != 0) return 0;
525 if (strcasecmp(val, "OFF") == 0) {
1ca2ea4f 526 currentConfig.EmuOpt &= ~(5<<9);
835e7900 527 } else if (strcasecmp(val, "writes") == 0) {
1ca2ea4f 528 currentConfig.EmuOpt &= ~(5<<9);
529 currentConfig.EmuOpt |= 1<<9;
835e7900 530 } else if (strcasecmp(val, "loads") == 0) {
1ca2ea4f 531 currentConfig.EmuOpt &= ~(5<<9);
532 currentConfig.EmuOpt |= 4<<9;
835e7900 533 } else if (strcasecmp(val, "both") == 0) {
1ca2ea4f 534 currentConfig.EmuOpt &= ~(5<<9);
535 currentConfig.EmuOpt |= 5<<9;
835e7900 536 } else
537 return 0;
538 return 1;
539
540 case MA_OPT_CPU_CLOCKS:
541 if (strcasecmp(var, "GP2X CPU clocks") != 0) return 0;
542 currentConfig.CPUclock = atoi(val);
543 return 1;
544
545 case MA_OPT2_GAMMA:
546 if (strcasecmp(var, "Gamma correction") != 0) return 0;
547 currentConfig.gamma = (int) (atof(val) * 100.0);
548 return 1;
549
550 case MA_OPT2_SQUIDGEHACK:
551 if (strcasecmp(var, "Squidgehack") != 0) return 0;
552 tmpi = atoi(val);
553 if (tmpi) *(int *)me->var |= me->mask;
554 else *(int *)me->var &= ~me->mask;
555 return 1;
556
557 case MA_CDOPT_READAHEAD:
558 if (strcasecmp(var, "ReadAhead buffer") != 0) return 0;
559 PicoCDBuffers = atoi(val) / 2;
560 return 1;
561
562 default:
c46ffd31 563 lprintf("unhandled custom_read: %i\n", me->id);
835e7900 564 return 0;
565 }
566}
567
568
1ca2ea4f 569static void keys_parse(const char *var, const char *val, int binds[32], const char *names[32])
570{
571 int t, i, keys_encountered = 0;
572 unsigned int player;
573
574 for (t = 0; t < 32; t++)
575 {
576 if (strcmp(names[t], var) == 0) break;
577 }
578 if (t == 32) {
579 lprintf("unhandled bind \"%s\"\n", var);
580 return;
581 }
582
583 if (!(keys_encountered & (1<<t))) {
584 binds[t] = 0;
585 keys_encountered |= 1<<t;
586 }
587 if (val[0] == 0)
588 return;
589 if (strncasecmp(val, "player", 6) == 0)
590 {
591 player = atoi(val + 6) - 1;
592 if (player > 1) goto fail;
593 for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
594 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0) {
595 binds[t] |= me_ctrl_actions[i].mask | (player<<16);
596 return;
597 }
598 }
599 }
600 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
601 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
602 binds[t] |= emuctrl_actions[i].mask;
603 return;
604 }
605 }
606
607fail:
608 lprintf("unhandled action \"%s\"\n", val);
609 return;
610
611}
612
613
614#define try_joy_parse(num) { \
615 if (strncasecmp(var, "bind_joy"#num " ", 10) == 0) { \
616 keys_parse(var + 10, val, currentConfig.JoyBinds[num], joyKeyNames); \
617 return; \
618 } \
619}
620
835e7900 621static void parse(const char *var, const char *val)
622{
623 menu_entry *me;
624 int t, i, tlen, tmp, ret = 0;
625
1ca2ea4f 626 if (strcasecmp(var, "LastUsedROM") == 0)
627 return; /* handled elsewhere */
628
629 // key binds
630 if (strncasecmp(var, "bind ", 5) == 0) {
631 keys_parse(var + 5, val, currentConfig.KeyBinds, keyNames);
632 return;
633 }
634 try_joy_parse(0)
635 try_joy_parse(1)
636 try_joy_parse(2)
637 try_joy_parse(3)
638
835e7900 639 for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]) && ret == 0; t++)
640 {
641 me = cfg_opts[t];
642 tlen = *(cfg_opt_counts[t]);
643 for (i = 0; i < tlen && ret == 0; i++, me++)
644 {
645 if (!me->need_to_save) continue;
646 if (me->name != NULL) {
647 if (strcasecmp(var, me->name) != 0) continue; // surely not this one
648 if (me->beh == MB_ONOFF) {
649 tmp = atoi(val);
650 if (tmp) *(int *)me->var |= me->mask;
651 else *(int *)me->var &= ~me->mask;
652 return;
653 } else if (me->beh == MB_RANGE) {
654 tmp = atoi(val);
655 if (tmp < me->min) tmp = me->min;
656 if (tmp > me->max) tmp = me->max;
657 *(int *)me->var = tmp;
658 return;
659 }
660 }
661 ret = custom_read(me, var, val);
662 }
663 }
c46ffd31 664 if (!ret) lprintf("config_readsect: unhandled var: %s\n", var);
835e7900 665}
666
667
1ca2ea4f 668int config_havesect(const char *fname, const char *section)
669{
670 FILE *f;
671 int ret;
672
673 f = fopen(fname, "r");
674 if (f == NULL) return 0;
675
676 ret = seek_sect(f, section);
677 fclose(f);
678 return ret;
679}
680
681
835e7900 682int config_readsect(const char *fname, const char *section)
683{
684 char line[128], *var, *val, *tmp;
835e7900 685 int len, i, ret;
1ca2ea4f 686 FILE *f;
835e7900 687
1ca2ea4f 688 f = fopen(fname, "r");
835e7900 689 if (f == NULL) return 0;
690
691 if (section != NULL)
692 {
693 ret = seek_sect(f, section);
694 if (!ret) {
c46ffd31 695 lprintf("config_readsect: %s: missing section [%s]\n", fname, section);
835e7900 696 fclose(f);
697 return -1;
698 }
699 }
700
701 while (!feof(f))
702 {
703 tmp = fgets(line, sizeof(line), f);
704 if (tmp == NULL) break;
705
706 if (line[0] == '[') break; // other section
707
708 // strip comments, linefeed, spaces..
709 len = strlen(line);
710 for (i = 0; i < len; i++)
711 if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
712 mystrip(line);
713 len = strlen(line);
714 if (len <= 0) continue;
715
716 // get var and val
717 for (i = 0; i < len; i++)
718 if (line[i] == '=') break;
719 if (i >= len || strchr(&line[i+1], '=') != NULL) {
c46ffd31 720 lprintf("config_readsect: can't parse: %s\n", line);
835e7900 721 continue;
722 }
723 line[i] = 0;
724 var = line;
725 val = &line[i+1];
726 mystrip(var);
727 mystrip(val);
1ca2ea4f 728 if (strlen(var) == 0 || (strlen(val) == 0 && strncasecmp(var, "bind", 4) != 0)) {
c46ffd31 729 lprintf("config_readsect: something's empty: \"%s\" = \"%s\"\n", var, val);
835e7900 730 continue;
731 }
732
733 parse(var, val);
734 }
735
736 fclose(f);
737 return 0;
738}
739