2 * Human-readable config file management for PicoDrive
15 static char *mystrip(char *str);
21 #include <Pico/Pico.h>
23 extern menu_entry opt_entries[];
24 extern menu_entry opt2_entries[];
25 extern menu_entry cdopt_entries[];
26 extern menu_entry ctrlopt_entries[];
27 extern const int opt_entry_count;
28 extern const int opt2_entry_count;
29 extern const int cdopt_entry_count;
30 extern const int ctrlopt_entry_count;
32 extern menu_entry opt3_entries[];
33 extern const int opt3_entry_count;
36 static menu_entry *cfg_opts[] =
47 static const int *cfg_opt_counts[] =
61 static int seek_sect(FILE *f, const char *section)
66 len = strlen(section);
67 // seek to the section needed
70 tmp = fgets(line, sizeof(line), f);
71 if (tmp == NULL) break;
73 if (line[0] != '[') continue; // not section start
74 if (strncmp(line + 1, section, len) == 0 && line[len+1] == ']')
82 static void custom_write(FILE *f, const menu_entry *me, int no_def)
89 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_ALT_RENDERER) &&
90 !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x80)) return;
91 if (PicoOpt&POPT_ALT_RENDERER)
97 else if (currentConfig.EmuOpt&0x80)
104 str = "8bit accurate";
105 fprintf(f, "Renderer = %s", str);
109 if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
111 switch (currentConfig.scaling) {
112 default: str = "OFF"; break;
113 case 1: str = "hw horizontal"; break;
114 case 2: str = "hw horiz. + vert."; break;
115 case 3: str = "sw horizontal"; break;
117 fprintf(f, "Scaling = %s", str);
120 case MA_OPT_FRAMESKIP:
121 if (no_def && defaultConfig.Frameskip == currentConfig.Frameskip) return;
122 if (currentConfig.Frameskip < 0)
123 strcpy(str24, "Auto");
124 else sprintf(str24, "%i", currentConfig.Frameskip);
125 fprintf(f, "Frameskip = %s", str24);
127 case MA_OPT_SOUND_QUALITY:
128 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_EN_STEREO) &&
129 defaultConfig.s_PsndRate == PsndRate) return;
130 str = (PicoOpt&POPT_EN_STEREO)?"stereo":"mono";
131 fprintf(f, "Sound Quality = %i %s", PsndRate, str);
134 if (no_def && defaultConfig.s_PicoRegion == PicoRegionOverride &&
135 defaultConfig.s_PicoAutoRgnOrder == PicoAutoRgnOrder) return;
136 strncpy(str24, me_region_name(PicoRegionOverride, PicoAutoRgnOrder), 23); str24[23] = 0;
137 fprintf(f, "Region = %s", mystrip(str24));
139 case MA_OPT_CONFIRM_STATES:
140 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&(5<<9))) return;
141 switch ((currentConfig.EmuOpt >> 9) & 5) {
142 default: str = "OFF"; break;
143 case 1: str = "writes"; break;
144 case 4: str = "loads"; break;
145 case 5: str = "both"; break;
147 fprintf(f, "Confirm savestate = %s", str);
149 case MA_OPT_CPU_CLOCKS:
150 if (no_def && defaultConfig.CPUclock == currentConfig.CPUclock) return;
152 fprintf(f, "GP2X CPU clocks = %i", currentConfig.CPUclock);
154 fprintf(f, "PSP CPU clock = %i", currentConfig.CPUclock);
158 if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
159 fprintf(f, "Gamma correction = %.3f", (double)currentConfig.gamma / 100.0);
161 case MA_OPT2_SQUIDGEHACK:
162 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x0010)) return;
163 fprintf(f, "Squidgehack = %i", (currentConfig.EmuOpt&0x0010)>>4);
165 case MA_CDOPT_READAHEAD:
166 if (no_def && defaultConfig.s_PicoCDBuffers == PicoCDBuffers) return;
167 sprintf(str24, "%i", PicoCDBuffers * 2);
168 fprintf(f, "ReadAhead buffer = %s", str24);
172 if (no_def && defaultConfig.scale == currentConfig.scale) return;
173 fprintf(f, "Scale factor = %.2f", currentConfig.scale);
175 case MA_OPT3_HSCALE32:
176 if (no_def && defaultConfig.hscale32 == currentConfig.hscale32) return;
177 fprintf(f, "Hor. scale (for low res. games) = %.2f", currentConfig.hscale32);
179 case MA_OPT3_HSCALE40:
180 if (no_def && defaultConfig.hscale40 == currentConfig.hscale40) return;
181 fprintf(f, "Hor. scale (for hi res. games) = %.2f", currentConfig.hscale40);
183 case MA_OPT3_FILTERING:
184 if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
185 fprintf(f, "Bilinear filtering = %i", currentConfig.scaling);
188 if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
189 fprintf(f, "Gamma adjustment = %i", currentConfig.gamma);
191 case MA_OPT3_BLACKLVL:
192 if (no_def && defaultConfig.gamma2 == currentConfig.gamma2) return;
193 fprintf(f, "Black level = %i", currentConfig.gamma2);
196 if (no_def && (defaultConfig.EmuOpt&0x12000) == (currentConfig.gamma2&0x12000)) return;
197 strcpy(str24, "never");
198 if (currentConfig.EmuOpt & 0x2000)
199 strcpy(str24, (currentConfig.EmuOpt & 0x10000) ? "sometimes" : "always");
200 fprintf(f, "Wait for vsync = %s", str24);
204 lprintf("unhandled custom_write: %i\n", me->id);
212 static const char *joyKeyNames[32] =
214 "UP", "DOWN", "LEFT", "RIGHT", "b1", "b2", "b3", "b4",
215 "b5", "b6", "b7", "b8", "b9", "b10", "b11", "b12",
216 "b13", "b14", "b15", "b16", "b17", "b19", "b19", "b20",
217 "b21", "b22", "b23", "b24", "b25", "b26", "b27", "b28"
221 static void keys_write(FILE *fn, const char *bind_str, const int binds[32],
222 const int def_binds[32], const char * const names[32], int key_count, int no_defaults)
225 char act[48], name[32];
227 for (t = 0; t < key_count; t++)
229 act[0] = act[31] = 0;
230 if (no_defaults && binds[t] == def_binds[t])
233 if (strcmp(names[t], "SELECT") == 0) continue;
235 if (t >= 32 || names[t] == NULL || strcmp(names[t], "???") == 0) {
236 if ((t >= '0' && t <= '9') || (t >= 'a' && t <= 'z') || (t >= 'A' && t <= 'Z'))
237 sprintf(name, "%c", t);
239 sprintf(name, "\\x%02x", t);
242 strcpy(name, names[t]);
244 if (binds[t] == 0 && def_binds[t] != 0) {
245 fprintf(fn, "%s %s =" NL, bind_str, name); // no binds
249 for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
250 if (me_ctrl_actions[i].mask & binds[t]) {
251 strncpy(act, me_ctrl_actions[i].name, 31);
252 fprintf(fn, "%s %s = player%i %s" NL, bind_str, name,
253 ((binds[t]>>16)&1)+1, mystrip(act));
257 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
258 if (emuctrl_actions[i].mask & binds[t]) {
259 strncpy(act, emuctrl_actions[i].name, 31);
260 fprintf(fn, "%s %s = %s" NL, bind_str, name, mystrip(act));
267 static int default_var(const menu_entry *me)
271 case MA_OPT_ACC_TIMING:
272 case MA_OPT_ACC_SPRITES:
273 case MA_OPT_ARM940_SOUND:
274 case MA_OPT_6BUTTON_PAD:
275 case MA_OPT2_ENABLE_Z80:
276 case MA_OPT2_ENABLE_YM2612:
277 case MA_OPT2_ENABLE_SN76496:
278 case MA_OPT2_SVP_DYNAREC:
281 case MA_CDOPT_SAVERAM:
282 case MA_CDOPT_SCALEROT_CHIP:
283 case MA_CDOPT_BETTER_SYNC:
284 return defaultConfig.s_PicoOpt;
286 case MA_OPT_SHOW_FPS:
287 case MA_OPT_ENABLE_SOUND:
288 case MA_OPT_SRAM_STATES:
289 case MA_OPT2_A_SN_GAMMA:
291 case MA_OPT2_GZIP_STATES:
292 case MA_OPT2_NO_LAST_ROM:
293 case MA_OPT2_RAMTIMINGS:
295 return defaultConfig.EmuOpt;
297 case MA_CTRL_TURBO_RATE: return defaultConfig.turbo_rate;
298 case MA_OPT_SCALING: return defaultConfig.scaling;
299 case MA_OPT_ROTATION: return defaultConfig.rotation;
301 case MA_OPT_SAVE_SLOT:
307 int config_writesect(const char *fname, const char *section)
309 FILE *fo = NULL, *fn = NULL; // old and new
310 int no_defaults = 0; // avoid saving defaults
313 char line[128], *tmp;
319 fo = fopen(fname, "r");
321 fn = fopen(fname, "w");
325 ret = seek_sect(fo, section);
327 // sect not found, we can simply append
328 fclose(fo); fo = NULL;
329 fn = fopen(fname, "a");
335 rename(fname, "tmp.cfg");
336 fo = fopen("tmp.cfg", "r");
337 fn = fopen(fname, "w");
338 if (fo == NULL || fn == NULL) goto write;
340 // copy everything until sect
341 tlen = strlen(section);
344 tmp = fgets(line, sizeof(line), fo);
345 if (tmp == NULL) break;
347 if (line[0] == '[' && strncmp(line + 1, section, tlen) == 0 && line[tlen+1] == ']')
352 // now skip to next sect
355 tmp = fgets(line, sizeof(line), fo);
356 if (tmp == NULL) break;
357 if (line[0] == '[') {
358 fseek(fo, -strlen(line), SEEK_CUR);
364 fclose(fo); fo = NULL;
370 fn = fopen(fname, "w");
379 fprintf(fn, "[%s]" NL, section);
381 for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]); t++)
384 tlen = *(cfg_opt_counts[t]);
385 for (i = 0; i < tlen; i++, me++)
387 if (!me->need_to_save) continue;
388 if ((me->beh != MB_ONOFF && me->beh != MB_RANGE) || me->name == NULL)
389 custom_write(fn, me, no_defaults);
390 else if (me->beh == MB_ONOFF) {
391 if (!no_defaults || ((*(int *)me->var ^ default_var(me)) & me->mask))
392 fprintf(fn, "%s = %i" NL, me->name, (*(int *)me->var & me->mask) ? 1 : 0);
393 } else if (me->beh == MB_RANGE) {
394 if (!no_defaults || (*(int *)me->var ^ default_var(me)))
395 fprintf(fn, "%s = %i" NL, me->name, *(int *)me->var);
401 keys_write(fn, "bind", currentConfig.KeyBinds, defaultConfig.KeyBinds, keyNames, PLAT_MAX_KEYS, no_defaults);
403 keys_write(fn, "bind_joy0", currentConfig.JoyBinds[0], defaultConfig.JoyBinds[0], joyKeyNames, 32, 1);
404 keys_write(fn, "bind_joy1", currentConfig.JoyBinds[1], defaultConfig.JoyBinds[1], joyKeyNames, 32, 1);
405 keys_write(fn, "bind_joy2", currentConfig.JoyBinds[2], defaultConfig.JoyBinds[2], joyKeyNames, 32, 1);
406 keys_write(fn, "bind_joy3", currentConfig.JoyBinds[3], defaultConfig.JoyBinds[3], joyKeyNames, 32, 1);
411 fprintf(fn, "Sound Volume = %i" NL, currentConfig.volume);
418 // copy whatever is left
421 tmp = fgets(line, sizeof(line), fo);
422 if (tmp == NULL) break;
435 int config_writelrom(const char *fname)
437 char line[128], *tmp, *optr = NULL;
438 char *old_data = NULL;
442 if (strlen(loadedRomFName) == 0) return -1;
444 f = fopen(fname, "r");
447 fseek(f, 0, SEEK_END);
449 fseek(f, 0, SEEK_SET);
450 old_data = malloc(size + size/8);
451 if (old_data != NULL)
456 tmp = fgets(line, sizeof(line), f);
457 if (tmp == NULL) break;
459 if (strncasecmp(line, "LastUsedROM", 11) == 0)
461 sprintf(optr, "%s", line);
462 optr += strlen(optr);
468 f = fopen(fname, "w");
469 if (f == NULL) return -1;
471 if (old_data != NULL) {
472 fwrite(old_data, 1, optr - old_data, f);
475 fprintf(f, "LastUsedROM = %s" NL, loadedRomFName);
480 /* --------------------------------------------------------------------------*/
482 int config_readlrom(const char *fname)
484 char line[128], *tmp;
485 int i, len, ret = -1;
488 f = fopen(fname, "r");
489 if (f == NULL) return -1;
491 // seek to the section needed
494 tmp = fgets(line, sizeof(line), f);
495 if (tmp == NULL) break;
497 if (strncasecmp(line, "LastUsedROM", 11) != 0) continue;
499 for (i = 0; i < len; i++)
500 if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
501 tmp = strchr(line, '=');
502 if (tmp == NULL) break;
506 len = sizeof(loadedRomFName);
507 strncpy(loadedRomFName, tmp, len);
508 loadedRomFName[len-1] = 0;
517 static int custom_read(menu_entry *me, const char *var, const char *val)
524 case MA_OPT_RENDERER:
525 if (strcasecmp(var, "Renderer") != 0) return 0;
526 if (strcasecmp(val, "8bit fast") == 0 || strcasecmp(val, "fast") == 0) {
527 PicoOpt |= POPT_ALT_RENDERER;
529 else if (strcasecmp(val, "16bit accurate") == 0 || strcasecmp(val, "accurate") == 0) {
530 PicoOpt &= ~POPT_ALT_RENDERER;
531 currentConfig.EmuOpt |= 0x80;
533 else if (strcasecmp(val, "8bit accurate") == 0) {
534 PicoOpt &= ~POPT_ALT_RENDERER;
535 currentConfig.EmuOpt &= ~0x80;
543 if (strcasecmp(var, "Scaling") != 0) return 0;
544 if (strcasecmp(val, "OFF") == 0) {
545 currentConfig.scaling = 0;
546 } else if (strcasecmp(val, "hw horizontal") == 0) {
547 currentConfig.scaling = 1;
548 } else if (strcasecmp(val, "hw horiz. + vert.") == 0) {
549 currentConfig.scaling = 2;
550 } else if (strcasecmp(val, "sw horizontal") == 0) {
551 currentConfig.scaling = 3;
559 case MA_OPT_FRAMESKIP:
560 if (strcasecmp(var, "Frameskip") != 0) return 0;
561 if (strcasecmp(val, "Auto") == 0)
562 currentConfig.Frameskip = -1;
563 else currentConfig.Frameskip = atoi(val);
566 case MA_OPT_SOUND_QUALITY:
567 if (strcasecmp(var, "Sound Quality") != 0) return 0;
568 PsndRate = strtoul(val, &tmp, 10);
569 if (PsndRate < 8000 || PsndRate > 44100)
571 while (*tmp == ' ') tmp++;
572 if (strcasecmp(tmp, "stereo") == 0) {
573 PicoOpt |= POPT_EN_STEREO;
574 } else if (strcasecmp(tmp, "mono") == 0) {
575 PicoOpt &= ~POPT_EN_STEREO;
581 if (strcasecmp(var, "Region") != 0) return 0;
582 if (strncasecmp(val, "Auto: ", 6) == 0)
584 const char *p = val + 5, *end = val + strlen(val);
586 PicoRegionOverride = PicoAutoRgnOrder = 0;
587 for (i = 0; p < end && i < 3; i++)
589 while (*p == ' ') p++;
590 if (p[0] == 'J' && p[1] == 'P') {
591 PicoAutoRgnOrder |= 1 << (i*4);
592 } else if (p[0] == 'U' && p[1] == 'S') {
593 PicoAutoRgnOrder |= 4 << (i*4);
594 } else if (p[0] == 'E' && p[1] == 'U') {
595 PicoAutoRgnOrder |= 8 << (i*4);
597 while (*p != ' ' && *p != 0) p++;
601 else if (strcasecmp(val, "Auto") == 0) {
602 PicoRegionOverride = 0;
603 } else if (strcasecmp(val, "Japan NTSC") == 0) {
604 PicoRegionOverride = 1;
605 } else if (strcasecmp(val, "Japan PAL") == 0) {
606 PicoRegionOverride = 2;
607 } else if (strcasecmp(val, "USA") == 0) {
608 PicoRegionOverride = 4;
609 } else if (strcasecmp(val, "Europe") == 0) {
610 PicoRegionOverride = 8;
615 case MA_OPT_CONFIRM_STATES:
616 if (strcasecmp(var, "Confirm savestate") != 0) return 0;
617 if (strcasecmp(val, "OFF") == 0) {
618 currentConfig.EmuOpt &= ~(5<<9);
619 } else if (strcasecmp(val, "writes") == 0) {
620 currentConfig.EmuOpt &= ~(5<<9);
621 currentConfig.EmuOpt |= 1<<9;
622 } else if (strcasecmp(val, "loads") == 0) {
623 currentConfig.EmuOpt &= ~(5<<9);
624 currentConfig.EmuOpt |= 4<<9;
625 } else if (strcasecmp(val, "both") == 0) {
626 currentConfig.EmuOpt &= ~(5<<9);
627 currentConfig.EmuOpt |= 5<<9;
632 case MA_OPT_CPU_CLOCKS:
634 if (strcasecmp(var, "GP2X CPU clocks") != 0) return 0;
636 if (strcasecmp(var, "PSP CPU clock") != 0) return 0;
638 currentConfig.CPUclock = atoi(val);
642 if (strcasecmp(var, "Gamma correction") != 0) return 0;
643 currentConfig.gamma = (int) (atof(val) * 100.0);
646 case MA_OPT2_SQUIDGEHACK:
647 if (strcasecmp(var, "Squidgehack") != 0) return 0;
649 if (tmpi) *(int *)me->var |= me->mask;
650 else *(int *)me->var &= ~me->mask;
653 case MA_CDOPT_READAHEAD:
654 if (strcasecmp(var, "ReadAhead buffer") != 0) return 0;
655 PicoCDBuffers = atoi(val) / 2;
660 if (strcasecmp(var, "Scale factor") != 0) return 0;
661 currentConfig.scale = atof(val);
663 case MA_OPT3_HSCALE32:
664 if (strcasecmp(var, "Hor. scale (for low res. games)") != 0) return 0;
665 currentConfig.hscale32 = atof(val);
667 case MA_OPT3_HSCALE40:
668 if (strcasecmp(var, "Hor. scale (for hi res. games)") != 0) return 0;
669 currentConfig.hscale40 = atof(val);
671 case MA_OPT3_FILTERING:
672 if (strcasecmp(var, "Bilinear filtering") != 0) return 0;
673 currentConfig.scaling = atoi(val);
676 if (strcasecmp(var, "Gamma adjustment") != 0) return 0;
677 currentConfig.gamma = atoi(val);
679 case MA_OPT3_BLACKLVL:
680 if (strcasecmp(var, "Black level") != 0) return 0;
681 currentConfig.gamma2 = atoi(val);
684 if (strcasecmp(var, "Wait for vsync") != 0) return 0;
685 if (strcasecmp(val, "never") == 0) {
686 currentConfig.EmuOpt &= ~0x12000;
687 } else if (strcasecmp(val, "sometimes") == 0) {
688 currentConfig.EmuOpt |= 0x12000;
689 } else if (strcasecmp(val, "always") == 0) {
690 currentConfig.EmuOpt &= ~0x12000;
691 currentConfig.EmuOpt |= 0x02000;
697 lprintf("unhandled custom_read: %i\n", me->id);
703 static unsigned int keys_encountered = 0;
705 static void keys_parse(const char *var, const char *val, int binds[32],
706 const char * const names[32], int max_keys)
711 for (t = 0; t < 32; t++)
713 if (names[t] && strcmp(names[t], var) == 0) break;
717 int len = strlen(var);
718 if (len == 1) t = var[0];
719 else if (len >= 4 && var[0] == '\\' && var[1] == 'x') {
721 t = (int)strtoul(var + 2, &p, 16);
722 if (*p != 0) t = max_keys; // parse failed
725 t = max_keys; // invalid
727 if (t < 0 || t >= max_keys) {
728 lprintf("unhandled bind \"%s\"\n", var);
732 if (binds == currentConfig.KeyBinds && !(keys_encountered & (1<<t))) { // hack
734 keys_encountered |= 1<<t;
738 if (strncasecmp(val, "player", 6) == 0)
740 player = atoi(val + 6) - 1;
741 if (player > 1) goto fail;
742 for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
743 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0) {
744 binds[t] |= me_ctrl_actions[i].mask | (player<<16);
749 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
750 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
751 binds[t] |= emuctrl_actions[i].mask;
757 lprintf("unhandled action \"%s\"\n", val);
762 #define try_joy_parse(num) { \
763 if (strncasecmp(var, "bind_joy"#num " ", 10) == 0) { \
764 keys_parse(var + 10, val, currentConfig.JoyBinds[num], joyKeyNames, 32); \
769 static void parse(const char *var, const char *val)
772 int t, i, tlen, tmp, ret = 0;
774 if (strcasecmp(var, "LastUsedROM") == 0)
775 return; /* handled elsewhere */
777 if (strcasecmp(var, "Sound Volume") == 0) {
778 currentConfig.volume = atoi(val);
783 if (strncasecmp(var, "bind ", 5) == 0) {
784 keys_parse(var + 5, val, currentConfig.KeyBinds, keyNames, PLAT_MAX_KEYS);
794 for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]) && ret == 0; t++)
797 tlen = *(cfg_opt_counts[t]);
798 for (i = 0; i < tlen && ret == 0; i++, me++)
800 if (!me->need_to_save) continue;
801 if (me->name != NULL) {
802 if (strcasecmp(var, me->name) != 0) continue; // surely not this one
803 if (me->beh == MB_ONOFF) {
805 if (tmp) *(int *)me->var |= me->mask;
806 else *(int *)me->var &= ~me->mask;
808 } else if (me->beh == MB_RANGE) {
810 if (tmp < me->min) tmp = me->min;
811 if (tmp > me->max) tmp = me->max;
812 *(int *)me->var = tmp;
816 ret = custom_read(me, var, val);
819 if (!ret) lprintf("config_readsect: unhandled var: \"%s\"\n", var);
823 int config_havesect(const char *fname, const char *section)
828 f = fopen(fname, "r");
829 if (f == NULL) return 0;
831 ret = seek_sect(f, section);
836 int config_readsect(const char *fname, const char *section)
838 char line[128], *var, *val;
842 f = fopen(fname, "r");
843 if (f == NULL) return -1;
847 ret = seek_sect(f, section);
849 lprintf("config_readsect: %s: missing section [%s]\n", fname, section);
855 keys_encountered = 0;
859 ret = config_get_var_val(f, line, sizeof(line), &var, &val);
861 if (ret == -1) continue;
872 static char *mystrip(char *str)
877 for (i = 0; i < len; i++)
878 if (str[i] != ' ') break;
879 if (i > 0) memmove(str, str + i, len - i + 1);
882 for (i = len - 1; i >= 0; i--)
883 if (str[i] != ' ') break;
892 * -1 - failed to parse line
894 int config_get_var_val(void *file, char *line, int lsize, char **rvar, char **rval)
896 char *var, *val, *tmp;
900 tmp = fgets(line, lsize, f);
901 if (tmp == NULL) return 0;
903 if (line[0] == '[') return 0; // other section
905 // strip comments, linefeed, spaces..
907 for (i = 0; i < len; i++)
908 if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
911 if (len <= 0) return -1;;
914 for (i = 0; i < len; i++)
915 if (line[i] == '=') break;
916 if (i >= len || strchr(&line[i+1], '=') != NULL) {
917 lprintf("config_readsect: can't parse: %s\n", line);
927 if (strlen(var) == 0 || (strlen(val) == 0 && strncasecmp(var, "bind", 4) != 0)) {
928 lprintf("config_readsect: something's empty: \"%s\" = \"%s\"\n", var, val);