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')
466 parseInt(char *buf
, int offset
, int *value_return
)
471 value
= strtol(buf
+ offset
, &p
, 0);
472 if(p
<= buf
+ offset
)
475 *value_return
= value
;
479 static struct config_state
{ char *name
; int value
; }
483 { "reluctantly", 1 },
496 parseState(char *buf
, int offset
, int kind
)
502 while(letter(buf
[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
;
516 if(state
== 0) return 0;
517 else if(state
== 4) return 1;
520 case CONFIG_TRISTATE
:
521 if(state
== 0) return 0;
522 else if(state
== 2) return 1;
523 else if(state
== 4) return 2;
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;
533 case CONFIG_PENTASTATE
:
542 parseAtom(char *buf
, int offset
, AtomPtr
*value_return
, int insensitive
)
553 while(buf
[i
] != '\"' && buf
[i
] != '\n' && buf
[i
] != '\0') {
554 if(buf
[i
] == '\\' && buf
[i
+ 1] != '\0') {
565 while(letter(buf
[i
]) || digit(buf
[i
]) ||
566 buf
[i
] == '_' || buf
[i
] == '-' || buf
[i
] == '~' ||
567 buf
[i
] == '.' || buf
[i
] == ':' || buf
[i
] == '/')
574 if(buf
== NULL
) return -1;
578 if(buf
[j
] == '\\' && j
<= i
- 2) {
585 atom
= internAtomLowerN(s
, k
);
587 atom
= internAtomN(s
, k
);
592 atom
= internAtomLowerN(buf
+ y0
, i
- y0
);
594 atom
= internAtomN(buf
+ y0
, i
- y0
);
596 *value_return
= atom
;
601 parseTime(char *line
, int i
, int *value_return
)
608 while(digit(line
[i
])) 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
;
623 parseConfigLine(char *line
, char *filename
, int lineno
, int set
)
628 ConfigVariablePtr var
;
635 i
= skipWhitespace(line
, 0);
636 if(line
[i
] == '\n' || line
[i
] == '\0' || line
[i
] == '#')
640 while(letter(line
[i
]) || digit(line
[i
]))
644 i
= skipWhitespace(line
, i
);
649 i
= skipWhitespace(line
, i
);
651 name
= internAtomN(line
+ x0
, x1
- x0
);
652 var
= findConfigVariable(name
);
655 if(set
&& var
->setter
== NULL
)
660 do_log(L_ERROR
, "%s:%d: unknown config variable ",
662 do_log_n(L_ERROR
, line
+ x0
, x1
- x0
);
663 do_log(L_ERROR
, "\n");
668 i
= skipWhitespace(line
, i
);
670 case CONFIG_INT
: case CONFIG_OCTAL
: case CONFIG_HEX
:
671 i
= parseInt(line
, i
, &iv
);
672 if(i
< 0) goto syntax
;
674 var
->setter(var
, &iv
);
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
] != '#')
685 var
->setter(var
, &iv
);
690 case CONFIG_TRISTATE
:
691 case CONFIG_TETRASTATE
:
692 case CONFIG_PENTASTATE
:
693 iv
= parseState(line
, i
, var
->type
);
697 var
->setter(var
, &iv
);
702 if(!digit(line
[i
]) && line
[i
] != '.')
706 var
->setter(var
, &fv
);
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
;
715 do_log(L_ERROR
, "%s:%d: couldn't allocate atom.\n",
719 i
= skipWhitespace(line
, i
);
720 if(line
[i
] != '\n' && line
[i
] != '\0' && line
[i
] != '#') {
725 var
->setter(var
, &av
);
727 if(*var
->value
.a
) releaseAtom(*var
->value
.a
);
731 case CONFIG_INT_LIST
:
732 ilv
= makeIntList(0);
735 do_log(L_ERROR
, "%s:%d: couldn't allocate int list.\n",
740 i
= parseInt(line
, i
, &from
);
741 if(i
< 0) goto syntax
;
743 i
= skipWhitespace(line
, i
);
745 i
= skipWhitespace(line
, i
+ 1);
746 i
= parseInt(line
, i
, &to
);
751 i
= skipWhitespace(line
, i
);
753 intListCons(from
, to
, ilv
);
754 if(line
[i
] == '\n' || line
[i
] == '\0' || line
[i
] == '#')
760 i
= skipWhitespace(line
, i
+ 1);
763 var
->setter(var
, &ilv
);
765 if(*var
->value
.il
) destroyIntList(*var
->value
.il
);
766 *var
->value
.il
= ilv
;
769 case CONFIG_ATOM_LIST
: case CONFIG_ATOM_LIST_LOWER
:
770 alv
= makeAtomList(NULL
, 0);
773 do_log(L_ERROR
, "%s:%d: couldn't allocate atom list.\n",
778 i
= parseAtom(line
, i
, &value
,
779 (var
->type
== CONFIG_ATOM_LIST_LOWER
));
780 if(i
< 0) goto syntax
;
783 do_log(L_ERROR
, "%s:%d: couldn't allocate atom.\n",
787 atomListCons(value
, alv
);
788 i
= skipWhitespace(line
, i
);
789 if(line
[i
] == '\n' || line
[i
] == '\0' || line
[i
] == '#')
792 destroyAtomList(alv
);
795 i
= skipWhitespace(line
, i
+ 1);
798 var
->setter(var
, &alv
);
800 if(*var
->value
.al
) destroyAtomList(*var
->value
.al
);
801 *var
->value
.al
= alv
;
810 do_log(L_ERROR
, "%s:%d: parse error.\n", filename
, lineno
);
815 parseConfigFile(AtomPtr filename
)
821 if(!filename
|| filename
->length
== 0)
823 f
= fopen(filename
->string
, "r");
825 do_log(L_ERROR
, "Couldn't open config file %s: %d.\n",
826 filename
->string
, errno
);
833 s
= fgets(buf
, 512, f
);
838 rc
= parseConfigLine(buf
, filename
->string
, lineno
, 0);
844 configIntSetter(ConfigVariablePtr var
, void* value
)
846 assert(var
->type
<= CONFIG_PENTASTATE
);
847 *var
->value
.i
= *(int*)value
;
852 configFloatSetter(ConfigVariablePtr var
, void* value
)
854 assert(var
->type
== CONFIG_FLOAT
);
855 *var
->value
.i
= *(float*)value
;
861 configAtomSetter(ConfigVariablePtr var
, void* value
)
863 assert(var
->type
== CONFIG_ATOM
|| var
->type
== CONFIG_ATOM_LOWER
||
864 var
->type
== CONFIG_PASSWORD
);
866 releaseAtom(*var
->value
.a
);
867 *var
->value
.a
= *(AtomPtr
*)value
;