PageRenderTime 483ms CodeModel.GetById 121ms app.highlight 279ms RepoModel.GetById 73ms app.codeStats 0ms

/config.c

https://github.com/hanselpc/polipo
C | 869 lines | 772 code | 74 blank | 23 comment | 261 complexity | b538f92adb97f0a0ae8955222600d678 MD5 | raw file
  1/*
  2Copyright (c) 2003-2006 by Juliusz Chroboczek
  3
  4Permission is hereby granted, free of charge, to any person obtaining a copy
  5of this software and associated documentation files (the "Software"), to deal
  6in the Software without restriction, including without limitation the rights
  7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8copies of the Software, and to permit persons to whom the Software is
  9furnished to do so, subject to the following conditions:
 10
 11The above copyright notice and this permission notice shall be included in
 12all copies or substantial portions of the Software.
 13
 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 20THE SOFTWARE.
 21*/
 22
 23#include "polipo.h"
 24
 25ConfigVariablePtr configVariables = NULL;
 26
 27static ConfigVariablePtr
 28findConfigVariable(AtomPtr name)
 29{
 30    ConfigVariablePtr var;
 31    var = configVariables;
 32    while(var != NULL) {
 33        if(var->name == name)
 34            break;
 35        var = var->next;
 36    }
 37    return var;
 38}
 39
 40void
 41declareConfigVariable(AtomPtr name, int type, void *value, 
 42                      int (*setter)(ConfigVariablePtr, void*), char *help)
 43{
 44    ConfigVariablePtr var, previous, next;
 45
 46    var = findConfigVariable(name);
 47
 48    if(var) {
 49        do_log(L_ERROR, 
 50               "Configuration variable %s declared multiple times.\n",
 51               name->string);
 52        if(var->type != type) {
 53            exit(1);
 54        }
 55    }
 56
 57    var = malloc(sizeof(ConfigVariableRec));
 58    if(var == NULL) {
 59        do_log(L_ERROR, "Couldn't allocate config variable.\n");
 60        exit(1);
 61    }
 62
 63    var->name = retainAtom(name);
 64    var->type = type;
 65    switch(type) {
 66    case CONFIG_INT: case CONFIG_OCTAL: case CONFIG_HEX: case CONFIG_TIME:
 67    case CONFIG_BOOLEAN: case CONFIG_TRISTATE: case CONFIG_TETRASTATE:
 68    case CONFIG_PENTASTATE:
 69        var->value.i = value; break;
 70    case CONFIG_FLOAT: var->value.f = value; break;
 71    case CONFIG_ATOM: case CONFIG_ATOM_LOWER: case CONFIG_PASSWORD:
 72        var->value.a = value; break;
 73    case CONFIG_INT_LIST:
 74        var->value.il = value; break;
 75    case CONFIG_ATOM_LIST: case CONFIG_ATOM_LIST_LOWER: 
 76        var->value.al = value; break;
 77    default: abort();
 78    }
 79    var->setter = setter;
 80    var->help = help;
 81
 82    previous = NULL;
 83    next = configVariables;
 84    while(next && strcmp(next->name->string, var->name->string) < 0) {
 85        previous = next;
 86        next = next->next;
 87    }
 88    if(next && strcmp(next->name->string, var->name->string) == 0) {
 89        do_log(L_ERROR, "Variable %s declared multiple times.\n",
 90               next->name->string);
 91        abort();
 92    }
 93    if(previous == NULL) {
 94        var->next = configVariables;
 95        configVariables = var;
 96    } else {
 97        var->next = next;
 98        previous->next = var;
 99    }
100}
101
102static void
103printString(FILE *out, char *string, int html)
104{
105    if(html) {
106        char buf[512];
107        int i;
108        i = htmlString(buf, 0, 512, string, strlen(string));
109        if(i < 0) {
110            fprintf(out, "(overflow)");
111            return;
112        }
113        fwrite(buf, 1, i, out);
114    } else {
115        fprintf(out, "%s", string);
116    }
117}
118
119static void
120printVariable(FILE *out, ConfigVariablePtr var, int html, int parseable)
121{
122    int i;
123
124    switch(var->type) {
125    case CONFIG_INT: fprintf(out, "%d", *var->value.i); break;
126    case CONFIG_OCTAL: fprintf(out, "0%o", *var->value.i); break;
127    case CONFIG_HEX: fprintf(out, "0x%x", *var->value.i); break;
128    case CONFIG_TIME:
129        {
130            int v = *var->value.i;
131            if(v == 0) {
132                fprintf(out, "0s");
133            } else {
134                if(v >= 3600 * 24) fprintf(out, "%dd", v/(3600*24));
135                v = v % (3600 * 24);
136                if(v >= 3600) fprintf(out, "%dh", v / 3600);
137                v = v % 3600;
138                if(v >= 60) fprintf(out, "%dm", v / 60);
139                v = v % 60;
140                if(v > 0) fprintf(out, "%ds", v);
141            }
142        }
143        break;
144    case CONFIG_BOOLEAN:
145        switch(*var->value.i) {
146        case 0: fprintf(out, "false"); break;
147        case 1: fprintf(out, "true"); break;
148        default: fprintf(out, "???"); break;
149        }
150        break;
151    case CONFIG_TRISTATE:
152        switch(*var->value.i) {
153        case 0: fprintf(out, "false"); break;
154        case 1: fprintf(out, "maybe"); break;
155        case 2: fprintf(out, "true"); break;
156        default: fprintf(out, "???"); break;
157        }
158        break;
159    case CONFIG_TETRASTATE:
160        switch(*var->value.i) {
161        case 0: fprintf(out, "false"); break;
162        case 1: fprintf(out, "reluctantly"); break;
163        case 2: fprintf(out, "happily"); break;
164        case 3: fprintf(out, "true"); break;
165        default: fprintf(out, "???"); break;
166        }
167        break;
168    case CONFIG_PENTASTATE:
169        switch(*var->value.i) {
170        case 0: fprintf(out, "no"); break;
171        case 1: fprintf(out, "reluctantly"); break;
172        case 2: fprintf(out, "maybe"); break;
173        case 3: fprintf(out, "happily"); break;
174        case 4: fprintf(out, "true"); break;
175        default: fprintf(out, "???"); break;
176        }
177        break;
178    case CONFIG_FLOAT: fprintf(out, "%f", *var->value.f); break;
179    case CONFIG_ATOM: case CONFIG_ATOM_LOWER:
180        if(*var->value.a) {
181            if((*var->value.a)->length > 0) {
182                printString(out, (*var->value.a)->string, html);
183            } else {
184                if(!parseable)
185                    fprintf(out, "(empty)");
186            }
187        } else {
188            if(!parseable)
189                fprintf(out, "(none)");
190        }
191        break;
192    case CONFIG_PASSWORD:
193        if(!parseable)
194            fprintf(out, "(hidden)");
195        break;
196    case CONFIG_INT_LIST:
197        if((*var->value.il) == NULL) {
198            if(!parseable)
199                fprintf(out, "(not set)");
200        } else if((*var->value.il)->length == 0) {
201            if(!parseable)
202                fprintf(out, "(empty list)");
203        } else {
204            for(i = 0; i < (*var->value.il)->length; i++) {
205                int from = (*var->value.il)->ranges[i].from;
206                int to = (*var->value.il)->ranges[i].to;
207                assert(from <= to);
208                if(from == to)
209                    fprintf(out, "%d", from);
210                else
211                    fprintf(out, "%d-%d", from, to);
212                if(i < (*var->value.il)->length - 1)
213                    fprintf(out, ", ");
214            }
215        }
216        break;
217    case CONFIG_ATOM_LIST: case CONFIG_ATOM_LIST_LOWER:
218        if((*var->value.al) == NULL) {
219            if(!parseable)
220                fprintf(out, "(not set)");
221        } else if((*var->value.al)->length == 0) {
222            if(!parseable)
223                fprintf(out, "(empty list)");
224        } else {
225            for(i = 0; i < (*var->value.al)->length; i++) {
226                AtomPtr atom = (*var->value.al)->list[i];
227                if(atom) {
228                    if(atom->length > 0)
229                        printString(out, atom->string, html);
230                    else {
231                        if(!parseable)
232                            fprintf(out, "(empty)");
233                    }
234                } else {
235                    if(!parseable)
236                        fprintf(out, "(none)");
237                }
238                if(i < (*var->value.al)->length - 1)
239                    fprintf(out, ", ");
240            }
241        }
242        break;
243    default: abort();
244    }
245}
246
247static void
248printVariableForm(FILE *out, ConfigVariablePtr var)
249{
250    char *disabled = "";
251    int i;
252    
253    if(disableConfiguration || !var->setter) disabled = "disabled=true";
254
255    fprintf(out, "<form method=POST action=\"config?\">");
256  
257    switch(var->type) {
258    case CONFIG_INT: case CONFIG_OCTAL: case CONFIG_HEX:
259    case CONFIG_TIME: case CONFIG_FLOAT: case CONFIG_ATOM:
260    case CONFIG_ATOM_LOWER: case CONFIG_PASSWORD:
261    case CONFIG_INT_LIST: case CONFIG_ATOM_LIST: case CONFIG_ATOM_LIST_LOWER:
262        fprintf(out, "<input value=\"");
263        printVariable(out, var, 1, 1);
264        fprintf(out, "\"%s size=14 name=%s %s>\n",
265                var->type == CONFIG_PASSWORD ? " type=password" : "",
266                var->name->string, disabled);
267        break;
268
269    case CONFIG_BOOLEAN:
270        {
271            static char *states[] = {"false", "true"};
272            
273            fprintf(out, "<select name=%s %s>", var->name->string, disabled);
274            for(i=0; i < sizeof(states) / sizeof(states[0]); i++) {
275                if(*var->value.i == i) {
276                    fprintf(out, "<option selected>%s</option>", states[i]);
277                } else {
278                    fprintf(out, "<option>%s</option>", states[i]);
279                }
280            }
281            fprintf(out, "</select>");
282            if(var->setter)
283                fprintf(out, "<input type=\"submit\" value=\"set\"\n>");
284            break;
285        }
286    
287    case CONFIG_TRISTATE:
288        {
289            static char *states[] = {"false", "maybe", "true"};
290            
291            fprintf(out, "<select name=%s %s>", var->name->string, disabled);
292            for(i=0; i < sizeof(states) / sizeof(states[0]); i++) {
293                if(*var->value.i == i) {
294                    fprintf(out, "<option selected>%s</option>", states[i]);
295                } else {
296                    fprintf(out, "<option>%s</option>", states[i]);
297                }
298            }
299            fprintf(out, "</select>");
300            if(var->setter)
301                fprintf(out, "<input type=\"submit\" value=\"set\"\n>");
302            break;
303        }
304
305    case CONFIG_TETRASTATE:
306        {
307            static char *states[] =
308                {"false", "reluctantly", "happily", "true"};
309            
310            fprintf(out, "<select name=%s %s>", var->name->string, disabled);
311            for(i=0; i <sizeof(states) / sizeof(states[0]); i++) {
312                if(*var->value.i == i) {
313                    fprintf(out, "<option selected>%s</option>", states[i]);
314                } else {
315                    fprintf(out, "<option>%s</option>", states[i]);
316                }
317            }
318            fprintf(out, "</select>");
319            if(var->setter)
320                fprintf(out, "<input type=\"submit\" value=\"set\"\n>");
321            break;
322        }
323
324    case CONFIG_PENTASTATE:
325        {
326            static char *states[] =
327                {"no", "reluctantly", "maybe", "happily", "true"};
328
329            fprintf(out, "<select name=%s %s>", var->name->string, disabled);
330            for(i=0; i < sizeof(states) / sizeof(states[0]); i++) {
331                if(*var->value.i == i) {
332                    fprintf(out, "<option selected>%s</option>", states[i]);
333                } else {
334                    fprintf(out, "<option>%s</option>", states[i]);
335                }
336            }
337            fprintf(out, "</select>");
338            if(var->setter)
339                fprintf(out,"<input type=\"submit\" value=\"set\"\n>");
340            break;
341        }
342    default: abort();
343    }
344    fprintf(out, "</form>");
345}
346
347
348
349
350
351void
352printConfigVariables(FILE *out, int html)
353{
354    ConfigVariablePtr var;
355    int entryno = 0;
356
357#define PRINT_SEP() \
358    do {if(html) fprintf(out, "</td><td>"); else fprintf(out, " ");} while(0)
359
360    if(html) {
361        fprintf(out, "<table>\n");
362        fprintf(out, "<tbody>\n");
363    }
364
365    if(html) {
366        alternatingHttpStyle(out, "configlist");
367        fprintf(out,
368                "<table id=configlist>\n"
369                "<thead>\n"
370                "<tr><th>variable name</th>"
371                "<th>current value</th>"
372                "<th>new value</th>"
373                "<th>description</th>\n"
374                "</thead><tbody>\n"
375);
376    }
377
378    /* configFile is not a config variable, for obvious bootstrapping reasons.
379       CHUNK_SIZE is hardwired for now. */
380
381    fprintf(out,
382	    html ?
383	    "<tr class=\"even\"><td>configFile</td><td>%s</td><td></td><td>"
384	    "Configuration file.</td></tr>\n" :
385	    "configFile %s Configuration file.\n",
386	    configFile && configFile->length > 0 ?
387	    configFile->string : "(none)");
388    fprintf(out,
389	    html ?
390	    "<tr class=\"odd\"><td>CHUNK_SIZE</td><td>%d</td><td></td><td>"
391	    "Unit of chunk memory allocation.</td></tr>\n" :
392	    "CHUNK_SIZE %d Unit of chunk memory allocation.\n", CHUNK_SIZE);
393    
394    var = configVariables;
395    while(var != NULL) {
396      if(html) {
397          if(entryno % 2)
398              fprintf(out, "<tr class=odd>");
399          else
400              fprintf(out, "<tr class=even>");
401          fprintf(out, "<td>");
402      }
403
404      fprintf(out, "%s", var->name->string);
405
406      fprintf(out, html ? "<br/>" : " "); 
407      
408      fprintf(out, html ? "<i>" : "");    
409      
410      switch(var->type) {
411      case CONFIG_INT: case CONFIG_OCTAL: case CONFIG_HEX:
412	  fprintf(out, "integer"); break;
413      case CONFIG_TIME: fprintf(out, "time"); break;
414      case CONFIG_BOOLEAN: fprintf(out, "boolean"); break;
415      case CONFIG_TRISTATE: fprintf(out, "tristate"); break;
416      case CONFIG_TETRASTATE: fprintf(out, "4-state"); break;
417      case CONFIG_PENTASTATE: fprintf(out, "5-state"); break;
418      case CONFIG_FLOAT: fprintf(out, "float"); break;
419      case CONFIG_ATOM: case CONFIG_ATOM_LOWER: case CONFIG_PASSWORD:
420          fprintf(out, "atom"); break;
421      case CONFIG_INT_LIST: fprintf(out, "intlist"); break;
422      case CONFIG_ATOM_LIST: case CONFIG_ATOM_LIST_LOWER:
423	  fprintf(out, "list"); break;
424      default: abort();
425      }
426        
427      fprintf(out, html ? "</i>" : "");
428
429      PRINT_SEP();
430
431      printVariable(out, var, html, 0);
432
433      PRINT_SEP();
434	
435      if(html) {
436	printVariableForm(out, var);
437	PRINT_SEP();
438      }
439
440      fprintf(out, "%s", var->help?var->help:"");
441      if(html)
442	fprintf(out, "</td></tr>\n");
443      else
444	fprintf(out, "\n");
445
446      entryno++;
447      var = var->next;
448    }
449    if(html) {
450        fprintf(out, "</tbody>\n");
451        fprintf(out, "</table>\n");
452    }
453    return;
454#undef PRINT_SEP
455}
456
457static int
458skipWhitespace(char *buf, int i)
459{
460    while(buf[i] == ' ' || buf[i] == '\t' || buf[i] == '\r')
461        i++;
462    return i;
463}
464
465static int
466parseInt(char *buf, int offset, int *value_return)
467{
468    char *p;
469    int value;
470
471    value = strtol(buf + offset, &p, 0);
472    if(p <= buf + offset)
473        return -1;
474
475    *value_return = value;
476    return p - buf;
477}
478
479static struct config_state { char *name; int value; }
480states[] = 
481    { { "false", 0 }, 
482      { "no", 0 },
483      { "reluctantly", 1 },
484      { "seldom", 1 },
485      { "rarely", 1 },
486      { "lazily", 1 },
487      { "maybe", 2 },
488      { "perhaps", 2 },
489      { "happily", 3 },
490      { "often", 3 },
491      { "eagerly", 3 },
492      { "true", 4 },
493      { "yes", 4 } };
494
495static int
496parseState(char *buf, int offset, int kind)
497{
498    int i = offset;
499    int n;
500    int state = -1;
501
502    while(letter(buf[i]))
503        i++;
504    for(n = 0; n < sizeof(states) / sizeof(states[0]); n++) {
505        if(strlen(states[n].name) == i - offset &&
506           lwrcmp(buf + offset, states[n].name, i - offset) == 0) {
507            state = states[n].value;
508            break;
509        }
510    }
511    if(state < 0)
512        return -1;
513
514    switch(kind) {
515    case CONFIG_BOOLEAN:
516        if(state == 0) return 0;
517        else if(state == 4) return 1;
518        else return -1;
519        break;
520    case CONFIG_TRISTATE:
521        if(state == 0) return 0;
522        else if(state == 2) return 1;
523        else if(state == 4) return 2;
524        else return -1;
525        break;
526    case CONFIG_TETRASTATE:
527        if(state == 0) return 0;
528        else if(state == 1) return 1;
529        else if(state == 3) return 2;
530        else if(state == 4) return 3;
531        else return -1;
532        break;
533    case CONFIG_PENTASTATE:
534        return state;
535        break;
536    default:
537        abort();
538    }
539}
540
541static int
542parseAtom(char *buf, int offset, AtomPtr *value_return, int insensitive)
543{
544    int y0, i, j, k;
545    AtomPtr atom;
546    int escape = 0;
547    char *s;
548
549    i = offset;
550    if(buf[i] == '\"') {
551        i++;
552        y0 = i;
553        while(buf[i] != '\"' && buf[i] != '\n' && buf[i] != '\0') {
554            if(buf[i] == '\\' && buf[i + 1] != '\0') {
555                escape = 1;
556                i += 2;
557            } else
558                i++;
559        }
560        if(buf[i] != '\"')
561            return -1;
562        j = i + 1;
563    } else {
564        y0 = i;
565        while(letter(buf[i]) || digit(buf[i]) || 
566              buf[i] == '_' || buf[i] == '-' || buf[i] == '~' ||
567              buf[i] == '.' || buf[i] == ':' || buf[i] == '/')
568            i++;
569        j = i;
570    }
571
572    if(escape) {
573        s = malloc(i - y0);
574        if(s == NULL) return -1;
575        k = 0;
576        j = y0;
577        while(j < i) {
578            if(buf[j] == '\\' && j <= i - 2) {
579                s[k++] = buf[j + 1];
580                j += 2;
581            } else
582                s[k++] = buf[j++];
583        }
584        if(insensitive)
585            atom = internAtomLowerN(s, k);
586        else
587            atom = internAtomN(s, k);
588        free(s);
589        j++;
590    } else {
591        if(insensitive)
592            atom = internAtomLowerN(buf + y0, i - y0);
593        else
594            atom = internAtomN(buf + y0, i - y0);
595    }
596    *value_return = atom;
597    return j;
598}
599
600static int
601parseTime(char *line, int i, int *value_return)
602{
603    int v = 0, w;
604    while(1) {
605        if(!digit(line[i]))
606            break;
607        w = atoi(line + i);
608        while(digit(line[i])) i++;
609        switch(line[i]) {
610        case 'd': v += w * 24 * 3600; i++; break;
611        case 'h': v += w * 3600; i++; break;
612        case 'm': v += w * 60; i++; break;
613        case 's': v += w; i++; break;
614        default: v += w; goto done;
615        }
616    }
617 done:
618    *value_return = v;
619    return i;
620}
621
622int
623parseConfigLine(char *line, char *filename, int lineno, int set)
624{
625    int x0, x1;
626    int i, from, to;
627    AtomPtr name, value;
628    ConfigVariablePtr var;
629    int iv;
630    float fv;
631    AtomPtr av;
632    AtomListPtr alv;
633    IntListPtr ilv;
634
635    i = skipWhitespace(line, 0);
636    if(line[i] == '\n' || line[i] == '\0' || line[i] == '#')
637        return 0;
638
639    x0 = i;
640    while(letter(line[i]) || digit(line[i]))
641        i++;
642    x1 = i;
643
644    i = skipWhitespace(line, i);
645    if(line[i] != '=') {
646        goto syntax;
647    }
648    i++;
649    i = skipWhitespace(line, i);
650
651    name = internAtomN(line + x0, x1 - x0);
652    var = findConfigVariable(name);
653    releaseAtom(name);
654
655    if(set && var->setter == NULL)
656        return -2;
657 
658    if(var == NULL) {
659        if(!set) {
660            do_log(L_ERROR, "%s:%d: unknown config variable ",
661                   filename, lineno);
662            do_log_n(L_ERROR, line + x0, x1 - x0);
663            do_log(L_ERROR, "\n");
664        }
665        return -1;
666    }
667    
668    i = skipWhitespace(line, i);
669    switch(var->type) {
670    case CONFIG_INT: case CONFIG_OCTAL: case CONFIG_HEX:
671        i = parseInt(line, i, &iv);
672        if(i < 0) goto syntax;
673        if(set)
674            var->setter(var, &iv);
675        else
676            *var->value.i = iv;
677    break;
678    case CONFIG_TIME:
679        i = parseTime(line, i, &iv);
680        if(i < 0) goto syntax;
681        i = skipWhitespace(line, i);
682        if(line[i] != '\n' && line[i] != '\0' && line[i] != '#')
683            goto syntax;
684        if(set)
685            var->setter(var, &iv);
686        else
687            *var->value.i = iv;
688        break;
689    case CONFIG_BOOLEAN:
690    case CONFIG_TRISTATE:
691    case CONFIG_TETRASTATE:
692    case CONFIG_PENTASTATE:
693        iv = parseState(line, i, var->type);
694        if(iv < 0)
695            goto syntax;
696        if(set)
697            var->setter(var, &iv);
698        else
699            *var->value.i = iv;
700        break;
701    case CONFIG_FLOAT: 
702        if(!digit(line[i]) && line[i] != '.')
703            goto syntax;
704        fv = atof(line + i);
705        if(set)
706            var->setter(var, &fv);
707        else
708            *var->value.f = fv;
709        break;
710    case CONFIG_ATOM: case CONFIG_ATOM_LOWER: case CONFIG_PASSWORD:
711        i = parseAtom(line, i, &av, (var->type == CONFIG_ATOM_LOWER));
712        if(i < 0) goto syntax;
713        if(!av) {
714            if(!set)
715                do_log(L_ERROR, "%s:%d: couldn't allocate atom.\n",
716                       filename, lineno);
717            return -1;
718        }
719        i = skipWhitespace(line, i);
720        if(line[i] != '\n' && line[i] != '\0' && line[i] != '#') {
721            releaseAtom(av);
722            goto syntax;
723        }
724        if(set)
725            var->setter(var, &av);
726        else {
727            if(*var->value.a) releaseAtom(*var->value.a);
728            *var->value.a = av;
729        }
730        break;
731    case CONFIG_INT_LIST:
732        ilv = makeIntList(0);
733        if(ilv == NULL) {
734            if(!set)
735                do_log(L_ERROR, "%s:%d: couldn't allocate int list.\n",
736                       filename, lineno);
737            return -1;
738        }
739        while(1) {
740            i = parseInt(line, i, &from);
741            if(i < 0) goto syntax;
742            to = from;
743            i = skipWhitespace(line, i);
744            if(line[i] == '-') {
745                i = skipWhitespace(line, i + 1);
746                i = parseInt(line, i, &to);
747                if(i < 0) {
748                    destroyIntList(ilv);
749                    goto syntax;
750                }
751                i = skipWhitespace(line, i);
752            }
753            intListCons(from, to, ilv);
754            if(line[i] == '\n' || line[i] == '\0' || line[i] == '#')
755                break;
756            if(line[i] != ',') {
757                destroyIntList(ilv);
758                goto syntax;
759            }
760            i = skipWhitespace(line, i + 1);
761        }
762        if(set)
763            var->setter(var, &ilv);
764        else {
765            if(*var->value.il) destroyIntList(*var->value.il);
766            *var->value.il = ilv;
767        }
768        break;
769    case CONFIG_ATOM_LIST: case CONFIG_ATOM_LIST_LOWER:
770        alv = makeAtomList(NULL, 0);
771        if(alv == NULL) {
772            if(!set)
773                do_log(L_ERROR, "%s:%d: couldn't allocate atom list.\n",
774                       filename, lineno);
775            return -1;
776        }
777        while(1) {
778            i = parseAtom(line, i, &value, 
779                          (var->type == CONFIG_ATOM_LIST_LOWER));
780            if(i < 0) goto syntax;
781            if(!value) {
782                if(!set)
783                    do_log(L_ERROR, "%s:%d: couldn't allocate atom.\n",
784                           filename, lineno);
785                return -1;
786            }
787            atomListCons(value, alv);
788            i = skipWhitespace(line, i);
789            if(line[i] == '\n' || line[i] == '\0' || line[i] == '#')
790                break;
791            if(line[i] != ',') {
792                destroyAtomList(alv);
793                goto syntax;
794            }
795            i = skipWhitespace(line, i + 1);
796        }
797        if(set)
798            var->setter(var, &alv);
799        else {
800            if(*var->value.al) destroyAtomList(*var->value.al);
801            *var->value.al = alv;
802        }
803        break;
804    default: abort();
805    }
806    return 1;
807
808 syntax:
809    if(!set)
810        do_log(L_ERROR, "%s:%d: parse error.\n", filename, lineno);
811    return -1;
812}
813
814int
815parseConfigFile(AtomPtr filename)
816{
817    char buf[512];
818    int lineno;
819    FILE *f;
820
821    if(!filename || filename->length == 0)
822        return 0;
823    f = fopen(filename->string, "r");
824    if(f == NULL) {
825        do_log_error(L_ERROR, errno, "Couldn't open config file %s",
826                     filename->string);
827        return -1;
828    }
829
830    lineno = 1;
831    while(1) {
832        char *s;
833        s = fgets(buf, 512, f);
834        if(s == NULL) {
835            fclose(f);
836            return 1;
837        }
838        parseConfigLine(buf, filename->string, lineno, 0);
839        lineno++;
840    }
841}
842
843int
844configIntSetter(ConfigVariablePtr var, void* value)
845{
846    assert(var->type <= CONFIG_PENTASTATE);
847    *var->value.i = *(int*)value;
848    return 1;
849}
850
851int
852configFloatSetter(ConfigVariablePtr var, void* value)
853{
854    assert(var->type == CONFIG_FLOAT);
855    *var->value.i = *(float*)value;
856    return 1;
857}
858
859
860int
861configAtomSetter(ConfigVariablePtr var, void* value)
862{
863    assert(var->type == CONFIG_ATOM || var->type == CONFIG_ATOM_LOWER ||
864           var->type == CONFIG_PASSWORD);
865    if(*var->value.a)
866        releaseAtom(*var->value.a);
867    *var->value.a = *(AtomPtr*)value;
868    return 1;
869}