1 /*
2  * sma -- Sendmail log analyser
3  *
4  * Copyright (c) 2000 - 2003 Jarkko Turkulainen. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY JARKKO TURKULAINEN ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL JARKKO TURKULAINEN BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $Date: 2002/12/06 12:48:51 $
30  */
31 
32 
33 /* Copyright for routines in scan_time() */
34 
35 /*
36  * Copyright (c) 1985, 1987, 1988, 1993
37  *      The Regents of the University of California.  All rights reserved.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. All advertising materials mentioning features or use of this software
48  *    must display the following acknowledgement:
49  *      This product includes software developed by the University of
50  *      California, Berkeley and its contributors.
51  * 4. Neither the name of the University nor the names of its contributors
52  *    may be used to endorse or promote products derived from this software
53  *    without specific prior written permission.
54  *
55  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65  * SUCH DAMAGE.
66  */
67 
68 #include "sma.h"
69 
70 #ifndef __DragonFly__
71 int isdigit(int);
72 #endif
73 
74 /*
75  * Take month as an ascii string and return integer
76  */
77 int
conv_mon(const char * mon)78 conv_mon(const char *mon) {
79 	int k;
80 	const char *montab[] = {
81 		"Jan", "Feb", "Mar", "Apr", "May",
82 		"Jun", "Jul", "Aug", "Sep", "Oct",
83 		"Nov", "Dec"
84 	};
85 
86 	for (k = 0; k < 12; k++) {
87 		if (!strcmp(mon, montab[k]))
88 			break;
89 	}
90 	return k;
91 }
92 
93 /*
94  * Take month, day, hour, minute and second as integers
95  * and return time_t
96  */
97 time_t
conv_time(int mon,int day,int hh,int min,int sec)98 conv_time(int mon, int day, int hh, int min, int sec) {
99 
100 	/*
101 	 * Assume that the year is current year...
102 	 * This is bug.
103 	 */
104 
105 	/* sanity check: */
106 	if (mon < 0 || mon >= 12)
107 		return (time_t)NULL;
108 
109 	/* fill the structure: */
110 	tp.tm_sec=sec;
111 	tp.tm_min=min;
112 	tp.tm_hour=hh;
113 	tp.tm_mday=day;
114 	tp.tm_mon=mon;
115 	tp.tm_year=curr->tm_year;
116 	tp.tm_isdst=-1;
117 
118 	return mktime(&tp);
119 }
120 
121 void
scan_time(time_t * utime,char * p)122 scan_time(time_t *utime, char *p) {
123 #define	ATOI2(ar)	((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
124 
125 	struct tm *lt;
126 	char *t, *dot;
127 	int bigyear;
128 	int yearset = 0;
129 
130 	for (t = p, dot = NULL; *t; ++t) {
131 		if (isdigit(*t))
132 			continue;
133 		if (*t == '.' && dot == NULL) {
134 			dot = t;
135 			continue;
136 		}
137 		usage();
138 	}
139 
140 	tval = time((time_t *)NULL);
141         lt = localtime(&tval);
142 
143 	lt->tm_isdst = -1;			/* correct for DST */
144 
145 	if (dot != NULL) {			/* .SS */
146 		*dot++ = '\0';
147 		if (strlen(dot) != 2)
148 			usage();
149 		lt->tm_sec = ATOI2(dot);
150 		if (lt->tm_sec > 61)
151 			usage();
152 	} else
153 		lt->tm_sec = 0;
154 
155 	switch (strlen(p)) {
156 	case 12:				/* cc */
157 		bigyear = ATOI2(p);
158 		lt->tm_year = bigyear * 100 - 1900;
159 		yearset = 1;
160 		/* FALLTHROUGH */
161 	case 10:				/* yy */
162 		if (yearset) {
163 			lt->tm_year += ATOI2(p);
164 		} else {
165 			lt->tm_year = ATOI2(p);
166 			if (lt->tm_year < 69)		/* hack for 2000 ;-} */
167 				lt->tm_year += (2000 - 1900);
168 			else
169 				lt->tm_year += (1900 - 1900);
170 		}
171 		/* FALLTHROUGH */
172 	case 8:					/* mm */
173 		lt->tm_mon = ATOI2(p);
174 		if ((lt->tm_mon > 12) || !lt->tm_mon)
175 			usage();
176 		--lt->tm_mon;			/* time struct is 0 - 11 */
177 		/* FALLTHROUGH */
178 	case 6:					/* dd */
179 		lt->tm_mday = ATOI2(p);
180 		if ((lt->tm_mday > 31) || !lt->tm_mday)
181 			usage();
182 		/* FALLTHROUGH */
183 	case 4:					/* HH */
184 		lt->tm_hour = ATOI2(p);
185 		if (lt->tm_hour > 23)
186 			usage();
187 		/* FALLTHROUGH */
188 	case 2:					/* MM */
189 		lt->tm_min = ATOI2(p);
190 		if (lt->tm_min > 59)
191 			usage();
192 		break;
193 	default:
194 		usage();
195 	}
196 	*utime = mktime(lt);
197 }
198 
199 /*
200  * Prepare a space for sorted address stuctures and do the sorting
201  */
202 void
sort(struct host * hptr)203 sort(struct host *hptr) {
204 	int i, j, k;
205 	struct envpair *eptr;
206 	struct in *iptr;
207 	struct out *optr;
208 	struct relpair *rptr;
209 	struct rin *riptr;
210 	struct rout *roptr;
211 	struct status *statptr;
212 	struct rule *ruleptr;
213 	struct rrelay *rrptr;
214 
215 	/* space for sorted structures: */
216 	if (!(hptr->setab = malloc(hptr->edif * sizeof(struct envpair *))))
217 		error_memory();
218 	if (!(hptr->sitab = malloc(hptr->idif * sizeof(struct sin *))))
219 		error_memory();
220 	if (!(hptr->sotab = malloc(hptr->odif * sizeof(struct sout *))))
221 		error_memory();
222 	if (!(hptr->srtab = malloc(hptr->rrdif * sizeof(struct relpair *))))
223 		error_memory();
224 	if (!(hptr->rsitab = malloc(hptr->ridif * sizeof(struct rin *))))
225 		error_memory();
226 	if (!(hptr->rsotab = malloc(hptr->rodif * sizeof(struct rout *))))
227 		error_memory();
228 	if (!(hptr->ssttab = malloc(hptr->sdif * sizeof(struct status *))))
229 		error_memory();
230 	if (!(hptr->sruletab = malloc(hptr->rdif * sizeof(struct rule *))))
231 		error_memory();
232 
233 	/* envelope pairs: */
234 	for (i = 0, k = 0; k < asize; k++)
235 	  for (eptr = hptr->etab[k]; eptr; eptr = eptr->next)
236 			/* copy pointers: */
237 			hptr->setab[i++] = eptr;
238 	/* relay pairs: */
239 	for (i = 0, k = 0; k < rsize; k++)
240 	  for (rptr = hptr->rtab[k]; rptr; rptr = rptr->next)
241 			/* copy pointers: */
242 			hptr->srtab[i++] = rptr;
243 
244 	/* input: */
245 	for (i = 0, k = 0; k < asize; k++)
246 	  for (iptr = hptr->itab[k]; iptr; iptr = iptr->next)
247 			/* copy pointers: */
248 			hptr->sitab[i++] = iptr;
249 	for (i = 0, k = 0; k < rsize; k++)
250 	  for (riptr = hptr->ritab[k]; riptr; riptr = riptr->next)
251 			/* copy pointers: */
252 			hptr->rsitab[i++] = riptr;
253 
254 	/* output: */
255 	for (i = 0, k = 0; k < asize; k++)
256 	  for (optr = hptr->otab[k]; optr; optr = optr->next)
257 			/* copy pointers: */
258 			hptr->sotab[i++] = optr;
259 	for (i = 0, k = 0; k < rsize; k++)
260 	  for (roptr = hptr->rotab[k];roptr; roptr = roptr->next)
261 			/* copy pointers: */
262 			hptr->rsotab[i++] = roptr;
263 
264 	/* status and ruleset: */
265 	for (i = 0, k = 0; k < rsize; k++)
266 	  for (statptr = hptr->sttab[k]; statptr; statptr = statptr->next)
267 			/* copy pointers: */
268 			hptr->ssttab[i++] = statptr;
269 
270 	for (i = 0, k = 0; k < rsize; k++)
271 	  for (ruleptr = hptr->ruletab[k]; ruleptr; ruleptr = ruleptr->next) {
272 
273 			/* copy pointers: */
274 			hptr->sruletab[i++] = ruleptr;
275 
276 			/* alloc space for sorted relays: */
277 			if (!(ruleptr->srrelaytab =
278 			  malloc(ruleptr->reldif * sizeof(struct rrelay *))))
279 				error_memory();
280 
281 	  		for (j = 0, rrptr = ruleptr->rrelaytab; rrptr;
282 			    rrptr = rrptr->next)
283 				ruleptr->srrelaytab[j++] = rrptr;
284 
285 			/* sort */
286 			qsort(ruleptr->srrelaytab, ruleptr->reldif,
287 			  sizeof(struct rrelay *), comp_rrel);
288 	  }
289 
290 	/* sort */
291 	qsort(hptr->setab, hptr->edif, sizeof(struct envpair *), comp_env);
292 	qsort(hptr->sitab, hptr->idif, sizeof(struct in *), comp_in);
293 	qsort(hptr->sotab, hptr->odif, sizeof(struct out *), comp_out);
294 	qsort(hptr->srtab, hptr->rrdif, sizeof(struct relpair *), comp_rel);
295 	qsort(hptr->rsitab, hptr->ridif, sizeof(struct rin *), comp_rin);
296 	qsort(hptr->rsotab, hptr->rodif, sizeof(struct rout *), comp_rout);
297 	qsort(hptr->ssttab, hptr->sdif, sizeof(struct status *), comp_s);
298 	qsort(hptr->sruletab, hptr->rdif, sizeof(struct rule *), comp_r);
299 }
300 
301 /*
302  * Comparing functions, called from sort()
303  */
304 int
comp_env(const void * p1,const void * p2)305 comp_env(const void *p1, const void *p2) {
306 	const struct envpair * sp1 = *(const struct envpair * const *)p1;
307 	const struct envpair * sp2 = *(const struct envpair * const *)p2;
308 
309 	if (sflag) {
310 		if (sp1->size < sp2->size)
311 			return (1);
312 		if (sp1->size > sp2->size)
313 			return (-1);
314 	} else {
315 		if (sp1->num < sp2->num)
316 			return (1);
317 		if (sp1->num > sp2->num)
318 			return (-1);
319 	}
320 	return (0);
321 }
322 int
comp_rel(const void * p1,const void * p2)323 comp_rel(const void *p1, const void *p2) {
324 	const struct relpair * sp1 = *(const struct relpair * const *)p1;
325 	const struct relpair * sp2 = *(const struct relpair * const *)p2;
326 
327 	if (sflag) {
328 		if (sp1->size < sp2->size)
329 			return (1);
330 		if (sp1->size > sp2->size)
331 			return (-1);
332 	} else {
333 		if (sp1->num < sp2->num)
334 			return (1);
335 		if (sp1->num > sp2->num)
336 			return (-1);
337 	}
338 	return (0);
339 }
340 int
comp_in(const void * p1,const void * p2)341 comp_in(const void *p1, const void *p2) {
342 	const struct in * sp1 = *(const struct in * const *)p1;
343 	const struct in * sp2 = *(const struct in * const *)p2;
344 
345 	if (sflag) {
346 		if (sp1->size < sp2->size)
347 			return (1);
348 		if (sp1->size > sp2->size)
349 			return (-1);
350 	} else {
351 		if (sp1->num < sp2->num)
352 			return (1);
353 		if (sp1->num > sp2->num)
354 			return (-1);
355 	}
356 	return (0);
357 }
358 int
comp_rin(const void * p1,const void * p2)359 comp_rin(const void *p1, const void *p2) {
360 	const struct rin * sp1 = *(const struct rin * const *)p1;
361 	const struct rin * sp2 = *(const struct rin * const *)p2;
362 
363 	if (sflag) {
364 		if (sp1->size < sp2->size)
365 			return (1);
366 		if (sp1->size > sp2->size)
367 			return (-1);
368 	} else {
369 		if (sp1->num < sp2->num)
370 			return (1);
371 		if (sp1->num > sp2->num)
372 			return (-1);
373 	}
374 	return (0);
375 }
376 
377 int
comp_out(const void * p1,const void * p2)378 comp_out(const void *p1, const void *p2) {
379 	const struct out * sp1 = *(const struct out * const *)p1;
380 	const struct out * sp2 = *(const struct out * const *)p2;
381 
382 	if (sflag) {
383 		if (sp1->size < sp2->size)
384 			return (1);
385 		if (sp1->size > sp2->size)
386 			return (-1);
387 	} else {
388 		if (sp1->num < sp2->num)
389 			return (1);
390 		if (sp1->num > sp2->num)
391 			return (-1);
392 	}
393 	return (0);
394 }
395 int
comp_rout(const void * p1,const void * p2)396 comp_rout(const void *p1, const void *p2) {
397 	const struct rout * sp1 = *(const struct rout * const *)p1;
398 	const struct rout * sp2 = *(const struct rout * const *)p2;
399 
400 	if (sflag) {
401 		if (sp1->size < sp2->size)
402 			return (1);
403 		if (sp1->size > sp2->size)
404 			return (-1);
405 	} else {
406 		if (sp1->num < sp2->num)
407 			return (1);
408 		if (sp1->num > sp2->num)
409 			return (-1);
410 	}
411 	return (0);
412 }
413 int
comp_s(const void * p1,const void * p2)414 comp_s(const void *p1, const void *p2) {
415 	const struct status * sp1 = *(const struct status * const *)p1;
416 	const struct status * sp2 = *(const struct status * const *)p2;
417 
418 	if (sp1->num < sp2->num)
419 		return (1);
420 	if (sp1->num > sp2->num)
421 		return (-1);
422 	return (0);
423 }
424 int
comp_r(const void * p1,const void * p2)425 comp_r(const void *p1, const void *p2) {
426 	const struct rule * sp1 = *(const struct rule * const *)p1;
427 	const struct rule * sp2 = *(const struct rule * const *)p2;
428 
429 	if (sp1->num < sp2->num)
430 		return (1);
431 	if (sp1->num > sp2->num)
432 		return (-1);
433 	return (0);
434 }
435 int
comp_rrel(const void * p1,const void * p2)436 comp_rrel(const void *p1, const void *p2) {
437 	const struct rrelay * sp1 = *(const struct rrelay * const *)p1;
438 	const struct rrelay * sp2 = *(const struct rrelay * const *)p2;
439 
440 	if (sp1->num < sp2->num)
441 		return (1);
442 	if (sp1->num > sp2->num)
443 		return (-1);
444 	return (0);
445 }
446 
447 
448 /*
449  * Calculate the average daily/hour messages
450  */
451 void
average(struct host * hptr,int * dd,float * ad,int * hh,float * ah)452 average(struct host *hptr, int *dd, float *ad, int *hh, float *ah) {
453 	int fulld;
454 	int fullh;
455 	int weeks;
456 	int hours;
457 	int hfday, hfhour;
458 	int i;
459 
460 	hfday = hptr->fday;
461 	hfhour = hptr->fhour;
462 
463 	/* integer value of hours: */
464 	fullh = (int)hptr->dtime/3600;
465 	fullh++;
466 
467 	/* integer value of days: */
468 	fulld = (int)hptr->dtime/(24*3600);
469 	fulld++;
470 
471 	/*
472 	 * calculate full weeks:
473 	 *
474 	 * days left over from last full week are returned
475 	 * as int fulld:
476  	 */
477 	weeks = 0;
478 	for (; fulld >= 7; fulld -= 7)
479 		weeks++;
480 
481 	/* calculate daily averages: */
482 
483 	for (i = 0; i < 7; i++)
484 			if (i == hfday && fulld > 0) {
485 				ad[i] = (float)dd[i] / ((float)weeks + 1.0);
486 				fulld--;
487 				hfday++;
488 			} else
489 				if (weeks)
490 					ad[i] = (float)dd[i] / (float)weeks;
491 				else
492 					ad[i] = (float)dd[i];
493 
494 
495 	/* same calculations for hours: */
496 
497 	hours = 0;
498 	for (; fullh >= 24; fullh -= 24)
499 		hours++;
500 	for (i = 0; i < 24; i++)
501 			if (i == hfhour && fullh > 0) {
502 				ah[i] = (float)hh[i] / ((float)hours + 1.0);
503 				fullh--;
504 				hfhour++;
505 			} else
506 				if (hours)
507 					ah[i] = (float)hh[i] / (float)hours;
508 				else
509 					ah[i] = (float)hh[i];
510 
511 }
512 
513 /* Strip newline: */
514 
515 char *
stripn(char * s)516 stripn(char *s) {
517 	char *p;
518 
519 	for (p = s; *s != '\n'; s++)
520 		;
521 	*s = '\0';
522 	return p;
523 }
524 
525 void
error_memory()526 error_memory() {
527 	fprintf(stderr, "%s: memory allocation failure, errno: %d\n",
528 		pname, errno);
529 	exit(1);
530 }
531 
532 void
copying()533 copying() {
534 	fprintf(stderr, "\nSMA version %s\n\n" , VERSION);
535 	fprintf(stderr, "This program is Copyright (c) 2000 - 2003\n"
536 			"      Jarkko Turkulainen. All rights reserved.\n"
537 			"Some parts are "
538 			"Copyright (c) 1992, 1993, 1994\n"
539 			"      Henry Spencer.  All rights reserved.\n"
540 			"Some parts are "
541 			"Copyright (c) 1985, 1987, 1988, 1993, 1994\n"
542 			"      The Regents of the University of California. "
543 			"All rights reserved.\n\n");
544 	exit(0);
545 }
546 
547 void
usage()548 usage() {
549 	/* Use stdout - easier to "more" or "less" .. */
550 	fprintf(stdout, "\nSMA version %s\n\n" , VERSION);
551 	fprintf(stdout, "usage: %s [OPTIONS] [files...]\n"
552 	"OPTIONS are\n"
553 	"    -A\t\tforce addresses to lower case to help with consistent counts\n"
554 	"    -a\t\tset the output format as ascii\n"
555 	"    -b <rgb>\tbackground color (RGB) of the HTML form\n"
556 	"    -c\t\tshow copyright information and exit\n"
557 	"    -C <string>\tset report header as <string>\n"
558 	"    -D <d1,d2>\tanalyse only log entries between dates d1 and d2\n"
559 	"    -d\t\tprint only the domain portion of email addresses\n"
560 	"    -f <file>\tuse configuration from <file> instead of default\n"
561 	"    -F\t\tdo not use default configuration file even if it exists\n"
562 	"    -h\t\tprint this text and exit\n"
563 	"    -H <name>\toverride the hostname with <name>\n"
564 	"    -i\t\tprint ASCII report as HTML comment (requires -w or -O html)\n"
565 	"    -L <string>\tprocess only lines with syslog tag <string>\n"
566 	"    -n\t\tdo not print the time distribution\n"
567 	"    -o <file>\twrite output to <file>\n"
568 	"    -O <format>\tset output format (ascii/html/clog)\n"
569 	"    -p\t\tprint current configuration to stdout\n"
570 	"    -s\t\tsort by tranfers\n"
571 	"    -q\t\tbe quiet (no error messages)\n"
572 	"    -v\t\tshow debugging information\n"
573 	"    -l <num>\tprint <num> top addresses\n"
574 	"    -r <num>\tprint <num> top relay addresses\n"
575 	"    -t <value>\tset the internal hash table size (normal/big/huge/a,r)\n"
576 	"    -w\t\tset the output format as html\n"
577 	"    files\tlog files\n\n", pname);
578 	exit(0);
579 }
580 
581 void
dump_config(FILE * handle)582 dump_config(FILE *handle) {
583 	char *p = NULL;
584 	char dates[128];
585 
586 	fprintf(handle, "  BgColor %s\n", bchar);
587 	fprintf(handle, "  BounceAddress %s\n", bechar);
588 	fprintf(handle, "  CaseSensitive %s\n", csflag ? "no" : "yes");
589 	if (cfchar)
590 		fprintf(handle, "  ClogFormat \"%s\"\n", cfchar);
591 	fprintf(handle, "  ClogSentOnly %s\n", clsflag ? "yes" : "no");
592 	fprintf(handle, "  Comment \"%s\"\n", Cchar);
593 	fprintf(handle, "  Debug %s\n", vflag ? "yes" : "no");
594 	if (eetime) {
595 		strftime(dates, 127, "%Y/%m/%d-%H:%M:%S", localtime(&eetime));
596 		fprintf(handle, "  EndTime %s\n", dates);
597 	}
598 	fprintf(handle, "  EnvelopePairs %d\n", epnum);
599 	fprintf(handle, "  EnvelopeRecipientFilter %s\n", ref ? ref : "*");
600 	fprintf(handle, "  EnvelopeRecipients %d\n", lrnum);
601 	fprintf(handle, "  EnvelopeSenderFilter %s\n", sef ? sef : "*");
602 	fprintf(handle, "  EnvelopeSenders %d\n", lnum);
603 	if (ftchar)
604 		fprintf(handle, "  FooterText \"%s\"\n", ftchar);
605 	switch(Oflag) {
606 		case FORMAT_ASCII:
607 			if (!(p = strdup("ascii")))
608 				error_memory();
609 			break;
610 		case FORMAT_HTML:
611 			if (!(p = strdup("html")))
612 				error_memory();
613 			break;
614 		case FORMAT_CLOG:
615 			if (!(p = strdup("clog")))
616 				error_memory();
617 			break;
618 	}
619 	fprintf(handle, "  Format %s\n", p);
620 	free(p);
621 	fprintf(handle, "  HashTables %d,%d\n", asize, rsize);
622 	if (htchar)
623 		fprintf(handle, "  HeaderText \"%s\"\n", htchar);
624 	if (Hchar)
625 		fprintf(handle, "  HostName \"%s\"\n", Hchar);
626 	fprintf(handle, "  IncludeAscii %s\n", iflag ? "yes" : "no");
627 	if (ochar)
628 		fprintf(handle, "  OutFile \"%s\"\n", ochar);
629 	if (plchar)
630 		fprintf(handle, "  PictureLink \"%s\"\n", plchar);
631 	if (ppchar)
632 		fprintf(handle, "  PictureParameters \"%s\"\n", ppchar);
633 	if (pachar)
634 	fprintf(handle, "  PictureURL %s\n", pachar);
635 	fprintf(handle, "  PrintGeneralInfo %s\n", pgflag ? "yes" : "no");
636 	fprintf(handle, "  PrintRuleset %s\n", rsnum ? "yes" : "no");
637 	fprintf(handle, "  PrintStatus %s\n", stnum ? "yes" : "no");
638 	fprintf(handle, "  RelayPairs %d\n", rpnum);
639 	fprintf(handle, "  RelayRecipientFilter %s\n", rrf ? rrf : "*");
640 	fprintf(handle, "  RelayRecipients %d\n", rrnum);
641 	fprintf(handle, "  RelaySenderFilter %s\n", srf ? srf : "*");
642 	fprintf(handle, "  RelaySenders %d\n", rnum);
643 	fprintf(handle, "  RulesetRelays %d\n", rsrnum);
644 	fprintf(handle, "  ShowUsers %s\n", dflag ? "no" : "yes");
645 	fprintf(handle, "  Silent %s\n", qflag ? "yes" : "no");
646 	fprintf(handle, "  Sorting %s\n", sflag ? "transfer" : "number");
647 	if (sstime) {
648 		strftime(dates, 127, "%Y/%m/%d-%H:%M:%S", localtime(&sstime));
649 		fprintf(handle, "  StartTime %s\n", dates);
650 	}
651 	if (Lchar)
652 		fprintf(handle, "  SyslogTag %s\n", Lchar);
653 	fprintf(handle, "  TbColor %s\n", tbchar);
654 }
655