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