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