1 /*** analog 6.0 http://www.analog.cx/ ***/
2 /*** This program is copyright (c) Stephen R. E. Turner 1995 - 2004 except as
3 *** stated otherwise.
4 ***
5 *** This program is free software. You can redistribute it and/or modify it
6 *** under the terms of version 2 of the GNU General Public License, which you
7 *** should have received with it.
8 ***
9 *** This program is distributed in the hope that it will be useful, but
10 *** without any warranty, expressed or implied. ***/
11
12 /*** init2.c; subsiduary functions for parsing config files */
13 /* See also init.c */
14
15 #include "anlghea3.h"
16
17 /*** All config commands have the following structure:
18 void config.....(void *opt, char *cmd, char *arg1, char *arg2, int rc)
19 opt is the variable to be changed, "cmd arg1 arg2" is the config command
20 issued, rc is the number of arguments retrieved from the config line (0..3),
21 or -1 if the config command was issued by the program internally,
22 or -2 if it was issued by the program in parsing command line (for those
23 -2's which don't go through confline(), cmd is command line option). -3 is
24 occasionally used for special cases. ***/
25
26 /* First some warnings of things that can go wrong in config commands.
27 NB rc = -1 announced to be because anlghead.h got messed up. But it
28 could be an internal error caused by a bug. */
29
shortwarn(char * cmd,char * arg1,int rc)30 void shortwarn(char *cmd, char *arg1, int rc) {
31 char *s = "Not enough arguments for configuration command: ignoring it";
32 if (rc == -1)
33 error("Default given for %s in anlghead.h too short");
34 else if (arg1 == NULL)
35 warn('C', TRUE, "%s:\n%s", s, cmd);
36 else
37 warn('C', TRUE, "%s:\n%s %s", s, cmd, delimit(arg1));
38 }
39
longwarn(char * cmd,char * arg1,char * arg2,int rc)40 void longwarn(char *cmd, char *arg1, char *arg2, int rc) {
41 char *s = "Too many arguments for configuration command: "
42 "ignoring end of line starting";
43 if (rc == -1)
44 warn('C', TRUE, "Default given for %s in anlghead.h too long: "
45 "ignoring end of it", cmd);
46 else if (arg2 == NULL)
47 warn('C', TRUE, "%s:\n%s %s", s, cmd, delimit(arg1));
48 else
49 warn('C', TRUE, "%s:\n%s %s %s", s, cmd, delimit(arg1), delimit(arg2));
50 }
51
badwarn(char * cmd,choice domess,char * arg1,char * arg2,int rc)52 void badwarn(char *cmd, choice domess, char *arg1, char *arg2, int rc) {
53 char *s = "Bad argument in configuration command: ignoring it";
54 if (rc == -2)
55 warn('C', TRUE, "Bad argument in command line argument %s: ignoring it",
56 cmd);
57 else if (rc == -1)
58 error("Incorrect default given for %s in anlghead.h", cmd);
59 else if (arg2 == NULL)
60 warn('C', domess, "%s:\n%s %s", s, cmd, delimit(arg1));
61 else
62 warn('C', domess, "%s:\n%s %s %s", s, cmd, delimit(arg1), delimit(arg2));
63 }
64
unknownwarn(char * cmd,char * arg1,char * arg2)65 void unknownwarn(char *cmd, char *arg1, char *arg2) {
66 char *s = "Unknown configuration command: ignoring it";
67 if (arg1 == NULL)
68 warn('C', TRUE, "%s:\n%s", s, cmd);
69 else if (arg2 == NULL)
70 warn('C', TRUE, "%s:\n%s %s", s, cmd, delimit(arg1));
71 else
72 warn('C', TRUE, "%s:\n%s %s %s", s, cmd, delimit(arg1), delimit(arg2));
73 }
74
configcall(void * opt,char * cmd,char * arg1,char * arg2,int rc)75 void configcall(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
76 if (rc == 0 || IS_EMPTY_STRING(arg1)) {
77 shortwarn(cmd, arg1, rc);
78 return;
79 }
80 if (rc > 1)
81 longwarn(cmd, arg1, arg2, rc);
82 /* calling function will then call config() */
83 }
84
configcols(void * opt,char * cmd,char * arg1,char * arg2,int rc)85 void configcols(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
86 extern char colcodes[];
87
88 choice *cols = (choice *)opt; /* see also configallcols() */
89
90 logical warn1 = FALSE, warn2 = FALSE, warn3;
91 char *c;
92 int i = 0, j, k;
93
94 if (rc == 0) {
95 shortwarn(cmd, arg1, rc);
96 return;
97 }
98 for (c = arg1; *c != '\0' && i < COL_NUMBER - 1; c++, i++) {
99 for (cols[i] = COL_NUMBER, k = 0;
100 cols[i] == COL_NUMBER && colcodes[k] != '\0';
101 k++) {
102 if (*c == colcodes[k])
103 cols[i] = k;
104 }
105 if (cols[i] == COL_NUMBER) {
106 i--;
107 if (!warn1) {
108 warn('C', TRUE, "Ignoring unknown columns in\n%s %s", cmd, arg1);
109 warn1 = TRUE;
110 }
111 }
112 /* check for repeated columns */
113 else if ((cols[i] == COL_DATE && strchr(arg1, 'D') != NULL) ||
114 (cols[i] == COL_FIRSTD && strchr(arg1, 'E') != NULL)) {
115 i--; /* cancel i++ in for loop, so overwriting this column */
116 if (!warn2) {
117 warn('C', TRUE,
118 "Ignoring repeated columns in configuration command\n%s %s",
119 cmd, arg1);
120 warn2 = TRUE;
121 }
122 }
123 else {
124 for (j = 0, warn3 = FALSE; j < i && !warn3; j++) {
125 if (cols[j] == cols[i]) { /* shortest code, not least work */
126 warn3 = TRUE;
127 i--; /* see above */
128 if (!warn2) {
129 warn('C', TRUE,
130 "Ignoring repeated columns in configuration command\n%s %s",
131 cmd, arg1);
132 warn2 = TRUE;
133 }
134 }
135 }
136 }
137 } /* for c */
138 if (*c != '\0' /* can this happen? */ || rc > 1)
139 longwarn(cmd, arg1, arg2, rc);
140 cols[i] = COL_NUMBER;
141 }
142
configallcols(void * opt,char * cmd,char * arg1,char * arg2,int rc)143 void configallcols(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
144 choice *cols = (choice *)opt;
145 int j;
146
147 if (rc == 0) {
148 shortwarn(cmd, arg1, rc);
149 return;
150 }
151 if (rc > 1)
152 longwarn(cmd, arg1, arg2, rc);
153 for (j = 0; j < DATEREP_NUMBER; j++) /* possibility of dup. warns */
154 configcols((void *)&(cols[j * COL_NUMBER]), cmd, arg1, NULL, -3);
155 }
156
157 /* A set of symbolic word choices for use with configchoice() below. Must all
158 end with "". */
159 Choices sortbychoices[] = {{"REQUESTS", REQUESTS}, {"REQUESTS7", REQUESTS7},
160 {"PAGES", PAGES}, {"PAGES7", PAGES7}, {"BYTES", BYTES}, {"BYTES7", BYTES7},
161 {"LASTDATE", DATESORT}, {"DATE", DATESORT}, {"FIRSTDATE", FIRSTDATE},
162 {"ALPHABETICAL", ALPHABETICAL}, {"RANDOM", RANDOM}, {"", 0}};
163 #ifndef NOGRAPHICS
164 Choices chartchoices[] = {{"ON", CHART_SORTBY}, {"TRUE", CHART_SORTBY},
165 {"SORTBY", CHART_SORTBY}, {"OFF", CHART_NONE}, {"FALSE", CHART_NONE},
166 {"NONE", CHART_NONE}, {"REQUESTS", REQUESTS}, {"REQUESTS7", REQUESTS7},
167 {"PAGES", PAGES}, {"PAGES7", PAGES7}, {"BYTES", BYTES}, {"BYTES7", BYTES7},
168 {"", 0}};
169 #endif
170 Choices outstylechoices[] = {{"HTML", HTML}, {"XHTML", XHTML},
171 {"PLAIN", PLAIN}, {"ASCII", ASCII}, {"LATEX", LATEX}, {"COMPUTER", COMPUTER},
172 {"PREFORMATTED", COMPUTER}, {"CRO", COMPUTER}, {"XML", XML},
173 {"CACHE", OUT_NONE}, {"NONE", OUT_NONE}, {"", 0}};
174 #ifndef NODNS
175 Choices dnschoices[] = {{"NONE", DNS_NONE}, {"OFF", DNS_NONE},
176 {"READ", DNS_READ}, {"LOOKUP", DNS_LOOKUP}, {"WRITE", DNS_WRITE}, {"", 0}};
177 #endif
178 Choices gotochoices[] = {{"ON", TRUE}, {"TRUE", TRUE}, {"ALL", TRUE},
179 {"OFF", FALSE}, {"FALSE", FALSE}, {"NONE", FALSE}, {"FEW", FEW},
180 {"SOME", FEW}, {"", 0}};
181 Choices casechoices[] = {{"INSENSITIVE", TRUE}, {"SENSITIVE", FALSE}, {"", 0}};
182 Choices daychoices[] = {{"SUNDAY", SUNDAY}, {"MONDAY", MONDAY},
183 {"TUESDAY", TUESDAY}, {"WEDNESDAY", WEDNESDAY}, {"THURSDAY", THURSDAY},
184 {"FRIDAY", FRIDAY}, {"SATURDAY", SATURDAY}, {"", 0}};
185 Choices langchoices[] = {{"ARMENIAN", ARMENIAN}, {"BASQUE", BASQUE},
186 {"BOSNIAN", OLDLANG},
187 {"BULGARIAN", BULGARIAN}, {"BULGARIAN-MIK", BULGARIAN_MIK},
188 {"CATALAN", CATALAN},
189 {"SIMP-CHINESE", SIMP_CHINESE}, {"CHINESE", SIMP_CHINESE},
190 {"TRAD-CHINESE", TRAD_CHINESE}, {"TAIWANESE", TRAD_CHINESE},
191 {"CROATIAN", OLDLANG}, {"CZECH", CZECH}, {"CZECH-1250", CZECH_1250},
192 {"DANISH", DANISH}, {"DUTCH", DUTCH}, {"FLEMISH", DUTCH},
193 {"ENGLISH", ENGLISH}, {"US-ENGLISH", US_ENGLISH}, {"FINNISH", FINNISH},
194 {"FRENCH", FRENCH}, {"GERMAN", GERMAN}, {"GREEK", OLDLANG},
195 {"HUNGARIAN", HUNGARIAN}, {"ICELANDIC", OLDLANG}, {"INDONESIAN", INDONESIAN},
196 {"ITALIAN", ITALIAN},
197 {"JAPANESE-EUC", JAPANESE_EUC}, {"JAPANESE-JIS", JAPANESE_JIS},
198 {"JAPANESE-SJIS", JAPANESE_SJIS}, {"JAPANESE-UTF", JAPANESE_UTF},
199 {"JAPANESE", JAPANESE_JIS}, {"KOREAN", KOREAN}, {"LATVIAN", LATVIAN},
200 {"LITHUANIAN", OLDLANG}, {"NORWEGIAN", NORWEGIAN}, {"BOKMAL", NORWEGIAN},
201 {"BOKMAAL", NORWEGIAN}, {"NYNORSK", NYNORSK}, {"POLISH", POLISH},
202 {"PORTUGUESE", PORTUGUESE}, {"PORTUGESE", PORTUGUESE},
203 {"BR-PORTUGUESE", BR_PORTUGUESE}, {"BR-PORTUGESE", BR_PORTUGUESE},
204 {"BRAZILIAN", BR_PORTUGUESE}, {"ROMANIAN", OLDLANG}, {"RUSSIAN", RUSSIAN},
205 {"RUSSIAN-1251", RUSSIAN_1251},
206 {"SERBIAN", SERBIAN}, {"SERBOCROATIAN", SERBIAN}, {"SERBOCROAT", SERBIAN},
207 {"SLOVAK", SLOVAK}, {"SLOVAK-1250", SLOVAK_1250},
208 {"SLOVENE", SLOVENE}, {"SLOVENIAN", SLOVENE},
209 {"SLOVENE-1250", SLOVENE_1250}, {"SLOVENIAN-1250", SLOVENE_1250},
210 {"SPANISH", SPANISH},
211 {"SWEDISH", SWEDISH}, {"SWEDISH-ALT", SWEDISH_ALT}, {"TURKISH", TURKISH},
212 {"UKRAINIAN", UKRAINIAN}, {"UKRANIAN", UKRAINIAN}, {"", 0}};
213 Choices onoffchoices[] = {{"ON", TRUE}, {"TRUE", TRUE}, {"OFF", FALSE},
214 {"FALSE", FALSE}, {"", 0}};
215
configchoice(void * opt,char * cmd,char * arg1,char * arg2,int rc)216 void configchoice(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
217 Choices *choices;
218 choice *ans = (choice *)opt;
219 logical *ansl = (logical *)opt;
220 char *cmdend = strchr(cmd, '\0');
221 int i;
222 logical done, islog = FALSE;
223
224 if (rc == 0) {
225 shortwarn(cmd, arg1, rc);
226 return;
227 }
228
229 if (STREQ(cmdend - 6, "SORTBY"))
230 choices = sortbychoices;
231 #ifndef NOGRAPHICS
232 else if (STREQ(cmdend - 5, "CHART"))
233 choices = chartchoices;
234 #endif
235 else if (STREQ(cmd, "OUTPUT"))
236 choices = outstylechoices;
237 #ifndef NODNS
238 else if (STREQ(cmd, "DNS"))
239 choices = dnschoices;
240 #endif
241 else if (STREQ(cmd, "LANGUAGE"))
242 choices = langchoices;
243 else if (STREQ(cmd, "GOTOS"))
244 choices = gotochoices;
245 else if (STREQ(cmdend - 4, "CASE")) {
246 choices = casechoices;
247 islog = TRUE;
248 }
249 else if (STREQ(cmd, "WEEKBEGINSON"))
250 choices = daychoices;
251 else if (STREQ(cmd, "SEARCHCHARCONVERT"))
252 choices = onoffchoices;
253 else {
254 choices = onoffchoices;
255 islog = TRUE;
256 }
257
258 if (rc > 1)
259 longwarn(cmd, arg1, arg2, rc);
260 for (i = 0, done = FALSE; !done && !IS_EMPTY_STRING(choices[i].name); i++) {
261 if (strcaseeq(arg1, choices[i].name)) {
262 if (islog)
263 *ansl = (logical)(choices[i].arg);
264 else
265 *ans = choices[i].arg;
266 /* I'm sure islog is unnecessary -- that we can safely pass a logical in
267 to this function and pretend it's a choice in here. But let's pretend
268 we're in a strongly-typed language. :-) */
269 done = TRUE;
270 }
271 }
272 if (!done)
273 badwarn(cmd, TRUE, arg1, arg2, rc);
274 }
275
276 #ifndef NOGRAPHICS
configallchart(void * opt,char * cmd,char * arg1,char * arg2,int rc)277 void configallchart(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
278 /* only takes ON and OFF to avoid invalid choices */
279 choice *q = (choice *)opt;
280 choice p = UNSET;
281 int i;
282
283 if (rc == 0) {
284 shortwarn(cmd, arg1, rc);
285 return;
286 }
287 if (rc > 1)
288 longwarn(cmd, arg1, arg2, rc);
289
290 if (strcaseeq(arg1, "ON") || strcaseeq(arg1, "TRUE") ||
291 strcaseeq(arg1, "SORTBY"))
292 p = CHART_SORTBY;
293 else if (strcaseeq(arg1, "OFF") || strcaseeq(arg1, "FALSE") ||
294 strcaseeq(arg1, "NONE"))
295 p = CHART_NONE;
296
297 if (p == UNSET)
298 badwarn(cmd, TRUE, arg1, arg2, rc);
299 else for (i = 0; i < GENREP_NUMBER; i++)
300 q[i] = p;
301 }
302 #endif /* NOGRAPHICS */
303
configdebug(void * opt,char * cmd,char * arg1,char * arg2,int rc)304 void configdebug(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
305 char **args = (char **)opt;
306 char *a, *c;
307
308 if (rc == 0) {
309 shortwarn(cmd, arg1, rc);
310 return;
311 }
312 if (rc > 1)
313 longwarn(cmd, arg1, arg2, rc);
314 if (strcaseeq(arg1, "ON") || strcaseeq(arg1, "TRUE") ||
315 strcaseeq(arg1, "ALL"))
316 configstr(args, NULL, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", NULL, -1);
317 else if (strcaseeq(arg1, "OFF") || strcaseeq(arg1, "FALSE") ||
318 strcaseeq(arg1, "NONE"))
319 configstr(args, NULL, "", NULL, -1);
320 else {
321 strtoupper(arg1);
322 if (arg1[0] == '-') {
323 while ((c = strpbrk(*args, arg1 + 1)) != NULL)
324 memmove((void *)c, (void *)(c + 1), strlen(c));
325 }
326 else if (arg1[0] == '+') { /* c.f. configstr() */
327 *args = (char *)xrealloc((void *)(*args), strlen(arg1) + strlen(*args));
328 for (a = strchr(*args, '\0'), c = arg1 + 1; *c != '\0'; c++) {
329 if (!strchr(*args, *c)) {
330 *a = *c;
331 *(++a) = '\0';
332 }
333 }
334 }
335 else
336 configstr(args, NULL, arg1, NULL, -1);
337 }
338 }
339
configall(void * opt,char * cmd,char * arg1,char * arg2,int rc)340 void configall(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
341 logical *q = (logical *)opt;
342 choice result = UNSET;
343 int i;
344
345 configchoice((void *)(&result), cmd, arg1, arg2, rc);
346 if (result != UNSET) {
347 for (i = 0; i < REP_NUMBER; i++) {
348 if (i != REP_GENSUM)
349 q[i] = result;
350 }
351 }
352 }
353
configallback(void * opt,char * cmd,char * arg1,char * arg2,int rc)354 void configallback(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
355 logical *q = (logical *)opt; /* same as configall(), but only DATEREPORTs */
356 choice result = UNSET;
357 int i;
358
359 configchoice((void *)(&result), cmd, arg1, arg2, rc);
360 if (result != UNSET) {
361 for (i = 0; i < DATEREPORTS_NUMBER; i++)
362 q[i] = (logical)result;
363 }
364 }
365
configlang(void * opt,char * cmd,char * arg1,char * arg2,int rc)366 void configlang(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
367 Lang *lang = (Lang *)opt;
368 choice code = UNSET;
369
370 configchoice((void *)(&code), cmd, arg1, arg2, rc);
371 if (rc == 0) {
372 shortwarn(cmd, arg1, rc);
373 return;
374 }
375 if (rc > 1)
376 longwarn(cmd, arg1, arg2, rc);
377 if (code == OLDLANG) {
378 badwarn(cmd, FALSE, arg1, arg2, rc);
379 warn('C', CONTINUATION, " (language not yet translated for version 5)");
380 }
381 else if (code != UNSET) { /* code == UNSET warning in configchoice() */
382 lang->code = code;
383 lang->file = NULL;
384 }
385 }
386
selectlang(char * country,Outchoices * op)387 void selectlang(char *country, Outchoices *op) {
388 /* Select localisation files with country code. If outstyle is HTML/XHTML
389 or ASCII, look first for special files for those styles. */
390 char partname[13];
391 /* The longest name we might want to build is 3 for the country code,
392 1 for h or a, 8 for "desc.txt" and terminating \0. */
393 char *filename = NULL; /* Just to keep the compiler happy */
394 FILE *f;
395
396 if (op->outstyle == HTML || op->outstyle == XHTML || op->outstyle == ASCII) {
397 sprintf(partname, "%s%c%clng", country, (op->outstyle == ASCII)?'a':'h',
398 EXTSEP);
399 filename = buildfilename(LANGDIR, "lang", partname);
400 }
401 if ((op -> outstyle != HTML && op->outstyle != XHTML &&
402 op->outstyle != ASCII) || (f = FOPENR(filename)) == NULL) {
403 sprintf(partname, "%s%clng", country, EXTSEP);
404 filename = buildfilename(LANGDIR, "lang", partname);
405 }
406 else
407 fclose(f);
408 COPYSTR(op->lang.file, filename);
409
410 if (op->domainsfile == NULL) {
411 if (op->outstyle == HTML || op->outstyle == XHTML ||
412 op->outstyle == ASCII) {
413 sprintf(partname, "%s%cdom%ctab", country,
414 (op->outstyle == ASCII)?'a':'h', EXTSEP);
415 filename = buildfilename(LANGDIR, "lang", partname);
416 }
417 if ((op->outstyle == HTML || op->outstyle == XHTML ||
418 op->outstyle == ASCII) && (f = FOPENR(filename)) != NULL)
419 fclose(f);
420 else {
421 sprintf(partname, "%sdom%ctab", country, EXTSEP);
422 filename = buildfilename(LANGDIR, "lang", partname);
423 if ((f = FOPENR(filename)) == NULL)
424 filename = NULL; /* and will get set to ukdom.tab on return */
425 else
426 fclose(f);
427 }
428 if (filename != NULL)
429 COPYSTR(op->domainsfile, filename);
430 }
431
432 if (op->descriptions && op->descfile == NULL) {
433 if (op->outstyle == HTML || op->outstyle == XHTML ||
434 op->outstyle == ASCII) {
435 sprintf(partname, "%s%cdesc%ctxt", country,
436 (op->outstyle == ASCII)?'a':'h', EXTSEP);
437 filename = buildfilename(LANGDIR, "lang", partname);
438 }
439 if ((op->outstyle == HTML || op->outstyle == XHTML ||
440 op->outstyle == ASCII) && (f = FOPENR(filename)) != NULL)
441 fclose(f);
442 else {
443 sprintf(partname, "%sdesc%ctxt", country, EXTSEP);
444 filename = buildfilename(LANGDIR, "lang", partname);
445 if ((f = FOPENR(filename)) == NULL)
446 filename = NULL;
447 else
448 fclose(f);
449 }
450 if (filename != NULL)
451 COPYSTR(op->descfile, filename);
452 }
453 }
454
configlogfmt(void * opt,char * cmd,char * arg1,char * arg2,int rc)455 void configlogfmt(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
456 extern Memman *xmemman;
457 Inputformatlist **logfmt = (Inputformatlist **)opt;
458 Inputformatlist *fp;
459 Inputformat *form;
460 choice count[INPUT_NUMBER];
461 choice rc2;
462 char code;
463 int i;
464
465 if (rc == 0) {
466 shortwarn(cmd, arg1, rc);
467 return;
468 }
469 if (rc > 1)
470 longwarn(cmd, arg1, arg2, rc);
471
472 if (strcaseeq(arg1, "DEFAULT")) {
473 (*logfmt)->used = TRUE;
474 configlogfmt(opt, NULL, "%x0", NULL, -1);
475 (*logfmt)->used = TRUE;
476 }
477 else if (strcaseeq(arg1, "AUTO")) {
478 (*logfmt)->used = TRUE;
479 configlogfmt(opt, NULL, "%x1", NULL, -1);
480 (*logfmt)->used = TRUE;
481 }
482 else if (strcaseeq(arg1, "COMMON")) {
483 configlogfmt(opt, NULL, LOG_COMMON1, NULL, -1);
484 configlogfmt(opt, NULL, LOG_COMMON2, NULL, -1);
485 configlogfmt(opt, NULL, LOG_COMMON3, NULL, -1);
486 }
487 else if (strcaseeq(arg1, "MS-COMMON")) {
488 configlogfmt(opt, NULL, LOG_MS_COMMON1, NULL, -1);
489 configlogfmt(opt, NULL, LOG_COMMON2, NULL, -1);
490 configlogfmt(opt, NULL, LOG_COMMON3, NULL, -1);
491 }
492 else if (strcaseeq(arg1, "COMBINED")) {
493 configlogfmt(opt, NULL, LOG_COMBINED1, NULL, -1);
494 configlogfmt(opt, NULL, LOG_COMBINED2, NULL, -1);
495 configlogfmt(opt, NULL, LOG_COMBINED3, NULL, -1);
496 }
497 else if (strcaseeq(arg1, "MICROSOFT-NA") ||
498 strcaseeq(arg1, "MICROSOFT-NA2") ||
499 strcaseeq(arg1, "MICROSOFT-NA4")) { /* NA2 and NA4 are legacy */
500 configlogfmt(opt, NULL, LOG_MS_NA1, NULL, -1);
501 configlogfmt(opt, NULL, LOG_MS_NA2, NULL, -1);
502 }
503 else if (strcaseeq(arg1, "MICROSOFT-INT") ||
504 strcaseeq(arg1, "MICROSOFT-INT2") ||
505 strcaseeq(arg1, "MICROSOFT-INT4")) {
506 configlogfmt(opt, NULL, LOG_MS_INT1, NULL, -1);
507 configlogfmt(opt, NULL, LOG_MS_INT2, NULL, -1);
508 }
509 else if (strcaseeq(arg1, "WEBSITE-NA"))
510 configlogfmt(opt, NULL, LOG_WEBSITE_NA, NULL, -1);
511 else if (strcaseeq(arg1, "WEBSITE-INT"))
512 configlogfmt(opt, NULL, LOG_WEBSITE_INT, NULL, -1);
513 else if (strcaseeq(arg1, "WEBSTAR")) {
514 configlogfmt(opt, NULL, LOG_WEBSTAR1, NULL, -1);
515 configlogfmt(opt, NULL, LOG_WEBSTAR2, NULL, -1);
516 }
517 else if (strcaseeq(arg1, "EXTENDED")) {
518 configlogfmt(opt, NULL, LOG_EXTENDED1, NULL, -1);
519 configlogfmt(opt, NULL, LOG_EXTENDED2, NULL, -1);
520 }
521 else if (strcaseeq(arg1, "MS-EXTENDED")) {
522 configlogfmt(opt, NULL, LOG_MS_EXTENDED1, NULL, -1);
523 configlogfmt(opt, NULL, LOG_EXTENDED2, NULL, -1);
524 }
525 else if (strcaseeq(arg1, "WEBSTAR-EXTENDED")) {
526 configlogfmt(opt, NULL, LOG_WEBSTAR_EXTENDED1, NULL, -1);
527 configlogfmt(opt, NULL, LOG_EXTENDED2, NULL, -1);
528 }
529 else if (strcaseeq(arg1, "MACHTTP"))
530 configlogfmt(opt, NULL, LOG_MACHTTP, NULL, -1);
531 else if (strcaseeq(arg1, "NETSCAPE"))
532 configlogfmt(opt, NULL, LOG_NETSCAPE, NULL, -1);
533 else if (strcaseeq(arg1, "BROWSER"))
534 configlogfmt(opt, NULL, LOG_BROWSER, NULL, -1);
535 else if (strcaseeq(arg1, "REFERRER") || strcaseeq(arg1, "REFERER")) {
536 configlogfmt(opt, NULL, LOG_REFERRER1, NULL, -1);
537 configlogfmt(opt, NULL, LOG_REFERRER2, NULL, -1);
538 }
539 else if ((rc2 = strtoinfmt(&form, arg1, count)) != FMT_OK) {
540 code = (rc == -3)?'F':'C';
541 if (rc == -3)
542 warn(code, FALSE, "Ignoring corrupt format line in logfile");
543 else
544 badwarn(cmd, FALSE, arg1, arg2, rc);
545 if (rc2 == FMT_DUP)
546 warn(code, CONTINUATION, " (reason: one item occurs twice in format)");
547 else if (rc2 == FMT_PARTTIME)
548 warn(code, CONTINUATION, " (reason: time without date or vice versa)");
549 else if (rc2 == FMT_BADPC)
550 warn(code, CONTINUATION, " (reason: an unknown item code is present)");
551 else if (rc2 == FMT_NOPC)
552 warn(code, CONTINUATION, " (reason: no item codes are present)");
553 else if (rc2 == FMT_BADCHAR)
554 warn(code, CONTINUATION,
555 " (reason: an unknown escape sequence is present)");
556 else if (rc2 == FMT_NOTERM)
557 warn(code, CONTINUATION,
558 " (reason: an unterminated string is present)");
559 else if (rc2 == FMT_BADBUILTIN)
560 warn(code, CONTINUATION, " (reason: non-existent built-in format)");
561 else if (rc2 == FMT_QBUTNOR)
562 warn(code, CONTINUATION, " (reason: query string without filename)");
563 /* despite finishing "else if", that should be all the rc2's */
564 return;
565 }
566 else {
567 if ((*logfmt)->used) {
568 fp = (Inputformatlist *)submalloc(xmemman, sizeof(Inputformatlist));
569 *logfmt = fp;
570 (*logfmt)->used = FALSE;
571 }
572 else {
573 for (fp = *logfmt; fp->next != NULL; TO_NEXT(fp))
574 ;
575 fp->next = (Inputformatlist *)submalloc(xmemman,
576 sizeof(Inputformatlist));
577 TO_NEXT(fp);
578 }
579 fp->form = form;
580 for (i = 0; i < INPUT_NUMBER; i++)
581 fp->count[i] = count[i];
582 fp->next = NULL;
583 }
584 }
585
configapachelogfmt(void * opt,char * cmd,char * arg1,char * arg2,int rc)586 void configapachelogfmt(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
587 char *p;
588
589 if (rc == 0) {
590 shortwarn(cmd, arg1, rc);
591 return;
592 }
593 if (rc > 1)
594 longwarn(cmd, arg1, arg2, rc);
595
596 if ((p = apachelogfmt(arg1)) == NULL)
597 warn('C', TRUE,
598 "Sorry, can't parse \"%%...{format}t\" in %s: ignoring whole line",
599 cmd);
600
601 else if (STREQ(cmd, "APACHELOGFORMAT"))
602 configlogfmt(opt, "APACHELOGFORMAT -> LOGFORMAT", p, NULL, 1);
603 else
604 configlogfmt(opt, "APACHEDAFAULTLOGFORMAT -> DEFAULTLOGFORMAT", p,
605 NULL, 1);
606 }
607
configrepord(void * opt,char * cmd,char * arg1,char * arg2,int rc)608 void configrepord(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
609 extern char repcodes[];
610 char *s1 = "REPORTORDER";
611 char *s2 = "REPORTORDER in anlghead.h";
612 char *s = (rc == -1)?s2:s1;
613 choice *order = (choice *)opt;
614 logical used[REP_NUMBER];
615 int i, j, k;
616
617 if (rc == 0) {
618 shortwarn(cmd, arg1, rc);
619 return;
620 }
621
622 for (i = 0; i < REP_NUMBER; i++)
623 used[i] = 0;
624 for (i = 0, k = 0; k < REP_NUMBER && arg1[i] != '\0'; i++) {
625 if (ISALNUM(arg1[i])) { /* else ignore */
626 for (j = 0; repcodes[j] != arg1[i] && repcodes[j] != '\0'; j++)
627 ;
628 if (repcodes[j] == '\0')
629 warn('C', TRUE, "Spurious character %c in %s: ignoring it", arg1[i],
630 s);
631 else if (used[j] == 0) {
632 order[k++] = j;
633 used[j] = 1;
634 }
635 else if (used[j] == 1) {
636 warn('C', TRUE, "Character %c used more than once in %s: "
637 "ignoring later occurrences", arg1[i], s);
638 used[j] = 2;
639 }
640 }
641 }
642
643 if (rc > 1 || arg1[i] != '\0')
644 longwarn(cmd, arg1, arg2, rc);
645
646 if (k < REP_NUMBER) {
647 for (i = 0; i < REP_NUMBER && k < REP_NUMBER; i++) {
648 if (used[i] == 0) { /* k should stay < R_N automatically, but... */
649 warn('C', TRUE, "Character %c not used in %s: adding it at end",
650 repcodes[i], s);
651 order[k++] = i;
652 }
653 }
654 }
655 order[k] = -1;
656 }
657
configstr(void * opt,char * cmd,char * arg1,char * arg2,int rc)658 void configstr(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
659 char **s = (char **)opt;
660 size_t len;
661
662 if (rc == 0) {
663 shortwarn(cmd, arg1, rc);
664 return;
665 }
666 if (rc > 1)
667 longwarn(cmd, arg1, arg2, rc);
668 len = strlen(arg1);
669 *s = (char *)xrealloc((void *)(*s), len + 1);
670 strcpy(*s, arg1);
671 }
672
configstrlist(void * opt,char * cmd,char * arg1,char * arg2,int rc)673 void configstrlist(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
674 Strlist **list = (Strlist **)opt;
675 Strlist *lp = NULL;
676 char *t;
677
678 if (rc == 0) {
679 shortwarn(cmd, arg1, rc);
680 return;
681 }
682 if (rc == -1) /* have to be careful because can't strtok a const string */
683 t = arg1;
684 else if ((t = strtok(arg1, ",")) == NULL) {
685 badwarn(cmd, TRUE, arg1, arg2, rc);
686 return;
687 }
688 if (rc > 1)
689 longwarn(cmd, arg1, arg2, rc);
690
691 if (*list != NULL) {
692 for (lp = *list; lp->next != NULL; TO_NEXT(lp))
693 ; /* find end of list */
694 }
695 for ( ; t != NULL; t = ((rc == -1)?NULL:strtok((char *)NULL, ","))) {
696 if (strcaseeq(t, "none"))
697 *list = NULL;
698 else {
699 if (*list == NULL) {
700 lp = (Strlist *)xmalloc(sizeof(Strlist));
701 *list = lp;
702 }
703 else {
704 lp->next = (Strlist *)xmalloc(sizeof(Strlist));
705 TO_NEXT(lp);
706 }
707 if (STREQ(cmd, "DOMCHARTEXPAND") && *t == '.') /* ugly special case */
708 t++;
709 COPYSTR(lp->name, t);
710 lp->next = NULL;
711 }
712 }
713 return;
714 }
715
configerrfile(void * opt,char * cmd,char * arg1,char * arg2,int rc)716 void configerrfile(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
717 /* NB Don't use freopen(), coz fatal errors sent to both errfile & stderr. */
718 FILE **err = (FILE **)opt;
719 FILE *tmp;
720
721 if (rc == 0) {
722 shortwarn(cmd, arg1, rc);
723 return;
724 }
725 if (rc > 1)
726 longwarn(cmd, arg1, arg2, rc);
727
728 if (strcaseeq(arg1, "stderr")) {
729 if (*err != stderr) {
730 debug('F', "Opening stderr as new ERRFILE");
731 warn('E', TRUE, "Redirecting future diagnostic messages to stderr");
732 fclose(*err);
733 *err = stderr;
734 }
735 }
736 else {
737 arg1 = buildfilename(ERRDIR, "", arg1);
738 if ((tmp = FOPENW(arg1)) == NULL)
739 warn('F', TRUE, "Failed to open ERRFILE %s: ignoring it", arg1);
740 else {
741 debug('F', "Opening %s as new ERRFILE", arg1);
742 warn('E', TRUE, "Redirecting future diagnostic messages to %s", arg1);
743 if (*err != stderr)
744 fclose(*err);
745 *err = tmp;
746 setvbuf(*err, NULL, ERRBUFMODE, BUFSIZ);
747 }
748 }
749 }
750
751 /* There are several versions of expandwildlogs(), according to the following
752 flags:
753 NODIRENT excludes all wildcards in logfile names.
754 Otherwise, if VMSDIRENT, MACDIRENT, WIN32DIRENT or RISCOSDIRENT is
755 defined, use that.
756 Otherwise use POSIX.2 glob, unless NOGLOB is defined when use POSIX.1
757 stuff, not allowing wildcards in directory names.
758 MACDIRENT does actually include the POSIX.1 dirent code here, but
759 implements all the functions itself in macstuff.c.
760 In all cases, expandwildlogs() takes an argument, Logfile *lp, and clones
761 it within the list (using clonelogs()) as it expands the wildcards. It
762 returns a pointer to the last clone. E.g. expandwildlogs(log2*) turns
763 log1 -> log2* -> log3 into log1 -> log2a -> log2b -> log3 returning log2b */
764 #ifndef NODIRENT
clonelogs(Logfile * from,char * name)765 Logfile *clonelogs(Logfile *from, char *name) {
766 Logfile *ans = (Logfile *)xmalloc(sizeof(Logfile));
767
768 memcpy(ans, from, sizeof(Logfile));
769 COPYSTR(ans->name, name);
770 ans->next = from->next;
771 from->next = ans;
772 return(ans);
773 }
774
775 #ifndef VMSDIRENT
776 #ifndef WIN32DIRENT
777 #ifndef RISCOSDIRENT
778 #ifndef NOGLOB
779 /* Glob code is due to Owen Cliffe Feb 2001, slightly modified by
780 Stephen Turner */
expandwildlogs(Logfile * lp,Logfile ** pter)781 Logfile *expandwildlogs(Logfile *lp, Logfile **pter) {
782 Logfile *ans = lp;
783 glob_t globbuf;
784 struct stat statbuf;
785 int i;
786
787 globbuf.gl_offs = 0;
788 if(glob(lp->name,GLOB_NOCHECK,NULL,&globbuf) != 0){
789 warn('F', TRUE, "Failed to expand wildcards %s", lp->name);
790 return(ans);
791 }
792
793 for(i=0;i<globbuf.gl_pathc;i++){
794 char * fname= globbuf.gl_pathv[i];
795 if (stat(fname, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
796 ans = clonelogs(ans, fname);
797 }
798 globfree(&globbuf);
799 if (ans != lp) /* remove expanded wildcard lp from list */
800 *pter = lp->next;
801 return(ans);
802 }
803 #else /* not GLOB; use POSIX dirent */
expandwildlogs(Logfile * lp,Logfile ** pter)804 Logfile *expandwildlogs(Logfile *lp, Logfile **pter) {
805 Logfile *ans = lp;
806 struct dirent *filep;
807 struct stat buf;
808 DIR *dirp;
809 char *dirname, *filename, *c;
810 size_t dirlen, len = 0;
811
812 if ((c = strrpbrk(lp->name, PATHSEPS)) == NULL) {
813 filename = lp->name;
814 dirname = (char *)xmalloc(3);
815 #ifdef MAC
816 dirname[0] = '\0';
817 #else
818 sprintf(dirname, ".%c", DIRSEP);
819 #endif
820 }
821 else {
822 filename = c + 1;
823 dirname = (char *)xmalloc((size_t)(c - lp->name) + 2);
824 memcpy((void *)dirname, (void *)(lp->name), (size_t)(c - lp->name) + 1);
825 dirname[(c - lp->name) + 1] = '\0';
826 }
827
828 if ((dirp = opendir(dirname)) == NULL) {
829 if (strchr(dirname, '*') != NULL || strchr(dirname, '?') != NULL)
830 warn('F', TRUE, "Cannot open directory %s: won't expand wildcards %s\n"
831 "(wildcards not allowed in directory name)", dirname, lp->name);
832 else
833 warn('F', TRUE, "Cannot open directory %s: won't expand wildcards %s",
834 dirname, lp->name);
835 return(ans);
836 }
837
838 dirlen = strlen(dirname);
839 while ((filep = readdir(dirp)) != NULL) {
840 if (MATCHES(filep->d_name, filename)) {
841 if (strlen(filep->d_name) > len) {
842 len = strlen(filep->d_name); /* d_namlen is not portable */
843 dirname = (char *)xrealloc((void *)dirname, dirlen + len + 1);
844 }
845 memcpy((void *)(dirname + dirlen), (void *)(filep->d_name), len + 1);
846 stat(dirname, &buf); /* dirname now contains complete filename */
847 if (S_ISREG(buf.st_mode))
848 ans = clonelogs(ans, dirname);
849 }
850 }
851 closedir(dirp);
852 free((void *)dirname);
853 if (ans != lp)
854 *pter = lp->next;
855 return(ans);
856 }
857 #endif /* NOGLOB */
858 #else /* RISCOSDIRENT */
859 /* The RISC OS dirent is due to Stefan Bellon (sbellon@sbellon.de) */
860 static struct {
861 unsigned int load, exec, length, attributes, type;
862 char name[255]; /* WARNING: arbitrary limit */
863 } direntry;
864
expandwildlogs(Logfile * lp,Logfile ** pter)865 Logfile *expandwildlogs(Logfile *lp, Logfile **pter) {
866 Logfile *ans = lp;
867 char *dirname, *leafname, *filename, *c;
868 int count, context = 0;
869 _kernel_oserror *e;
870 size_t dirlen, path = 0, len = 0;
871
872 if ((c = strrpbrk(lp->name, PATHSEPS)) == NULL) {
873 path = 0;
874 dirname = (char *)xmalloc(1);
875 dirname[0] = '\0';
876 leafname = lp->name;
877 }
878 else {
879 path = 1;
880 dirname = (char *)xmalloc((size_t)(c - lp->name) + 1);
881 memcpy((void *)dirname, (void *)(lp->name), (size_t)(c - lp->name));
882 dirname[(c - lp->name)] = '\0';
883 leafname = c + 1;
884 }
885 dirlen = strlen(dirname);
886 filename = (char *)xmalloc(dirlen + path + 1);
887 memcpy(filename, dirname, dirlen);
888 filename[dirlen] = DIRSEP;
889 filename[dirlen + path] = '\0'; /* may overwrite the DIRSEP again */
890
891 while (context != -1) {
892 e = _swix(OS_GBPB, _INR(0,6) | _OUTR(3,4),
893 10, dirname, &direntry, 1, context, sizeof(direntry), leafname,
894 &count, &context);
895 if (e != NULL)
896 error("%s\n", e->errmess);
897
898 if (count > 0 && direntry.type == 1) {
899 if (strlen(direntry.name) > len) {
900 len = strlen(direntry.name);
901 filename = (char *)xrealloc((void *)filename, dirlen + path + len + 1);
902 }
903 memcpy((void *)(filename + dirlen + path),
904 (void *)(direntry.name), len + 1);
905 ans = clonelogs(ans, filename);
906 }
907 }
908
909 free((void *)dirname);
910 free((void *)filename);
911 if (ans != lp)
912 *pter = lp->next;
913 return(ans);
914 }
915 #endif /* RISCOSDIRENT */
916 #else /* WIN32DIRENT */
expandwildlogs(Logfile * lp,Logfile ** pter)917 Logfile *expandwildlogs(Logfile *lp, Logfile **pter) {
918 Logfile *ans = lp;
919 struct _stat buf;
920 char *dirname, *c;
921 struct _finddata_t file;
922 long code;
923 int rc2;
924 size_t dirlen, len = 0;
925
926 if ((code = _findfirst(lp->name, &file)) != (long)(-1)) {
927 if ((c = strrpbrk(lp->name, PATHSEPS)) == NULL) {
928 dirname = (char *)xmalloc(1);
929 dirname[0] = '\0';
930 }
931 else {
932 dirname = (char *)xmalloc((size_t)(c - lp->name) + 2);
933 memcpy((void *)dirname, (void *)(lp->name), (size_t)(c - lp->name) + 1);
934 dirname[(c - lp->name) + 1] = '\0';
935 }
936 dirlen = strlen(dirname);
937 for (rc2 = 0; rc2 == 0; rc2 = _findnext(code, &file)) {
938 if (strlen(file.name) > len) {
939 len = strlen(file.name);
940 dirname = (char *)xrealloc((void *)dirname, dirlen + len + 1);
941 }
942 memcpy((void *)(dirname + dirlen), (void *)(file.name), len + 1);
943 _stat(dirname, &buf); /* dirname now contains complete filename */
944 if (_S_IFREG & buf.st_mode) /* bitwise & */
945 ans = clonelogs(ans, dirname);
946 }
947 _findclose(code);
948 free((void *)dirname);
949 }
950 if (ans != lp)
951 *pter = lp->next;
952 return(ans);
953 }
954 #endif /* WIN32DIRENT */
955 #else /* VMSDIRENT */
956 /* This function is due to Dave Jones (except for any errors I introduced when
957 converting from old configwildlogs() to new expandwildlogs()) */
expandwildlogs(Logfile * lp,Logfile ** pter)958 Logfile *expandwildlogs(Logfile *lp, Logfile **pter) {
959 Logfile *ans = lp;
960 static char fspec[VMS_FSPEC_MAX], related[VMS_FSPEC_MAX];
961 static char result[VMS_FSPEC_MAX];
962 static $DESCRIPTOR(fspec_dx,fspec);
963 static $DESCRIPTOR(related_dx,"");
964 static $DESCRIPTOR(default_dx,".log");
965 static $DESCRIPTOR(result_dx,result);
966 char *space, *ques;
967 long int context;
968 int status, stsval, length, LIB$FIND_FILE(), LIB$FIND_FILE_END();
969
970 length = strlen (lp->name);
971 if ( length >= VMS_FSPEC_MAX ) length = VMS_FSPEC_MAX - 1;
972 strncpy ( fspec, lp->name, length ); fspec[length] = '\0';
973 while ( ques = strchr(fspec,'?') ) *ques = '%';
974 fspec_dx.dsc$w_length = length;
975 for ( context = 0;
976 1&(status=LIB$FIND_FILE ( &fspec_dx, &result_dx, &context, &default_dx,
977 &related_dx, &stsval, (long *) 0 )); ) {
978 space = strchr ( result, ' ' );
979 if ( !space ) space = &result[VMS_FSPEC_MAX-1];
980 *space = '\0';
981 ans = clonelogs(ans, result);
982 /* Save last result to use as default for next lookup */
983 strcpy ( related, result );
984 related_dx.dsc$w_length = strlen(result);
985 related_dx.dsc$a_pointer = related;
986 }
987 if ( context ) LIB$FIND_FILE_END ( &context );
988 if (ans != lp)
989 *pter = lp->next;
990 return(ans);
991 }
992 #endif /* VMSDIRENT */
993 #endif /* NODIRENT */
994
configlogfile(void * opt,char * cmd,char * arg1,char * arg2,int rc)995 void configlogfile(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
996 extern Inputformatlist *logformat;
997 extern logical newloglist, iscache;
998 /* see note in configcachefile() below re iscache */
999 extern int tz;
1000
1001 Logfile **logfile = (Logfile **)opt;
1002 /* logfile[0] is logfiles, logfile[1] is cache files */
1003 Logfile *lf;
1004 char *s, *t;
1005
1006 if (rc == 0) {
1007 shortwarn(cmd, arg1, rc);
1008 return;
1009 }
1010 if (rc > 2)
1011 longwarn(cmd, arg1, arg2, rc);
1012
1013 if (strchr(arg1, ',') != NULL) {
1014 s = (char *)xmalloc(strlen(arg1) + 1);
1015 strcpy(s, arg1); /* can't strtok arg1 directly in case it's const */
1016 if ((t = strtok(s, ",")) == NULL) {
1017 badwarn(cmd, TRUE, arg1, arg2, rc);
1018 free((void *)s);
1019 return;
1020 }
1021 for ( ; t != NULL; t = strtok((char *)NULL, ","))
1022 configlogfile(opt, "LOGFILE", t, arg2, rc);
1023 free((void *)s);
1024 return;
1025 }
1026 if (strcaseeq(arg1, "NONE")) {
1027 /* from LOGFILE not CACHEFILE because CACHEFILE NONE caught earlier */
1028 configlogfmt((void *)&logformat, "LOGFORMAT", "DEFAULT", NULL, -1);
1029 logfile[iscache] = NULL;
1030 return;
1031 }
1032 if (newloglist) {
1033 logfile[0] = NULL;
1034 logfile[1] = NULL;
1035 newloglist = FALSE;
1036 }
1037 if (logfile[iscache] == NULL) {
1038 logfile[iscache] = (Logfile *)xmalloc(sizeof(Logfile));
1039 lf = logfile[iscache];
1040 }
1041 else {
1042 for (lf = logfile[iscache]; lf->next != NULL; TO_NEXT(lf))
1043 ;
1044 lf->next = (Logfile *)xmalloc(sizeof(Logfile));
1045 TO_NEXT(lf);
1046 }
1047 if (IS_STDIN(arg1) || rc == -2) {
1048 COPYSTR(lf->name, arg1);
1049 }
1050 else
1051 lf->name = buildfilename(iscache?(char *)CACHEDIR:(char *)LOGSDIR, "",
1052 arg1);
1053
1054 /* Non-configuration options initialised in my_lfopen */
1055 lf->type = LF_NOTOPENED;
1056 lf->format = logformat;
1057 lf->tz = tz;
1058 if (rc >= 2) { /* not from CACHEFILE: those are caught earlier */
1059 COPYSTR(lf->prefix, arg2);
1060 lf->prefixlen = strlen(lf->prefix);
1061 if (strstr(arg2, "%v") == NULL)
1062 lf->pvpos = UNSET;
1063 else
1064 lf->pvpos = strstr(arg2, "%v") - arg2;
1065 }
1066 else
1067 lf->prefix = NULL;
1068 lf->next = NULL;
1069 if (!iscache)
1070 logformat->used = TRUE;
1071 }
1072
configcachefile(void * opt,char * cmd,char * arg1,char * arg2,int rc)1073 void configcachefile(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1074 /* a wrapper to configlogfile(), but we have to catch a few cases first */
1075 Logfile **logfile = (Logfile **)opt;
1076 extern logical iscache;
1077 /* iscache tells configlogfile() to use logfile[1] instead of logfile[0],
1078 not to change logformat->used, and to use CACHEDIR instead of LOGSDIR. */
1079
1080 if (rc > 1) {
1081 longwarn(cmd, arg1, arg2, rc);
1082 rc = 1;
1083 }
1084 if (strcaseeq(arg1, "NONE")) {
1085 logfile[1] = NULL;
1086 return;
1087 }
1088 iscache = TRUE;
1089 configlogfile(opt, cmd, arg1, NULL, rc);
1090 iscache = FALSE;
1091 }
1092
configoutfile(void * opt,char * cmd,char * arg1,char * arg2,int rc)1093 void configoutfile(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1094 char **file = (char **)opt;
1095
1096 if (rc == 0) {
1097 shortwarn(cmd, arg1, rc);
1098 return;
1099 }
1100 if (rc > 1)
1101 longwarn(cmd, arg1, arg2, rc);
1102
1103 if (IS_STDOUT(arg1) || rc == -2) {
1104 COPYSTR(*file, arg1);
1105 }
1106 else
1107 *file = buildfilename(OUTDIR, "", arg1);
1108 }
1109
configchar(void * opt,char * cmd,char * arg1,char * arg2,int rc)1110 void configchar(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1111 char *c = (char *)opt;
1112
1113 if (rc == 0) {
1114 shortwarn(cmd, arg1, rc);
1115 return;
1116 }
1117 if (rc > 1)
1118 longwarn(cmd, arg1, arg2, rc);
1119 if (IS_EMPTY_STRING(arg1) || strcaseeq(arg1, "none"))
1120 *c = '\0';
1121 else {
1122 if (arg1[1] != '\0')
1123 longwarn(cmd, arg1, arg2, rc);
1124 *c = arg1[0];
1125 }
1126 }
1127
configbarstyle(void * opt,char * cmd,char * arg1,char * arg2,int rc)1128 void configbarstyle(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1129 char *g = (char *)opt;
1130 char h;
1131
1132 if (rc == 0 || IS_EMPTY_STRING(arg1)) {
1133 shortwarn(cmd, arg1, rc);
1134 return;
1135 }
1136 if (rc > 1 || arg1[1] != '\0')
1137 longwarn(cmd, arg1, arg2, rc);
1138 h = TOLOWER(arg1[0]);
1139 if (h != 'a' && h != 'b' && h != 'c' && h != 'd' && h != 'e' && h != 'f' &&
1140 h != 'g' && h != 'h' && h != 'i' && h != 'j')
1141 badwarn(cmd, TRUE, arg1, arg2, rc);
1142 else
1143 *g = h;
1144 }
1145
configgraph(void * opt,char * cmd,char * arg1,char * arg2,int rc)1146 void configgraph(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1147 char *g = (char *)opt; /* see also configallgraph() below */
1148
1149 if (rc == 0 || IS_EMPTY_STRING(arg1)) {
1150 shortwarn(cmd, arg1, rc);
1151 return;
1152 }
1153 if (rc > 1 || arg1[1] != '\0')
1154 longwarn(cmd, arg1, arg2, rc);
1155 if (arg1[0] != 'R' && arg1[0] != 'P' && arg1[0] != 'B' && arg1[0] != 'r' &&
1156 arg1[0] != 'p' && arg1[0] != 'b')
1157 badwarn(cmd, TRUE, arg1, arg2, rc);
1158 else
1159 *g = arg1[0];
1160 }
1161
configallgraph(void * opt,char * cmd,char * arg1,char * arg2,int rc)1162 void configallgraph(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1163 char *g = (char *)opt; /* same as configgraph() */
1164 int i;
1165
1166 if (rc == 0 || IS_EMPTY_STRING(arg1)) {
1167 shortwarn(cmd, arg1, rc);
1168 return;
1169 }
1170 if (rc > 1 || arg1[1] != '\0')
1171 longwarn(cmd, arg1, arg2, rc);
1172 if (arg1[0] != 'R' && arg1[0] != 'P' && arg1[0] != 'B' && arg1[0] != 'r' &&
1173 arg1[0] != 'p' && arg1[0] != 'b')
1174 badwarn(cmd, TRUE, arg1, arg2, rc);
1175 else {
1176 for (i = 0; i < DATEREP_NUMBER; i++)
1177 g[i] = arg1[0];
1178 }
1179 }
1180
configfloor(void * opt,char * cmd,char * arg1,char * arg2,int rc)1181 void configfloor(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1182 /* This function parses the following formats for floor
1183 r = requests, p = pages, b = bytes,
1184 s = 7-day requests, q = 7-day pages, c = 7-day bytes,
1185 d = last date, e = first date.
1186 -100(r|s|p|q|b|c|d|e) // top 100
1187 1000(r|s|p|q|b|c) // at least 1000
1188 9.5(k|M|G|T|P|E|Z|Y)(b|c) // ditto
1189 0.5%(r|s|p|q|b|c) // at least 0.5% of total
1190 0.5:(r|s|p|q|b|c) // at least 0.5% of largest
1191 970701(d|e) // last/first access since
1192 -00-0201(d|e) // same with a relative date
1193 Upper case letters for r|p|b|c|d are also acceptable. */
1194 extern time_t starttime;
1195 extern char *byteprefix;
1196
1197 Floor *floor = (Floor *)opt;
1198 char **b, *c, d;
1199 choice floorby;
1200 char qual = '\0';
1201 double mn;
1202 timecode_t t;
1203
1204 if (rc == 0 || IS_EMPTY_STRING(arg1)) {
1205 shortwarn(cmd, arg1, rc);
1206 return;
1207 }
1208 if (rc > 1)
1209 longwarn(cmd, arg1, arg2, rc);
1210 c = arg1 + strlen(arg1) - 1;
1211 d = TOUPPER(*c);
1212 if (d == 'R')
1213 floorby = REQUESTS;
1214 else if (d == 'S')
1215 floorby = REQUESTS7;
1216 else if (d == 'P')
1217 floorby = PAGES;
1218 else if (d == 'Q')
1219 floorby = PAGES7;
1220 else if (d == 'B')
1221 floorby = BYTES;
1222 else if (d == 'C')
1223 floorby = BYTES7;
1224 else if (d == 'D')
1225 floorby = DATESORT;
1226 else if (d == 'E')
1227 floorby = FIRSTDATE;
1228 else {
1229 badwarn(cmd, TRUE, arg1, arg2, rc);
1230 return;
1231 }
1232 if (floorby != DATESORT && floorby != FIRSTDATE) {
1233 b = (char **)xmalloc(sizeof(char *));
1234 mn = strtod(arg1, b);
1235 if (*b == arg1) {
1236 badwarn(cmd, TRUE, arg1, arg2, rc);
1237 return;
1238 }
1239 else if (*b == c - 1) {
1240 if (mn < 0) {
1241 badwarn(cmd, TRUE, arg1, arg2, rc);
1242 return;
1243 }
1244 else if (**b == '%' || **b == ':' ||
1245 ((floorby == BYTES || floorby == BYTES7) &&
1246 strchr(byteprefix + 1, **b)))
1247 qual = **b;
1248 else {
1249 badwarn(cmd, TRUE, arg1, arg2, rc);
1250 return;
1251 }
1252 }
1253 free((void *)b);
1254 }
1255 else { /* floorby == DATESORT or FIRSTDATE */
1256 if (arg1[0] == '-' && arg1[1] != '0' && ISDIGIT(arg1[1]))
1257 mn = atof(arg1);
1258 else if (parsedate(starttime, arg1, &t, TRUE, FALSE) == ERR) {
1259 badwarn(cmd, TRUE, arg1, arg2, rc);
1260 return;
1261 }
1262 else
1263 mn = (double)t;
1264 }
1265 floor->min = mn;
1266 floor->qual = qual;
1267 floor->floorby = floorby;
1268 }
1269
configtree(void * opt,char * cmd,char * arg1,char * arg2,int rc)1270 void configtree(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1271 Tree **treex = (Tree **)opt;
1272 Hashindex item;
1273 Hashentry *own;
1274 char *name, *nameend, *t;
1275
1276 if (rc == 0) {
1277 shortwarn(cmd, arg1, rc);
1278 return;
1279 }
1280 if (rc > 1)
1281 longwarn(cmd, arg1, arg2, rc);
1282
1283 if (strchr(arg1, ',') != NULL) {
1284 if ((t = strtok(arg1, ",")) == NULL) {
1285 badwarn(cmd, TRUE, arg1, arg2, rc);
1286 return;
1287 }
1288 for ( ; t != NULL; t = strtok((char *)NULL, ","))
1289 configtree(opt, cmd, t, NULL, -1);
1290 return;
1291 }
1292 own = newhashentry(DATA_NUMBER, FALSE);
1293 item.own = own;
1294 item.name = arg1;
1295 name = NULL;
1296 (*treex)->cutfn(&name, &nameend, item.name, TRUE);
1297 (void)treefind(name, nameend, &((*treex)->tree), &item, (*treex)->cutfn,
1298 TRUE, TRUE, FALSE, (*treex)->space, NULL, DATA_NUMBER);
1299 }
1300
configulong(void * opt,char * cmd,char * arg1,char * arg2,int rc)1301 void configulong(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1302 unsigned int *x = (unsigned int *)opt;
1303 char **c;
1304
1305 if (rc == 0 || IS_EMPTY_STRING(arg1)) {
1306 shortwarn(cmd, arg1, rc);
1307 return;
1308 }
1309 if (!ISDIGIT(arg1[0])) {
1310 badwarn(cmd, TRUE, arg1, arg2, rc);
1311 return;
1312 }
1313 c = (char **)xmalloc(sizeof(char *));
1314 *x = strtoul(arg1, c, 10);
1315 if (rc > 1 || **c != '\0')
1316 longwarn(cmd, arg1, arg2, rc);
1317 free((void *)c);
1318 }
1319
configuint(void * opt,char * cmd,char * arg1,char * arg2,int rc)1320 void configuint(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1321 unsigned int *x = (unsigned int *)opt;
1322 char **c;
1323
1324 if (rc == 0 || IS_EMPTY_STRING(arg1)) {
1325 shortwarn(cmd, arg1, rc);
1326 return;
1327 }
1328 if (!ISDIGIT(arg1[0])) {
1329 badwarn(cmd, TRUE, arg1, arg2, rc);
1330 return;
1331 }
1332 c = (char **)xmalloc(sizeof(char *));
1333 *x = (unsigned int)strtoul(arg1, c, 10);
1334 if (rc > 1 || **c != '\0')
1335 longwarn(cmd, arg1, arg2, rc);
1336 free((void *)c);
1337 }
1338
configoffset(void * opt,char * cmd,char * arg1,char * arg2,int rc)1339 void configoffset(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1340 /* configint with extra checks */
1341 int *x = (int *)opt;
1342 char **c;
1343 int y;
1344
1345 if (rc == 0 || IS_EMPTY_STRING(arg1)) {
1346 shortwarn(cmd, arg1, rc);
1347 return;
1348 }
1349 if (arg1[0] == '+') {
1350 if (!ISDIGIT(arg1[1])) {
1351 badwarn(cmd, TRUE, arg1, arg2, rc);
1352 return;
1353 }
1354 arg1++;
1355 }
1356 else if (!ISDIGIT(arg1[0]) && !(arg1[0] == '-' && ISDIGIT(arg1[1]))) {
1357 badwarn(cmd, TRUE, arg1, arg2, rc);
1358 return;
1359 }
1360 c = (char **)xmalloc(sizeof(char *));
1361 y = (int)strtol(arg1, c, 10);
1362 if (rc > 1 || **c != '\0')
1363 longwarn(cmd, arg1, arg2, rc);
1364 if (y > 40320 || y < -40320)
1365 warn('C', TRUE, "Ignoring offset of more than 28 days in configuration "
1366 "command\n%s %s", cmd, arg1);
1367 else {
1368 *x = y;
1369 if (*x % 30 != 0)
1370 warn('D', TRUE,
1371 "Offset not a multiple of 30 in configuration command\n%s %s",
1372 cmd, arg1);
1373 else if (*x > 1500 || *x < -1500)
1374 warn('D', TRUE,
1375 "Offset more than 25 hours in configuration command\n%s %s",
1376 cmd, arg1);
1377 }
1378 free((void *)c);
1379 }
1380
configlowmem(void * opt,char * cmd,char * arg1,char * arg2,int rc)1381 void configlowmem(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1382 choice *x = (choice *)opt;
1383
1384 if (rc == 0 || IS_EMPTY_STRING(arg1)) {
1385 shortwarn(cmd, arg1, rc);
1386 return;
1387 }
1388 if (!(arg1[0] >= '0' && arg1[0] <= '3')) {
1389 badwarn(cmd, TRUE, arg1, arg2, rc);
1390 return;
1391 }
1392 if (rc > 1 || arg1[1] != '\0')
1393 longwarn(cmd, arg1, arg2, rc);
1394 *x = (choice)(arg1[0] - '0');
1395 }
1396
aliastocount(char * s)1397 int aliastocount(char *s) {
1398 int n;
1399
1400 if (headcasematch(s, "PLAIN:"))
1401 return(0);
1402
1403 n = (strchr(s, '*') != NULL);
1404 while ((s = strchr(s, '$')) != NULL) {
1405 s++;
1406 if (*s < '1' || *s > '9')
1407 s++;
1408 else
1409 n = MAX(n, (*s - '0'));
1410 }
1411 return(n);
1412 }
1413
configaliasto(char * s,logical is_regex)1414 AliasTo *configaliasto(char *s, logical is_regex) {
1415 /* Can prob be optimised somewhat, but with loss of any remaining clarity. */
1416 AliasTo *ans, *atp;
1417 char *t, next;
1418
1419 ans = (AliasTo *)xmalloc(sizeof(AliasTo));
1420
1421 if (headcasematch(s, "PLAIN:")) {
1422 ans->string = (char *)xmalloc(strlen(s) - 5);
1423 strcpy(ans->string, s + 6);
1424 ans->after = -1;
1425 ans->next = NULL;
1426 return(ans);
1427 }
1428
1429 /* else the normal case */
1430 atp = ans;
1431 while (TRUE) {
1432 t = strpbrk(s, "*$");
1433 while (t != NULL && *t == '$' &&
1434 (*(t + 1) < '1' || *(t + 1) > '9')) /* ignore ${non-digit} */
1435 t = strpbrk(t + 2, "*$");
1436 if (t == NULL)
1437 t = strchr(s, '\0');
1438 atp->string = (char *)xmalloc(t - s + 1);
1439 memcpy(atp->string, s, t - s);
1440 atp->string[t - s] = '\0';
1441 for (s = atp->string; (s = strstr(s, "$$")) != NULL; s++)
1442 memmove(s + 1, s + 2, strlen(s + 1)); /* "$$"->"$" (reuse s) */
1443 if (*t == '\0')
1444 atp->after = -1;
1445 else {
1446 if (*t == '*')
1447 next = '1';
1448 else /* *t == '$' */
1449 next = *(++t);
1450 atp->after = 2 * ((int)next - (int)(is_regex?'0':'1'));
1451 /* internal index of *'s is from 0; of regex brackets is from 1 */
1452 }
1453 if (*t == '\0' || *(t + 1) == '\0') {
1454 atp->next = NULL;
1455 return(ans);
1456 }
1457 else {
1458 atp->next = (AliasTo *)xmalloc(sizeof(AliasTo));
1459 TO_NEXT(atp);
1460 }
1461 s = t + 1; /* set s to rest of original string */
1462 }
1463 }
1464
configalias(void * opt,char * cmd,char * arg1,char * arg2,int rc)1465 void configalias(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1466 Alias **alias = (Alias **)opt;
1467 Alias *ap;
1468 unsigned int leftstars;
1469 int maxrightstar;
1470 char starchar;
1471 logical is_regex = FALSE;
1472 pcre *pattern = NULL;
1473 char *errstr;
1474 int erroffset;
1475
1476 if (rc == 0) {
1477 shortwarn(cmd, arg1, rc);
1478 return;
1479 }
1480 if (rc == 1) {
1481 if (strcaseeq(arg1, "none"))
1482 *alias = NULL;
1483 else
1484 shortwarn(cmd, arg1, rc);
1485 return;
1486 }
1487 if (rc > 2)
1488 longwarn(cmd, arg1, arg2, rc);
1489
1490 if (headcasematch(arg1, "REGEXP:") || headcasematch(arg1, "REGEXPI:")) {
1491 if ((pattern =
1492 pcre_compile(arg1 + 7 + (arg1[6] != ':'),
1493 PCRE_DOTALL | ((arg1[6] == ':')?0:PCRE_CASELESS),
1494 (const char **)(&errstr), &erroffset, NULL)) == NULL) {
1495 badwarn(cmd, FALSE, arg1, arg2, rc);
1496 warn('C', CONTINUATION, " (%s in regular expression)", errstr);
1497 return;
1498 }
1499 starchar = '(';
1500 pcre_fullinfo(pattern, NULL, PCRE_INFO_CAPTURECOUNT, (void *)&leftstars);
1501 is_regex = TRUE;
1502 }
1503 else {
1504 starchar = '*';
1505 leftstars = chrdistn(arg1, starchar);
1506 }
1507 if (leftstars >= 2 && strchr(arg2, '*') != NULL &&
1508 !headcasematch(arg2, "PLAIN:")) {
1509 badwarn(cmd, FALSE, arg1, arg2, rc);
1510 warn('C', CONTINUATION,
1511 " (Can't have * on RHS with two %c's on LHS: use $1, $2 etc.)",
1512 starchar);
1513 return;
1514 }
1515 if ((maxrightstar = aliastocount(arg2)) > (int)leftstars) {
1516 badwarn(cmd, FALSE, arg1, arg2, rc);
1517 warn('C', CONTINUATION, " (More substitutions on RHS than %c's on LHS)",
1518 starchar);
1519 return;
1520 }
1521
1522 /* add new one to front for speed; swap round in reversealias() later */
1523 ap = (Alias *)xmalloc(sizeof(Alias));
1524 COPYSTR(ap->from, arg1); /* save string even for regex: might l.c. */
1525 ap->to = configaliasto(arg2, is_regex);
1526 ap->isregex = is_regex;
1527 if (is_regex)
1528 ap->pattern = pattern;
1529 ap->next = *alias;
1530 *alias = ap;
1531 }
1532
configstrpair(void * opt,char * cmd,char * arg1,char * arg2,int rc)1533 void configstrpair(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1534 Strpairlist **pair = (Strpairlist **)opt;
1535 Strpairlist *ap;
1536
1537 if (rc == 0) {
1538 shortwarn(cmd, arg1, rc);
1539 return;
1540 }
1541 if (rc == 1) {
1542 if (strcaseeq(arg1, "none"))
1543 *pair = NULL;
1544 else
1545 shortwarn(cmd, arg1, rc);
1546 return;
1547 }
1548 if (chrn(arg1, '*') >= 2 && strchr(arg2, '*') != NULL) {
1549 badwarn(cmd, TRUE, arg1, arg2, rc);
1550 return;
1551 }
1552 if (rc > 2)
1553 longwarn(cmd, arg1, arg2, rc);
1554
1555 if (*pair == NULL) {
1556 ap = (Strpairlist *)xmalloc(sizeof(Strpairlist));
1557 *pair = ap;
1558 }
1559 else {
1560 for (ap = *pair; ap->next != NULL; TO_NEXT(ap))
1561 ; /* find end of list */
1562 ap->next = (Strpairlist *)xmalloc(sizeof(Strpairlist));
1563 TO_NEXT(ap);
1564 }
1565 COPYSTR(ap->name, arg1);
1566 COPYSTR(ap->data, arg2);
1567 ap->next = NULL;
1568 }
1569
configstrpairlist(void * opt,char * cmd,char * arg1,char * arg2,int rc)1570 void configstrpairlist(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1571 char *t;
1572
1573 if (arg1 == NULL) {
1574 configstrpair(opt, cmd, arg1, arg2, rc); /* will warn about args if nec. */
1575 return;
1576 }
1577 if ((t = strtok(arg1, ",")) == NULL) {
1578 badwarn(cmd, TRUE, arg1, arg2, rc);
1579 return;
1580 }
1581 for ( ; t != NULL; t = strtok((char *)NULL, ","))
1582 configstrpair(opt, cmd, t, arg2, rc);
1583 return;
1584 }
1585
configstrpair2list(void * opt,char * cmd,char * arg1,char * arg2,int rc)1586 void configstrpair2list(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1587 /* comma-separated list in arg2 instead of arg1 */
1588 char *t;
1589
1590 if (arg2 == NULL) {
1591 configstrpair(opt, cmd, arg1, arg2, rc); /* will warn about args if nec. */
1592 return;
1593 }
1594 if ((t = strtok(arg2, ",")) == NULL) {
1595 badwarn(cmd, TRUE, arg1, arg2, rc);
1596 return;
1597 }
1598 for ( ; t != NULL; t = strtok((char *)NULL, ","))
1599 configstrpair(opt, cmd, arg1, t, rc);
1600 return;
1601 }
1602
configdomlevel(void * opt,char * cmd,char * arg1,char * arg2,int rc)1603 void configdomlevel(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1604 Strpairlist ***levels = (Strpairlist ***)opt;
1605 unsigned int c;
1606
1607 c = 26 * ((int)(*arg1 - 'a'));
1608 if (*arg1 != '\0')
1609 c += (int)(*(arg1 + 1) - 'a');
1610 if (c >= DOMLEVEL_NUMBER) /* this shouldn't happen */
1611 c = DOMLEVEL_NUMBER - 1;
1612 configstrpair((void *)(&((*levels)[c])), cmd, arg1, arg2, rc);
1613 }
1614
configinex(void * opt,char * cmd,char * arg1,char * arg2,int rc,logical in,logical omitinitdot,logical omittrailslash)1615 void configinex(void *opt, char *cmd, char *arg1, char *arg2, int rc,
1616 logical in, logical omitinitdot, logical omittrailslash) {
1617 Include **include = (Include **)opt;
1618 Include *ip;
1619 char *errstr;
1620 int erroffset;
1621 char *t;
1622
1623 if (rc == 0) {
1624 shortwarn(cmd, arg1, rc);
1625 return;
1626 }
1627 if (rc > 1)
1628 longwarn(cmd, arg1, arg2, rc);
1629
1630 if (strchr(arg1, ',') != NULL && !headcasematch(arg1, "REGEXP:") &&
1631 !headcasematch(arg1, "REGEXPI:")) {
1632 /* This still allows a REGEXP in a comma-separated list if it's not the
1633 first item and doesn't contain a comma. The docs don't admit this. :) */
1634 if ((t = strtok(arg1, ",")) == NULL) {
1635 badwarn(cmd, TRUE, arg1, arg2, rc);
1636 return;
1637 }
1638 for ( ; t != NULL; t = strtok((char *)NULL, ","))
1639 configinex(opt, cmd, t, NULL, -1, in, omitinitdot, omittrailslash);
1640 return;
1641 }
1642
1643 /* NB Put new include on front of list (so will test it earlier) */
1644 ip = *include;
1645 *include = (Include *)xmalloc(sizeof(Include));
1646 if (omitinitdot && arg1[0] == '.')
1647 arg1++;
1648 if (omittrailslash && !IS_EMPTY_STRING(arg1) &&
1649 arg1[strlen(arg1) - 1] == '/')
1650 arg1[strlen(arg1) - 1] = '\0';
1651 COPYSTR((*include)->name, arg1);
1652 (*include)->next = ip; /* save name even for regex: might l.c. */
1653 if (headcasematch(arg1, "REGEXP:") || headcasematch(arg1, "REGEXPI:")) {
1654 if (((*include)->pattern =
1655 pcre_compile(arg1 + 7 + (arg1[6] != ':'),
1656 PCRE_DOTALL | ((arg1[6] == ':')?0:PCRE_CASELESS),
1657 (const char **)(&errstr), &erroffset, NULL)) == NULL) {
1658 *include = ip;
1659 badwarn(cmd, FALSE, arg1, arg2, rc);
1660 warn('C', CONTINUATION, " (%s in regular expression)", errstr);
1661 return;
1662 }
1663 (*include)->type = in?(REGEX_INC):(REGEX_EXC);
1664 }
1665 else {
1666 if (strcaseeq((*include)->name, "pages"))
1667 strcpy((*include)->name, "pages");
1668 (*include)->type = in?(NORMAL_INC):(NORMAL_EXC);
1669 }
1670 }
1671
configinc(void * opt,char * cmd,char * arg1,char * arg2,int rc)1672 void configinc(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1673 configinex(opt, cmd, arg1, arg2, rc, TRUE, FALSE, FALSE);
1674 }
1675
configexc(void * opt,char * cmd,char * arg1,char * arg2,int rc)1676 void configexc(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1677 configinex(opt, cmd, arg1, arg2, rc, FALSE, FALSE, FALSE);
1678 }
1679
1680 /* same, omitting initial dot */
configincd(void * opt,char * cmd,char * arg1,char * arg2,int rc)1681 void configincd(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1682 configinex(opt, cmd, arg1, arg2, rc, TRUE, TRUE, FALSE);
1683 }
1684
configexcd(void * opt,char * cmd,char * arg1,char * arg2,int rc)1685 void configexcd(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1686 configinex(opt, cmd, arg1, arg2, rc, FALSE, TRUE, FALSE);
1687 }
1688
1689 /* same, omitting trailing slash */
configincs(void * opt,char * cmd,char * arg1,char * arg2,int rc)1690 void configincs(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1691 configinex(opt, cmd, arg1, arg2, rc, TRUE, FALSE, TRUE);
1692 }
1693
configexcs(void * opt,char * cmd,char * arg1,char * arg2,int rc)1694 void configexcs(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1695 configinex(opt, cmd, arg1, arg2, rc, FALSE, FALSE, TRUE);
1696 }
1697
1698 /* Try and interpret a string representing a range of IP addresses or a subnet
1699 mask. Return whether successful. cf matchiprange() in utils.c.
1700 */
parseiprange(char * arg1,unsigned long * minaddr,unsigned long * maxaddr)1701 logical parseiprange(char *arg1, unsigned long *minaddr,
1702 unsigned long *maxaddr) {
1703 unsigned long addr = 0, mask;
1704 int n1, n2, octet, i;
1705 char *arg, *tok, *sep, *c;
1706
1707 /* We only interpret ranges and subnet masks here, not single addresses or
1708 wildcards, for example. Everything else is rejected so that it gets
1709 submitted to normal string matching, which is more efficient.
1710 */
1711 if (!ISDIGIT(*arg1) || (strchr(arg1, '-') == NULL &&
1712 strchr(arg1, '/') == NULL))
1713 return FALSE;
1714
1715 /* We can't use strtok here because it's already used in an outer loop in
1716 confighostinex(), so we do it by hand. We first copy arg1 into arg so that
1717 we don't have to worry about restoring arg1 afterwards.
1718 */
1719 COPYSTR(arg, arg1);
1720 for (octet = 0, tok = arg; octet < 4 && tok != NULL; octet++, tok = sep) {
1721
1722 if ((sep = strchr(tok, '.')) != NULL) {
1723 *sep = '\0';
1724 sep++; /* sep is now NULL if no more tokens, else start of next token */
1725 }
1726
1727 /* Case 1: 131.111-114 or 131.111-114.* */
1728 if ((c = strchr(tok, '-')) != NULL) {
1729 *c = 0;
1730 n1 = atoi255(tok);
1731 n2 = atoi255(c + 1);
1732 if (n1 < 0 || n2 <= n1) /* includes n2 < 0 */
1733 return FALSE;
1734 /* Check just dots and stars left in the string. We don't bother checking
1735 for too many dots though. */
1736 for (c = sep; c != NULL && *c != '\0'; c++) {
1737 if (*c != '.' && *c != '*')
1738 return FALSE;
1739 }
1740 addr <<= 8 * (4 - octet);
1741 *minaddr = addr + (n1 << 8 * (3 - octet));
1742 *maxaddr = addr + (((n2 + 1) << 8 * (3 - octet)) - 1);
1743 /* brackets essential to avoid underflow or overflow */
1744 return TRUE;
1745 }
1746
1747 /* Case 2: 131.111.20.18/24 */
1748 if ((c = strchr(tok, '/')) != NULL) {
1749 if (octet != 3) /* slash only valid after last octet */
1750 return FALSE;
1751 if (sep != NULL && *sep != '\0') /* check end of string */
1752 return FALSE;
1753 *c = 0;
1754 n1 = atoi255(tok);
1755 n2 = atoi255(c + 1);
1756 if (n1 < 0 || n2 < 1 || n2 > 32)
1757 return FALSE;
1758 addr <<= 8;
1759 addr += n1;
1760 /* calculate subnet mask */
1761 mask = 0;
1762 for (i = 0; i < 32; i++) {
1763 mask <<= 1;
1764 mask += (i < n2);
1765 }
1766 addr &= mask;
1767 *minaddr = addr;
1768 *maxaddr = addr + ((1 << (32 - n2)) - 1);
1769 /* brackets essential to avoid underflow or overflow */
1770 return TRUE;
1771 }
1772
1773 /* Case 3: The normal case: just the next number */
1774 n1 = atoi255(tok);
1775 if (n1 < 0)
1776 return FALSE;
1777 addr <<= 8;
1778 addr += n1;
1779
1780 } /* for octet */
1781
1782 /* We can only get here if we've read four octets but still not encountered
1783 - or / . In that case, the string is corrupt. */
1784 return FALSE;
1785 }
1786
1787 /* For hosts, use confighostinex(). */
1788 /* First try parsing as a contiguous range of IP addresses, using
1789 parseiprange() above. If that fails, pass to configinex for normal
1790 processing. */
confighostinex(void * opt,char * cmd,char * arg1,char * arg2,int rc,logical in)1791 void confighostinex(void *opt, char *cmd, char *arg1, char *arg2, int rc,
1792 logical in) {
1793 Include **include = (Include **)opt;
1794 Include *ip;
1795 unsigned long minaddr, maxaddr;
1796 char *c;
1797
1798 if (rc == 0) {
1799 shortwarn(cmd, arg1, rc);
1800 return;
1801 }
1802 if (rc > 1)
1803 longwarn(cmd, arg1, arg2, rc);
1804
1805 if (strchr(arg1, ',') != NULL) {
1806 if ((c = strtok(arg1, ",")) == NULL) {
1807 badwarn(cmd, TRUE, arg1, arg2, rc);
1808 return;
1809 }
1810 for ( ; c != NULL; c = strtok((char *)NULL, ","))
1811 confighostinex(opt, cmd, c, NULL, 1, in);
1812 return;
1813 }
1814
1815 /* First try parsing the argument as a range of IP addresses. */
1816 if (parseiprange(arg1, &minaddr, &maxaddr)) {
1817 ip = *include;
1818 *include = (Include *)xmalloc(sizeof(Include));
1819 COPYSTR((*include)->name, arg1);
1820 (*include)->minaddr = minaddr;
1821 (*include)->maxaddr = maxaddr;
1822 (*include)->next = ip;
1823 (*include)->type = in?(IPADDR_INC):(IPADDR_EXC);
1824 }
1825 /* If that fails, parse to the regular string-like processing. */
1826 else
1827 configinex(opt, cmd, arg1, arg2, rc, in, FALSE, FALSE);
1828 }
1829
confighostinc(void * opt,char * cmd,char * arg1,char * arg2,int rc)1830 void confighostinc(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1831 confighostinex(opt, cmd, arg1, arg2, rc, TRUE);
1832 }
1833
confighostexc(void * opt,char * cmd,char * arg1,char * arg2,int rc)1834 void confighostexc(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1835 confighostinex(opt, cmd, arg1, arg2, rc, FALSE);
1836 }
1837
1838 /* for status codes */
configscinex(void * opt,char * cmd,char * arg1,char * arg2,int rc,logical in)1839 void configscinex(void *opt, char *cmd, char *arg1, char *arg2, int rc,
1840 logical in) {
1841 choice *code2type = (choice *)opt;
1842 unsigned int bottom, top, i;
1843 char *c, *d;
1844 logical f = FALSE;
1845
1846 if (rc == 0) {
1847 shortwarn(cmd, arg1, rc);
1848 return;
1849 }
1850 if (rc > 1)
1851 longwarn(cmd, arg1, arg2, rc);
1852
1853 if (strchr(arg1, ',') != NULL) {
1854 if ((c = strtok(arg1, ",")) == NULL) {
1855 badwarn(cmd, TRUE, arg1, arg2, rc);
1856 return;
1857 }
1858 for ( ; c != NULL; c = strtok((char *)NULL, ","))
1859 configscinex(opt, cmd, c, NULL, 1, in);
1860 return;
1861 }
1862
1863 if (STREQ(arg1, "*")) {
1864 bottom = MIN_SC;
1865 top = SC_NUMBER - 1;
1866 }
1867 else {
1868 if (*arg1 == '-') {
1869 bottom = MIN_SC;
1870 c = arg1 + 1;
1871 if (!ISDIGIT(*c)) {
1872 badwarn(cmd, TRUE, arg1, arg2, rc);
1873 return;
1874 }
1875 }
1876 else {
1877 bottom = (unsigned int)strtoul(arg1, &c, 10);
1878 if (*c == '-') {
1879 c++;
1880 f = TRUE;
1881 }
1882 if (bottom < MIN_SC || bottom >= SC_NUMBER ||
1883 (*c != '\0' && !ISDIGIT(*c))) {
1884 badwarn(cmd, TRUE, arg1, arg2, rc);
1885 return;
1886 }
1887 }
1888 if (*c == '\0') {
1889 if (f)
1890 top = SC_NUMBER - 1;
1891 else
1892 top = bottom;
1893 }
1894 else {
1895 top = (unsigned int)strtoul(c, &d, 10);
1896 if (top < bottom || top >= SC_NUMBER || *d != '\0') {
1897 badwarn(cmd, TRUE, arg1, arg2, rc);
1898 return;
1899 }
1900 }
1901 }
1902
1903 if (code2type[0] == UNSET)
1904 code2type[0] = in?SUCCESS:UNWANTED; /* mark first in/ex: see finalinit() */
1905 for (i = bottom; i <= top; i++)
1906 code2type[i] = in?SUCCESS:UNWANTED; /* SUCCESS set more carefully there */
1907 }
1908
configscinc(void * opt,char * cmd,char * arg1,char * arg2,int rc)1909 void configscinc(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1910 configscinex(opt, cmd, arg1, arg2, rc, TRUE);
1911 }
1912
configscexc(void * opt,char * cmd,char * arg1,char * arg2,int rc)1913 void configscexc(void *opt, char *cmd, char *arg1, char *arg2, int rc) {
1914 configscinex(opt, cmd, arg1, arg2, rc, FALSE);
1915 }
1916