bugfixes, new config system and messed code for it
[libpicofe.git] / common / config.c
CommitLineData
c51bd2fe 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"
3e85ebdd 11#include "lprintf.h"
c51bd2fe 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
367b6f1f 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
c51bd2fe 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
3e85ebdd 63static void custom_write(FILE *f, const menu_entry *me, int no_def)
c51bd2fe 64{
65 char *str, str24[24];
66
67 switch (me->id)
68 {
69 case MA_OPT_RENDERER:
3e85ebdd 70 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&0x10) &&
c51bd2fe 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:
3e85ebdd 82 if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
c51bd2fe 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:
3e85ebdd 92 if (no_def && defaultConfig.Frameskip == currentConfig.Frameskip) return;
c51bd2fe 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:
3e85ebdd 99 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&8) &&
c51bd2fe 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:
3e85ebdd 105 if (no_def && defaultConfig.s_PicoRegion == PicoRegionOverride &&
c51bd2fe 106 defaultConfig.s_PicoAutoRgnOrder == PicoAutoRgnOrder) return;
107 fprintf(f, "Region = %s", me_region_name(PicoRegionOverride, PicoAutoRgnOrder));
108 break;
109 case MA_OPT_CONFIRM_STATES:
3e85ebdd 110 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&(5<<9))) return;
c51bd2fe 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:
3e85ebdd 120 if (no_def && defaultConfig.CPUclock == currentConfig.CPUclock) return;
c51bd2fe 121 fprintf(f, "GP2X CPU clocks = %i", currentConfig.CPUclock);
122 break;
123 case MA_OPT2_GAMMA:
3e85ebdd 124 if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
c51bd2fe 125 fprintf(f, "Gamma correction = %.3f", (double)currentConfig.gamma / 100.0);
126 break;
127 case MA_OPT2_SQUIDGEHACK:
3e85ebdd 128 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x0010)) return;
c51bd2fe 129 fprintf(f, "Squidgehack = %i", (currentConfig.EmuOpt&0x0010)>>4);
130 break;
131 case MA_CDOPT_READAHEAD:
3e85ebdd 132 if (no_def && defaultConfig.s_PicoCDBuffers == PicoCDBuffers) return;
c51bd2fe 133 sprintf(str24, "%i", PicoCDBuffers * 2);
134 fprintf(f, "ReadAhead buffer = %s", str24);
135 break;
136
137 default:
3e85ebdd 138 lprintf("unhandled custom_write: %i\n", me->id);
c51bd2fe 139 return;
140 }
141 fprintf(f, NL);
142}
143
367b6f1f 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
3e85ebdd 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:
367b6f1f 201 case MA_OPT2_SVP_DYNAREC:
3e85ebdd 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}
c51bd2fe 225
226int config_writesect(const char *fname, const char *section)
227{
228 FILE *fo = NULL, *fn = NULL; // old and new
3e85ebdd 229 int no_defaults = 0; // avoid saving defaults
c51bd2fe 230 menu_entry *me;
231 int t, i, tlen, ret;
232 char line[128], *tmp;
233
234 if (section != NULL)
235 {
3e85ebdd 236 no_defaults = 1;
237
c51bd2fe 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)
3e85ebdd 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 }
c51bd2fe 316 }
317 }
367b6f1f 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
c51bd2fe 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
c51bd2fe 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
367b6f1f 354 if (strlen(lastRomFile) == 0) return 0;
c51bd2fe 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 }
367b6f1f 387 fprintf(f, "LastUsedROM = %s" NL, lastRomFile);
c51bd2fe 388 fclose(f);
389 return 0;
390}
391
367b6f1f 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
c51bd2fe 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;
3e85ebdd 495 for (i = 0; p < end && i < 3; i++)
496 {
497 while (*p == ' ') p++;
c51bd2fe 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 }
3e85ebdd 505 while (*p != ' ' && *p != 0) p++;
506 if (*p == 0) break;
c51bd2fe 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) {
367b6f1f 526 currentConfig.EmuOpt &= ~(5<<9);
c51bd2fe 527 } else if (strcasecmp(val, "writes") == 0) {
367b6f1f 528 currentConfig.EmuOpt &= ~(5<<9);
529 currentConfig.EmuOpt |= 1<<9;
c51bd2fe 530 } else if (strcasecmp(val, "loads") == 0) {
367b6f1f 531 currentConfig.EmuOpt &= ~(5<<9);
532 currentConfig.EmuOpt |= 4<<9;
c51bd2fe 533 } else if (strcasecmp(val, "both") == 0) {
367b6f1f 534 currentConfig.EmuOpt &= ~(5<<9);
535 currentConfig.EmuOpt |= 5<<9;
c51bd2fe 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:
3e85ebdd 563 lprintf("unhandled custom_read: %i\n", me->id);
c51bd2fe 564 return 0;
565 }
566}
567
568
367b6f1f 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
c51bd2fe 621static void parse(const char *var, const char *val)
622{
623 menu_entry *me;
624 int t, i, tlen, tmp, ret = 0;
625
367b6f1f 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
c51bd2fe 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 }
3e85ebdd 664 if (!ret) lprintf("config_readsect: unhandled var: %s\n", var);
c51bd2fe 665}
666
667
367b6f1f 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
c51bd2fe 682int config_readsect(const char *fname, const char *section)
683{
684 char line[128], *var, *val, *tmp;
c51bd2fe 685 int len, i, ret;
367b6f1f 686 FILE *f;
c51bd2fe 687
367b6f1f 688 f = fopen(fname, "r");
c51bd2fe 689 if (f == NULL) return 0;
690
691 if (section != NULL)
692 {
693 ret = seek_sect(f, section);
694 if (!ret) {
3e85ebdd 695 lprintf("config_readsect: %s: missing section [%s]\n", fname, section);
c51bd2fe 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) {
3e85ebdd 720 lprintf("config_readsect: can't parse: %s\n", line);
c51bd2fe 721 continue;
722 }
723 line[i] = 0;
724 var = line;
725 val = &line[i+1];
726 mystrip(var);
727 mystrip(val);
367b6f1f 728 if (strlen(var) == 0 || (strlen(val) == 0 && strncasecmp(var, "bind", 4) != 0)) {
3e85ebdd 729 lprintf("config_readsect: something's empty: \"%s\" = \"%s\"\n", var, val);
c51bd2fe 730 continue;
731 }
732
733 parse(var, val);
734 }
735
736 fclose(f);
737 return 0;
738}
739