2 Copyright (c) 2003-2006 by Juliusz Chroboczek
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 ConfigVariablePtr configVariables
= NULL
;
27 static ConfigVariablePtr
28 findConfigVariable(AtomPtr name
)
30 ConfigVariablePtr var
;
31 var
= configVariables
;
41 declareConfigVariable(AtomPtr name
, int type
, void *value
,
42 int (*setter
)(ConfigVariablePtr
, void*), char *help
)
44 ConfigVariablePtr var
, previous
, next
;
46 var
= findConfigVariable(name
);
50 "Configuration variable %s declared multiple times.\n",
52 if(var
->type
!= type
) {
57 var
= malloc(sizeof(ConfigVariableRec
));
59 do_log(L_ERROR
, "Couldn't allocate config variable.\n");
63 var
->name
= retainAtom(name
);
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;
74 var
->value
.il
= value
; break;
75 case CONFIG_ATOM_LIST
: case CONFIG_ATOM_LIST_LOWER
:
76 var
->value
.al
= value
; break;
83 next
= configVariables
;
84 while(next
&& strcmp(next
->name
->string
, var
->name
->string
) < 0) {
88 if(next
&& strcmp(next
->name
->string
, var
->name
->string
) == 0) {
89 do_log(L_ERROR
, "Variable %s declared multiple times.\n",
93 if(previous
== NULL
) {
94 var
->next
= configVariables
;
95 configVariables
= var
;
103 printString(FILE *out
, char *string
, int html
)
108 i
= htmlString(buf
, 0, 512, string
, strlen(string
));
110 fprintf(out
, "(overflow)");
113 fwrite(buf
, 1, i
, out
);
115 fprintf(out
, "%s", string
);
120 printVariable(FILE *out
, ConfigVariablePtr var
, int html
, int parseable
)
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;
130 int v
= *var
->value
.i
;
134 if(v
>= 3600 * 24) fprintf(out
, "%dd", v
/(3600*24));
136 if(v
>= 3600) fprintf(out
, "%dh", v
/ 3600);
138 if(v
>= 60) fprintf(out
, "%dm", v
/ 60);
140 if(v
> 0) fprintf(out
, "%ds", v
);
145 switch(*var
->value
.i
) {
146 case 0: fprintf(out
, "false"); break;
147 case 1: fprintf(out
, "true"); break;
148 default: fprintf(out
, "???"); 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;
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;
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;
178 case CONFIG_FLOAT
: fprintf(out
, "%f", *var
->value
.f
); break;
179 case CONFIG_ATOM
: case CONFIG_ATOM_LOWER
:
181 if((*var
->value
.a
)->length
> 0) {
182 printString(out
, (*var
->value
.a
)->string
, html
);
185 fprintf(out
, "(empty)");
189 fprintf(out
, "(none)");
192 case CONFIG_PASSWORD
:
194 fprintf(out
, "(hidden)");
196 case CONFIG_INT_LIST
:
197 if((*var
->value
.il
) == NULL
) {
199 fprintf(out
, "(not set)");
200 } else if((*var
->value
.il
)->length
== 0) {
202 fprintf(out
, "(empty list)");
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
;
209 fprintf(out
, "%d", from
);
211 fprintf(out
, "%d-%d", from
, to
);
212 if(i
< (*var
->value
.il
)->length
- 1)
217 case CONFIG_ATOM_LIST
: case CONFIG_ATOM_LIST_LOWER
:
218 if((*var
->value
.al
) == NULL
) {
220 fprintf(out
, "(not set)");
221 } else if((*var
->value
.al
)->length
== 0) {
223 fprintf(out
, "(empty list)");
225 for(i
= 0; i
< (*var
->value
.al
)->length
; i
++) {
226 AtomPtr atom
= (*var
->value
.al
)->list
[i
];
229 printString(out
, atom
->string
, html
);
232 fprintf(out
, "(empty)");
236 fprintf(out
, "(none)");
238 if(i
< (*var
->value
.al
)->length
- 1)
248 printVariableForm(FILE *out
, ConfigVariablePtr var
)
253 if(disableConfiguration
|| !var
->setter
) disabled
= "disabled=true";
255 fprintf(out
, "<form method=POST action=\"config?\">");
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
);
271 static char *states
[] = {"false", "true"};
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
]);
278 fprintf(out
, "<option>%s</option>", states
[i
]);
281 fprintf(out
, "</select>");
283 fprintf(out
, "<input type=\"submit\" value=\"set\"\n>");
287 case CONFIG_TRISTATE
:
289 static char *states
[] = {"false", "maybe", "true"};
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
]);
296 fprintf(out
, "<option>%s</option>", states
[i
]);
299 fprintf(out
, "</select>");
301 fprintf(out
, "<input type=\"submit\" value=\"set\"\n>");
305 case CONFIG_TETRASTATE
:
307 static char *states
[] =
308 {"false", "reluctantly", "happily", "true"};
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
]);
315 fprintf(out
, "<option>%s</option>", states
[i
]);
318 fprintf(out
, "</select>");
320 fprintf(out
, "<input type=\"submit\" value=\"set\"\n>");
324 case CONFIG_PENTASTATE
:
326 static char *states
[] =
327 {"no", "reluctantly", "maybe", "happily", "true"};
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
]);
334 fprintf(out
, "<option>%s</option>", states
[i
]);
337 fprintf(out
, "</select>");
339 fprintf(out
,"<input type=\"submit\" value=\"set\"\n>");
344 fprintf(out
, "</form>");
352 printConfigVariables(FILE *out
, int html
)
354 ConfigVariablePtr var
;
357 #define PRINT_SEP() \
358 do {if(html) fprintf(out, "</td><td>"); else fprintf(out, " ");} while(0)
361 fprintf(out
, "<table>\n");
362 fprintf(out
, "<tbody>\n");
366 alternatingHttpStyle(out
, "configlist");
368 "<table id=configlist>\n"
370 "<tr><th>variable name</th>"
371 "<th>current value</th>"
373 "<th>description</th>\n"
378 /* configFile is not a config variable, for obvious bootstrapping reasons.
379 CHUNK_SIZE is hardwired for now. */
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)");
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
);
394 var
= configVariables
;
398 fprintf(out
, "<tr class=odd>");
400 fprintf(out
, "<tr class=even>");
401 fprintf(out
, "<td>");
404 fprintf(out
, "%s", var
->name
->string
);
406 fprintf(out
, html
? "<br/>" : " ");
408 fprintf(out
, html
? "<i>" : "");
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;
427 fprintf(out
, html
? "</i>" : "");
431 printVariable(out
, var
, html
, 0);
436 printVariableForm(out
, var
);
440 fprintf(out
, "%s", var
->help
?var
->help
:"");
442 fprintf(out
, "</td></tr>\n");
450 fprintf(out
, "</tbody>\n");
451 fprintf(out
, "</table>\n");
458 skipWhitespace(char *buf
, int i
)
460 while(buf
[i
] == ' ' || buf
[i
] == '\t' || buf
[i
] == '\r')
465 static struct config_state
{ char *name
; int value
; }
469 { "reluctantly", 1 },
482 parseState(char *buf
, int offset
, int kind
)
488 while(letter(buf
[i
]))
490 for(n
= 0; n
< sizeof(states
) / sizeof(states
[0]); n
++) {
491 if(strlen(states
[n
].name
) == i
- offset
&&
492 lwrcmp(buf
+ offset
, states
[n
].name
, i
- offset
) == 0) {
493 state
= states
[n
].value
;
502 if(state
== 0) return 0;
503 else if(state
== 4) return 1;
506 case CONFIG_TRISTATE
:
507 if(state
== 0) return 0;
508 else if(state
== 2) return 1;
509 else if(state
== 4) return 2;
512 case CONFIG_TETRASTATE
:
513 if(state
== 0) return 0;
514 else if(state
== 1) return 1;
515 else if(state
== 3) return 2;
516 else if(state
== 4) return 3;
519 case CONFIG_PENTASTATE
:
528 parseAtom(char *buf
, int offset
, AtomPtr
*value_return
, int insensitive
)
539 while(buf
[i
] != '\"' && buf
[i
] != '\n' && buf
[i
] != '\0') {
540 if(buf
[i
] == '\\' && buf
[i
+ 1] != '\0') {
551 while(letter(buf
[i
]) || digit(buf
[i
]) ||
552 buf
[i
] == '_' || buf
[i
] == '-' || buf
[i
] == '~' ||
553 buf
[i
] == '.' || buf
[i
] == ':' || buf
[i
] == '/')
560 if(buf
== NULL
) return -1;
564 if(buf
[j
] == '\\' && j
<= i
- 2) {
571 atom
= internAtomLowerN(s
, k
);
573 atom
= internAtomN(s
, k
);
578 atom
= internAtomLowerN(buf
+ y0
, i
- y0
);
580 atom
= internAtomN(buf
+ y0
, i
- y0
);
582 *value_return
= atom
;
587 parseTime(char *line
, int i
, int *value_return
)
594 while(digit(line
[i
])) i
++;
596 case 'd': v
+= w
* 24 * 3600; i
++; break;
597 case 'h': v
+= w
* 3600; i
++; break;
598 case 'm': v
+= w
* 60; i
++; break;
599 case 's': v
+= w
; i
++; break;
600 default: v
+= w
; goto done
;
609 parseConfigLine(char *line
, char *filename
, int lineno
, int set
)
614 ConfigVariablePtr var
;
621 i
= skipWhitespace(line
, 0);
622 if(line
[i
] == '\n' || line
[i
] == '\0' || line
[i
] == '#')
626 while(letter(line
[i
]) || digit(line
[i
]))
630 i
= skipWhitespace(line
, i
);
635 i
= skipWhitespace(line
, i
);
637 name
= internAtomN(line
+ x0
, x1
- x0
);
638 var
= findConfigVariable(name
);
641 if(set
&& var
->setter
== NULL
)
646 do_log(L_ERROR
, "%s:%d: unknown config variable ",
648 do_log_n(L_ERROR
, line
+ x0
, x1
- x0
);
649 do_log(L_ERROR
, "\n");
654 i
= skipWhitespace(line
, i
);
656 case CONFIG_INT
: case CONFIG_OCTAL
: case CONFIG_HEX
:
657 i
= parseInt(line
, i
, INT_MIN
, INT_MAX
, 0, &iv
);
658 if(i
< 0) goto syntax
;
660 var
->setter(var
, &iv
);
665 i
= parseTime(line
, i
, &iv
);
666 if(i
< 0) goto syntax
;
667 i
= skipWhitespace(line
, i
);
668 if(line
[i
] != '\n' && line
[i
] != '\0' && line
[i
] != '#')
671 var
->setter(var
, &iv
);
676 case CONFIG_TRISTATE
:
677 case CONFIG_TETRASTATE
:
678 case CONFIG_PENTASTATE
:
679 iv
= parseState(line
, i
, var
->type
);
683 var
->setter(var
, &iv
);
688 if(!digit(line
[i
]) && line
[i
] != '.')
692 var
->setter(var
, &fv
);
696 case CONFIG_ATOM
: case CONFIG_ATOM_LOWER
: case CONFIG_PASSWORD
:
697 i
= parseAtom(line
, i
, &av
, (var
->type
== CONFIG_ATOM_LOWER
));
698 if(i
< 0) goto syntax
;
701 do_log(L_ERROR
, "%s:%d: couldn't allocate atom.\n",
705 i
= skipWhitespace(line
, i
);
706 if(line
[i
] != '\n' && line
[i
] != '\0' && line
[i
] != '#') {
711 var
->setter(var
, &av
);
713 if(*var
->value
.a
) releaseAtom(*var
->value
.a
);
717 case CONFIG_INT_LIST
:
718 ilv
= makeIntList(0);
721 do_log(L_ERROR
, "%s:%d: couldn't allocate int list.\n",
726 i
= parseInt(line
, i
, INT_MIN
, INT_MAX
, 0, &from
);
727 if(i
< 0) goto syntax
;
729 i
= skipWhitespace(line
, i
);
731 i
= skipWhitespace(line
, i
+ 1);
732 i
= parseInt(line
, i
, INT_MIN
, INT_MAX
, 0, &to
);
737 i
= skipWhitespace(line
, i
);
739 intListCons(from
, to
, ilv
);
740 if(line
[i
] == '\n' || line
[i
] == '\0' || line
[i
] == '#')
746 i
= skipWhitespace(line
, i
+ 1);
749 var
->setter(var
, &ilv
);
751 if(*var
->value
.il
) destroyIntList(*var
->value
.il
);
752 *var
->value
.il
= ilv
;
755 case CONFIG_ATOM_LIST
: case CONFIG_ATOM_LIST_LOWER
:
756 alv
= makeAtomList(NULL
, 0);
759 do_log(L_ERROR
, "%s:%d: couldn't allocate atom list.\n",
764 i
= parseAtom(line
, i
, &value
,
765 (var
->type
== CONFIG_ATOM_LIST_LOWER
));
766 if(i
< 0) goto syntax
;
769 do_log(L_ERROR
, "%s:%d: couldn't allocate atom.\n",
773 atomListCons(value
, alv
);
774 i
= skipWhitespace(line
, i
);
775 if(line
[i
] == '\n' || line
[i
] == '\0' || line
[i
] == '#')
778 destroyAtomList(alv
);
781 i
= skipWhitespace(line
, i
+ 1);
784 var
->setter(var
, &alv
);
786 if(*var
->value
.al
) destroyAtomList(*var
->value
.al
);
787 *var
->value
.al
= alv
;
796 do_log(L_ERROR
, "%s:%d: parse error.\n", filename
, lineno
);
801 parseConfigFile(AtomPtr filename
)
807 if(!filename
|| filename
->length
== 0)
809 f
= fopen(filename
->string
, "r");
811 do_log(L_ERROR
, "Couldn't open config file %s: %d.\n",
812 filename
->string
, errno
);
819 s
= fgets(buf
, 512, f
);
824 rc
= parseConfigLine(buf
, filename
->string
, lineno
, 0);
830 configIntSetter(ConfigVariablePtr var
, void* value
)
832 assert(var
->type
<= CONFIG_PENTASTATE
);
833 *var
->value
.i
= *(int*)value
;
838 configFloatSetter(ConfigVariablePtr var
, void* value
)
840 assert(var
->type
== CONFIG_FLOAT
);
841 *var
->value
.i
= *(float*)value
;
847 configAtomSetter(ConfigVariablePtr var
, void* value
)
849 assert(var
->type
== CONFIG_ATOM
|| var
->type
== CONFIG_ATOM_LOWER
||
850 var
->type
== CONFIG_PASSWORD
);
852 releaseAtom(*var
->value
.a
);
853 *var
->value
.a
= *(AtomPtr
*)value
;