xref: /original-bsd/usr.sbin/sendmail/src/readcf.c (revision f7b88020)
1 /*
2  * Copyright (c) 1983, 1995 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)readcf.c	8.92 (Berkeley) 05/25/95";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include <grp.h>
15 #if NAMED_BIND
16 # include <resolv.h>
17 #endif
18 
19 /*
20 **  READCF -- read control file.
21 **
22 **	This routine reads the control file and builds the internal
23 **	form.
24 **
25 **	The file is formatted as a sequence of lines, each taken
26 **	atomically.  The first character of each line describes how
27 **	the line is to be interpreted.  The lines are:
28 **		Dxval		Define macro x to have value val.
29 **		Cxword		Put word into class x.
30 **		Fxfile [fmt]	Read file for lines to put into
31 **				class x.  Use scanf string 'fmt'
32 **				or "%s" if not present.  Fmt should
33 **				only produce one string-valued result.
34 **		Hname: value	Define header with field-name 'name'
35 **				and value as specified; this will be
36 **				macro expanded immediately before
37 **				use.
38 **		Sn		Use rewriting set n.
39 **		Rlhs rhs	Rewrite addresses that match lhs to
40 **				be rhs.
41 **		Mn arg=val...	Define mailer.  n is the internal name.
42 **				Args specify mailer parameters.
43 **		Oxvalue		Set option x to value.
44 **		Pname=value	Set precedence name to value.
45 **		Vversioncode[/vendorcode]
46 **				Version level/vendor name of
47 **				configuration syntax.
48 **		Kmapname mapclass arguments....
49 **				Define keyed lookup of a given class.
50 **				Arguments are class dependent.
51 **		Eenvar=value	Set the environment value to the given value.
52 **
53 **	Parameters:
54 **		cfname -- control file name.
55 **		safe -- TRUE if this is the system config file;
56 **			FALSE otherwise.
57 **		e -- the main envelope.
58 **
59 **	Returns:
60 **		none.
61 **
62 **	Side Effects:
63 **		Builds several internal tables.
64 */
65 
66 readcf(cfname, safe, e)
67 	char *cfname;
68 	bool safe;
69 	register ENVELOPE *e;
70 {
71 	FILE *cf;
72 	int ruleset = 0;
73 	int nextruleset = MAXRWSETS;
74 	char *q;
75 	struct rewrite *rwp = NULL;
76 	char *bp;
77 	auto char *ep;
78 	int nfuzzy;
79 	char *file;
80 	bool optional;
81 	int mid;
82 	char buf[MAXLINE];
83 	register char *p;
84 	extern char **copyplist();
85 	struct stat statb;
86 	char exbuf[MAXLINE];
87 	char pvpbuf[MAXLINE + MAXATOM];
88 	static char *null_list[1] = { NULL };
89 	extern char *munchstring();
90 	extern void makemapentry();
91 
92 	FileName = cfname;
93 	LineNumber = 0;
94 
95 	cf = fopen(cfname, "r");
96 	if (cf == NULL)
97 	{
98 		syserr("cannot open");
99 		exit(EX_OSFILE);
100 	}
101 
102 	if (fstat(fileno(cf), &statb) < 0)
103 	{
104 		syserr("cannot fstat");
105 		exit(EX_OSFILE);
106 	}
107 
108 	if (!S_ISREG(statb.st_mode))
109 	{
110 		syserr("not a plain file");
111 		exit(EX_OSFILE);
112 	}
113 
114 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
115 	{
116 		if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
117 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
118 				FileName);
119 #ifdef LOG
120 		if (LogLevel > 0)
121 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
122 				FileName);
123 #endif
124 	}
125 
126 #ifdef XLA
127 	xla_zero();
128 #endif
129 
130 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
131 	{
132 		if (bp[0] == '#')
133 		{
134 			if (bp != buf)
135 				free(bp);
136 			continue;
137 		}
138 
139 		/* do macro expansion mappings */
140 		for (p = bp; *p != '\0'; p++)
141 		{
142 			if (*p == '#' && p > bp && ConfigLevel >= 3)
143 			{
144 				/* this is an on-line comment */
145 				register char *e;
146 
147 				switch (*--p & 0377)
148 				{
149 				  case MACROEXPAND:
150 					/* it's from $# -- let it go through */
151 					p++;
152 					break;
153 
154 				  case '\\':
155 					/* it's backslash escaped */
156 					(void) strcpy(p, p + 1);
157 					break;
158 
159 				  default:
160 					/* delete preceeding white space */
161 					while (isascii(*p) && isspace(*p) && p > bp)
162 						p--;
163 					if ((e = strchr(++p, '\n')) != NULL)
164 						(void) strcpy(p, e);
165 					else
166 						p[0] = p[1] = '\0';
167 					break;
168 				}
169 				continue;
170 			}
171 
172 			if (*p != '$' || p[1] == '\0')
173 				continue;
174 
175 			if (p[1] == '$')
176 			{
177 				/* actual dollar sign.... */
178 				(void) strcpy(p, p + 1);
179 				continue;
180 			}
181 
182 			/* convert to macro expansion character */
183 			*p++ = MACROEXPAND;
184 
185 			/* convert macro name to code */
186 			*p = macid(p, &ep);
187 			if (ep != p)
188 				strcpy(p + 1, ep);
189 		}
190 
191 		/* interpret this line */
192 		errno = 0;
193 		switch (bp[0])
194 		{
195 		  case '\0':
196 		  case '#':		/* comment */
197 			break;
198 
199 		  case 'R':		/* rewriting rule */
200 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
201 				continue;
202 
203 			if (*p == '\0')
204 			{
205 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
206 				break;
207 			}
208 
209 			/* allocate space for the rule header */
210 			if (rwp == NULL)
211 			{
212 				RewriteRules[ruleset] = rwp =
213 					(struct rewrite *) xalloc(sizeof *rwp);
214 			}
215 			else
216 			{
217 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
218 				rwp = rwp->r_next;
219 			}
220 			rwp->r_next = NULL;
221 
222 			/* expand and save the LHS */
223 			*p = '\0';
224 			expand(&bp[1], exbuf, sizeof exbuf, e);
225 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
226 					     sizeof pvpbuf, NULL, NULL);
227 			nfuzzy = 0;
228 			if (rwp->r_lhs != NULL)
229 			{
230 				register char **ap;
231 
232 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
233 
234 				/* count the number of fuzzy matches in LHS */
235 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
236 				{
237 					char *botch;
238 
239 					botch = NULL;
240 					switch (**ap & 0377)
241 					{
242 					  case MATCHZANY:
243 					  case MATCHANY:
244 					  case MATCHONE:
245 					  case MATCHCLASS:
246 					  case MATCHNCLASS:
247 						nfuzzy++;
248 						break;
249 
250 					  case MATCHREPL:
251 						botch = "$0-$9";
252 						break;
253 
254 					  case CANONNET:
255 						botch = "$#";
256 						break;
257 
258 					  case CANONUSER:
259 						botch = "$:";
260 						break;
261 
262 					  case CALLSUBR:
263 						botch = "$>";
264 						break;
265 
266 					  case CONDIF:
267 						botch = "$?";
268 						break;
269 
270 					  case CONDELSE:
271 						botch = "$|";
272 						break;
273 
274 					  case CONDFI:
275 						botch = "$.";
276 						break;
277 
278 					  case HOSTBEGIN:
279 						botch = "$[";
280 						break;
281 
282 					  case HOSTEND:
283 						botch = "$]";
284 						break;
285 
286 					  case LOOKUPBEGIN:
287 						botch = "$(";
288 						break;
289 
290 					  case LOOKUPEND:
291 						botch = "$)";
292 						break;
293 					}
294 					if (botch != NULL)
295 						syserr("Inappropriate use of %s on LHS",
296 							botch);
297 				}
298 			}
299 			else
300 			{
301 				syserr("R line: null LHS");
302 				rwp->r_lhs = null_list;
303 			}
304 
305 			/* expand and save the RHS */
306 			while (*++p == '\t')
307 				continue;
308 			q = p;
309 			while (*p != '\0' && *p != '\t')
310 				p++;
311 			*p = '\0';
312 			expand(q, exbuf, sizeof exbuf, e);
313 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
314 					     sizeof pvpbuf, NULL, NULL);
315 			if (rwp->r_rhs != NULL)
316 			{
317 				register char **ap;
318 
319 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
320 
321 				/* check no out-of-bounds replacements */
322 				nfuzzy += '0';
323 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
324 				{
325 					char *botch;
326 
327 					botch = NULL;
328 					switch (**ap & 0377)
329 					{
330 					  case MATCHREPL:
331 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
332 						{
333 							syserr("replacement $%c out of bounds",
334 								(*ap)[1]);
335 						}
336 						break;
337 
338 					  case MATCHZANY:
339 						botch = "$*";
340 						break;
341 
342 					  case MATCHANY:
343 						botch = "$+";
344 						break;
345 
346 					  case MATCHONE:
347 						botch = "$-";
348 						break;
349 
350 					  case MATCHCLASS:
351 						botch = "$=";
352 						break;
353 
354 					  case MATCHNCLASS:
355 						botch = "$~";
356 						break;
357 					}
358 					if (botch != NULL)
359 						syserr("Inappropriate use of %s on RHS",
360 							botch);
361 				}
362 			}
363 			else
364 			{
365 				syserr("R line: null RHS");
366 				rwp->r_rhs = null_list;
367 			}
368 			break;
369 
370 		  case 'S':		/* select rewriting set */
371 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
372 				continue;
373 			if (!isascii(*p))
374 			{
375 				syserr("invalid argument to S line: \"%.20s\"",
376 					&bp[1]);
377 				break;
378 			}
379 			if (isdigit(*p))
380 			{
381 				ruleset = atoi(p);
382 				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
383 				{
384 					syserr("bad ruleset %d (%d max)",
385 						ruleset, MAXRWSETS / 2);
386 					ruleset = 0;
387 				}
388 			}
389 			else
390 			{
391 				STAB *s;
392 				char delim;
393 
394 				q = p;
395 				while (*p != '\0' && isascii(*p) &&
396 				       (isalnum(*p) || strchr("-_$", *p) != NULL))
397 					p++;
398 				while (isascii(*p) && isspace(*p))
399 					*p++ = '\0';
400 				delim = *p;
401 				if (delim != '\0')
402 					*p++ = '\0';
403 				s = stab(q, ST_RULESET, ST_ENTER);
404 				if (s->s_ruleset != 0)
405 					ruleset = s->s_ruleset;
406 				else if (delim == '=')
407 				{
408 					ruleset = atoi(p);
409 					if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
410 					{
411 						syserr("bad ruleset %s = %d (%d max)",
412 							q, ruleset, MAXRWSETS / 2);
413 						ruleset = 0;
414 					}
415 				}
416 				else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
417 				{
418 					syserr("%s: too many named rulesets (%d max)",
419 						q, MAXRWSETS / 2);
420 					ruleset = 0;
421 				}
422 				s->s_ruleset = ruleset;
423 			}
424 			rwp = NULL;
425 			break;
426 
427 		  case 'D':		/* macro definition */
428 			mid = macid(&bp[1], &ep);
429 			p = munchstring(ep, NULL);
430 			define(mid, newstr(p), e);
431 			break;
432 
433 		  case 'H':		/* required header line */
434 			(void) chompheader(&bp[1], TRUE, NULL, e);
435 			break;
436 
437 		  case 'C':		/* word class */
438 		  case 'T':		/* trusted user (set class `t') */
439 			if (bp[0] == 'C')
440 			{
441 				mid = macid(&bp[1], &ep);
442 				expand(ep, exbuf, sizeof exbuf, e);
443 				p = exbuf;
444 			}
445 			else
446 			{
447 				mid = 't';
448 				p = &bp[1];
449 			}
450 			while (*p != '\0')
451 			{
452 				register char *wd;
453 				char delim;
454 
455 				while (*p != '\0' && isascii(*p) && isspace(*p))
456 					p++;
457 				wd = p;
458 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
459 					p++;
460 				delim = *p;
461 				*p = '\0';
462 				if (wd[0] != '\0')
463 					setclass(mid, wd);
464 				*p = delim;
465 			}
466 			break;
467 
468 		  case 'F':		/* word class from file */
469 			mid = macid(&bp[1], &ep);
470 			for (p = ep; isascii(*p) && isspace(*p); )
471 				p++;
472 			if (p[0] == '-' && p[1] == 'o')
473 			{
474 				optional = TRUE;
475 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
476 					p++;
477 				while (isascii(*p) && isspace(*p))
478 					p++;
479 			}
480 			else
481 				optional = FALSE;
482 			file = p;
483 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
484 				p++;
485 			if (*p == '\0')
486 				p = "%s";
487 			else
488 			{
489 				*p = '\0';
490 				while (isascii(*++p) && isspace(*p))
491 					continue;
492 			}
493 			fileclass(bp[1], file, p, safe, optional);
494 			break;
495 
496 #ifdef XLA
497 		  case 'L':		/* extended load average description */
498 			xla_init(&bp[1]);
499 			break;
500 #endif
501 
502 		  case 'M':		/* define mailer */
503 			makemailer(&bp[1]);
504 			break;
505 
506 		  case 'O':		/* set option */
507 			setoption(bp[1], &bp[2], safe, FALSE, e);
508 			break;
509 
510 		  case 'P':		/* set precedence */
511 			if (NumPriorities >= MAXPRIORITIES)
512 			{
513 				toomany('P', MAXPRIORITIES);
514 				break;
515 			}
516 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
517 				continue;
518 			if (*p == '\0')
519 				goto badline;
520 			*p = '\0';
521 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
522 			Priorities[NumPriorities].pri_val = atoi(++p);
523 			NumPriorities++;
524 			break;
525 
526 		  case 'V':		/* configuration syntax version */
527 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
528 				continue;
529 			if (!isascii(*p) || !isdigit(*p))
530 			{
531 				syserr("invalid argument to V line: \"%.20s\"",
532 					&bp[1]);
533 				break;
534 			}
535 			ConfigLevel = strtol(p, &ep, 10);
536 
537 			/*
538 			**  Do heuristic tweaking for back compatibility.
539 			*/
540 
541 			if (ConfigLevel >= 5)
542 			{
543 				/* level 5 configs have short name in $w */
544 				p = macvalue('w', e);
545 				if (p != NULL && (p = strchr(p, '.')) != NULL)
546 					*p = '\0';
547 			}
548 			if (ConfigLevel >= 6)
549 			{
550 				ColonOkInAddr = FALSE;
551 			}
552 
553 			/*
554 			**  Look for vendor code.
555 			*/
556 
557 			if (*ep++ == '/')
558 			{
559 				/* extract vendor code */
560 				for (p = ep; isascii(*p) && isalpha(*p); )
561 					p++;
562 				*p = '\0';
563 
564 				if (!setvendor(ep))
565 					syserr("invalid V line vendor code: \"%s\"",
566 						ep);
567 			}
568 			break;
569 
570 		  case 'K':
571 			makemapentry(&bp[1]);
572 			break;
573 
574 		  case 'E':
575 			p = strchr(bp, '=');
576 			if (p != NULL)
577 				*p++ = '\0';
578 			setuserenv(&bp[1], p);
579 			break;
580 
581 		  default:
582 		  badline:
583 			syserr("unknown control line \"%s\"", bp);
584 		}
585 		if (bp != buf)
586 			free(bp);
587 	}
588 	if (ferror(cf))
589 	{
590 		syserr("I/O read error", cfname);
591 		exit(EX_OSFILE);
592 	}
593 	fclose(cf);
594 	FileName = NULL;
595 
596 	/* initialize host maps from local service tables */
597 	inithostmaps();
598 
599 	/* determine if we need to do special name-server frotz */
600 	{
601 		int nmaps;
602 		char *maptype[MAXMAPSTACK];
603 		short mapreturn[MAXMAPACTIONS];
604 
605 		nmaps = switch_map_find("hosts", maptype, mapreturn);
606 		UseNameServer = FALSE;
607 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
608 		{
609 			register int mapno;
610 
611 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
612 			{
613 				if (strcmp(maptype[mapno], "dns") == 0)
614 					UseNameServer = TRUE;
615 			}
616 		}
617 
618 #ifdef HESIOD
619 		nmaps = switch_map_find("passwd", maptype, mapreturn);
620 		UseHesiod = FALSE;
621 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
622 		{
623 			register int mapno;
624 
625 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
626 			{
627 				if (strcmp(maptype[mapno], "hesiod") == 0)
628 					UseHesiod = TRUE;
629 			}
630 		}
631 #endif
632 	}
633 }
634 /*
635 **  TOOMANY -- signal too many of some option
636 **
637 **	Parameters:
638 **		id -- the id of the error line
639 **		maxcnt -- the maximum possible values
640 **
641 **	Returns:
642 **		none.
643 **
644 **	Side Effects:
645 **		gives a syserr.
646 */
647 
648 toomany(id, maxcnt)
649 	char id;
650 	int maxcnt;
651 {
652 	syserr("too many %c lines, %d max", id, maxcnt);
653 }
654 /*
655 **  FILECLASS -- read members of a class from a file
656 **
657 **	Parameters:
658 **		class -- class to define.
659 **		filename -- name of file to read.
660 **		fmt -- scanf string to use for match.
661 **		safe -- if set, this is a safe read.
662 **		optional -- if set, it is not an error for the file to
663 **			not exist.
664 **
665 **	Returns:
666 **		none
667 **
668 **	Side Effects:
669 **
670 **		puts all lines in filename that match a scanf into
671 **			the named class.
672 */
673 
674 fileclass(class, filename, fmt, safe, optional)
675 	int class;
676 	char *filename;
677 	char *fmt;
678 	bool safe;
679 	bool optional;
680 {
681 	FILE *f;
682 	int sff;
683 	int pid;
684 	register char *p;
685 	char buf[MAXLINE];
686 
687 	if (tTd(37, 2))
688 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
689 
690 	if (filename[0] == '|')
691 	{
692 		auto int fd;
693 		int i;
694 		char *argv[MAXPV + 1];
695 
696 		i = 0;
697 		for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
698 		{
699 			if (i >= MAXPV)
700 				break;
701 			argv[i++] = p;
702 		}
703 		argv[i] = NULL;
704 		pid = prog_open(argv, &fd, CurEnv);
705 		if (pid < 0)
706 			f = NULL;
707 		else
708 			f = fdopen(fd, "r");
709 	}
710 	else
711 	{
712 		pid = -1;
713 		sff = SFF_REGONLY;
714 		if (safe)
715 			sff |= SFF_OPENASROOT;
716 		f = safefopen(filename, O_RDONLY, 0, sff);
717 	}
718 	if (f == NULL)
719 	{
720 		if (!optional)
721 			syserr("fileclass: cannot open %s", filename);
722 		return;
723 	}
724 
725 	while (fgets(buf, sizeof buf, f) != NULL)
726 	{
727 		register STAB *s;
728 		register char *p;
729 # ifdef SCANF
730 		char wordbuf[MAXNAME+1];
731 
732 		if (sscanf(buf, fmt, wordbuf) != 1)
733 			continue;
734 		p = wordbuf;
735 # else /* SCANF */
736 		p = buf;
737 # endif /* SCANF */
738 
739 		/*
740 		**  Break up the match into words.
741 		*/
742 
743 		while (*p != '\0')
744 		{
745 			register char *q;
746 
747 			/* strip leading spaces */
748 			while (isascii(*p) && isspace(*p))
749 				p++;
750 			if (*p == '\0')
751 				break;
752 
753 			/* find the end of the word */
754 			q = p;
755 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
756 				p++;
757 			if (*p != '\0')
758 				*p++ = '\0';
759 
760 			/* enter the word in the symbol table */
761 			setclass(class, q);
762 		}
763 	}
764 
765 	(void) fclose(f);
766 	if (pid > 0)
767 		(void) waitfor(pid);
768 }
769 /*
770 **  MAKEMAILER -- define a new mailer.
771 **
772 **	Parameters:
773 **		line -- description of mailer.  This is in labeled
774 **			fields.  The fields are:
775 **			   A -- the argv for this mailer
776 **			   C -- the character set for MIME conversions
777 **			   D -- the directory to run in
778 **			   E -- the eol string
779 **			   F -- the flags associated with the mailer
780 **			   L -- the maximum line length
781 **			   M -- the maximum message size
782 **			   N -- the niceness at which to run
783 **			   P -- the path to the mailer
784 **			   R -- the recipient rewriting set
785 **			   S -- the sender rewriting set
786 **			   T -- the mailer type (for DSNs)
787 **			   U -- the uid to run as
788 **			The first word is the canonical name of the mailer.
789 **
790 **	Returns:
791 **		none.
792 **
793 **	Side Effects:
794 **		enters the mailer into the mailer table.
795 */
796 
797 makemailer(line)
798 	char *line;
799 {
800 	register char *p;
801 	register struct mailer *m;
802 	register STAB *s;
803 	int i;
804 	char fcode;
805 	auto char *endp;
806 	extern int NextMailer;
807 	extern char **makeargv();
808 	extern char *munchstring();
809 
810 	/* allocate a mailer and set up defaults */
811 	m = (struct mailer *) xalloc(sizeof *m);
812 	bzero((char *) m, sizeof *m);
813 	m->m_eol = "\n";
814 	m->m_uid = m->m_gid = 0;
815 
816 	/* collect the mailer name */
817 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
818 		continue;
819 	if (*p != '\0')
820 		*p++ = '\0';
821 	m->m_name = newstr(line);
822 
823 	/* now scan through and assign info from the fields */
824 	while (*p != '\0')
825 	{
826 		auto char *delimptr;
827 
828 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
829 			p++;
830 
831 		/* p now points to field code */
832 		fcode = *p;
833 		while (*p != '\0' && *p != '=' && *p != ',')
834 			p++;
835 		if (*p++ != '=')
836 		{
837 			syserr("mailer %s: `=' expected", m->m_name);
838 			return;
839 		}
840 		while (isascii(*p) && isspace(*p))
841 			p++;
842 
843 		/* p now points to the field body */
844 		p = munchstring(p, &delimptr);
845 
846 		/* install the field into the mailer struct */
847 		switch (fcode)
848 		{
849 		  case 'P':		/* pathname */
850 			m->m_mailer = newstr(p);
851 			break;
852 
853 		  case 'F':		/* flags */
854 			for (; *p != '\0'; p++)
855 				if (!(isascii(*p) && isspace(*p)))
856 					setbitn(*p, m->m_flags);
857 			break;
858 
859 		  case 'S':		/* sender rewriting ruleset */
860 		  case 'R':		/* recipient rewriting ruleset */
861 			i = strtol(p, &endp, 10);
862 			if (i < 0 || i >= MAXRWSETS)
863 			{
864 				syserr("invalid rewrite set, %d max", MAXRWSETS);
865 				return;
866 			}
867 			if (fcode == 'S')
868 				m->m_sh_rwset = m->m_se_rwset = i;
869 			else
870 				m->m_rh_rwset = m->m_re_rwset = i;
871 
872 			p = endp;
873 			if (*p++ == '/')
874 			{
875 				i = strtol(p, NULL, 10);
876 				if (i < 0 || i >= MAXRWSETS)
877 				{
878 					syserr("invalid rewrite set, %d max",
879 						MAXRWSETS);
880 					return;
881 				}
882 				if (fcode == 'S')
883 					m->m_sh_rwset = i;
884 				else
885 					m->m_rh_rwset = i;
886 			}
887 			break;
888 
889 		  case 'E':		/* end of line string */
890 			m->m_eol = newstr(p);
891 			break;
892 
893 		  case 'A':		/* argument vector */
894 			m->m_argv = makeargv(p);
895 			break;
896 
897 		  case 'M':		/* maximum message size */
898 			m->m_maxsize = atol(p);
899 			break;
900 
901 		  case 'L':		/* maximum line length */
902 			m->m_linelimit = atoi(p);
903 			break;
904 
905 		  case 'N':		/* run niceness */
906 			m->m_nice = atoi(p);
907 			break;
908 
909 		  case 'D':		/* working directory */
910 			m->m_execdir = newstr(p);
911 			break;
912 
913 		  case 'C':		/* default charset */
914 			m->m_defcharset = newstr(p);
915 			break;
916 
917 		  case 'T':		/* MTA Type */
918 			m->m_mtatype = newstr(p);
919 			p = strchr(m->m_mtatype, '/');
920 			if (p != NULL)
921 			{
922 				*p++ = '\0';
923 				if (*p == '\0')
924 					p = NULL;
925 			}
926 			if (p == NULL)
927 				m->m_addrtype = m->m_mtatype;
928 			else
929 			{
930 				m->m_addrtype = p;
931 				p = strchr(p, '/');
932 			}
933 			if (p != NULL)
934 			{
935 				*p++ = '\0';
936 				if (*p == '\0')
937 					p = NULL;
938 			}
939 			if (p == NULL)
940 				m->m_diagtype = m->m_mtatype;
941 			else
942 				m->m_diagtype = p;
943 			break;
944 
945 		  case 'U':		/* user id */
946 			if (isascii(*p) && !isdigit(*p))
947 			{
948 				char *q = p;
949 				struct passwd *pw;
950 
951 				while (isascii(*p) && isalnum(*p))
952 					p++;
953 				while (isascii(*p) && isspace(*p))
954 					*p++ = '\0';
955 				if (*p != '\0')
956 					*p++ = '\0';
957 				pw = sm_getpwnam(q);
958 				if (pw == NULL)
959 					syserr("readcf: mailer U= flag: unknown user %s", q);
960 				else
961 				{
962 					m->m_uid = pw->pw_uid;
963 					m->m_gid = pw->pw_gid;
964 				}
965 			}
966 			else
967 			{
968 				auto char *q;
969 
970 				m->m_uid = strtol(p, &q, 0);
971 				p = q;
972 			}
973 			while (isascii(*p) && isspace(*p))
974 				p++;
975 			if (*p == '\0')
976 				break;
977 			if (isascii(*p) && !isdigit(*p))
978 			{
979 				char *q = p;
980 				struct group *gr;
981 
982 				while (isascii(*p) && isalnum(*p))
983 					p++;
984 				*p++ = '\0';
985 				gr = getgrnam(q);
986 				if (gr == NULL)
987 					syserr("readcf: mailer U= flag: unknown group %s", q);
988 				else
989 					m->m_gid = gr->gr_gid;
990 			}
991 			else
992 			{
993 				m->m_gid = strtol(p, NULL, 0);
994 			}
995 			break;
996 		}
997 
998 		p = delimptr;
999 	}
1000 
1001 	/* do some rationality checking */
1002 	if (m->m_argv == NULL)
1003 	{
1004 		syserr("M%s: A= argument required", m->m_name);
1005 		return;
1006 	}
1007 	if (m->m_mailer == NULL)
1008 	{
1009 		syserr("M%s: P= argument required", m->m_name);
1010 		return;
1011 	}
1012 
1013 	if (NextMailer >= MAXMAILERS)
1014 	{
1015 		syserr("too many mailers defined (%d max)", MAXMAILERS);
1016 		return;
1017 	}
1018 
1019 	/* do some heuristic cleanup for back compatibility */
1020 	if (bitnset(M_LIMITS, m->m_flags))
1021 	{
1022 		if (m->m_linelimit == 0)
1023 			m->m_linelimit = SMTPLINELIM;
1024 		if (ConfigLevel < 2)
1025 			setbitn(M_7BITS, m->m_flags);
1026 	}
1027 
1028 	if (ConfigLevel < 6 &&
1029 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
1030 	     strcmp(m->m_mailer, "[TCP]") == 0))
1031 	{
1032 		if (m->m_mtatype == NULL)
1033 			m->m_mtatype = "dns";
1034 		if (m->m_addrtype == NULL)
1035 			m->m_addrtype = "rfc822";
1036 		if (m->m_diagtype == NULL)
1037 			m->m_diagtype = "smtp";
1038 	}
1039 
1040 	/* enter the mailer into the symbol table */
1041 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
1042 	if (s->s_mailer != NULL)
1043 	{
1044 		i = s->s_mailer->m_mno;
1045 		free(s->s_mailer);
1046 	}
1047 	else
1048 	{
1049 		i = NextMailer++;
1050 	}
1051 	Mailer[i] = s->s_mailer = m;
1052 	m->m_mno = i;
1053 }
1054 /*
1055 **  MUNCHSTRING -- translate a string into internal form.
1056 **
1057 **	Parameters:
1058 **		p -- the string to munch.
1059 **		delimptr -- if non-NULL, set to the pointer of the
1060 **			field delimiter character.
1061 **
1062 **	Returns:
1063 **		the munched string.
1064 */
1065 
1066 char *
1067 munchstring(p, delimptr)
1068 	register char *p;
1069 	char **delimptr;
1070 {
1071 	register char *q;
1072 	bool backslash = FALSE;
1073 	bool quotemode = FALSE;
1074 	static char buf[MAXLINE];
1075 
1076 	for (q = buf; *p != '\0'; p++)
1077 	{
1078 		if (backslash)
1079 		{
1080 			/* everything is roughly literal */
1081 			backslash = FALSE;
1082 			switch (*p)
1083 			{
1084 			  case 'r':		/* carriage return */
1085 				*q++ = '\r';
1086 				continue;
1087 
1088 			  case 'n':		/* newline */
1089 				*q++ = '\n';
1090 				continue;
1091 
1092 			  case 'f':		/* form feed */
1093 				*q++ = '\f';
1094 				continue;
1095 
1096 			  case 'b':		/* backspace */
1097 				*q++ = '\b';
1098 				continue;
1099 			}
1100 			*q++ = *p;
1101 		}
1102 		else
1103 		{
1104 			if (*p == '\\')
1105 				backslash = TRUE;
1106 			else if (*p == '"')
1107 				quotemode = !quotemode;
1108 			else if (quotemode || *p != ',')
1109 				*q++ = *p;
1110 			else
1111 				break;
1112 		}
1113 	}
1114 
1115 	if (delimptr != NULL)
1116 		*delimptr = p;
1117 	*q++ = '\0';
1118 	return (buf);
1119 }
1120 /*
1121 **  MAKEARGV -- break up a string into words
1122 **
1123 **	Parameters:
1124 **		p -- the string to break up.
1125 **
1126 **	Returns:
1127 **		a char **argv (dynamically allocated)
1128 **
1129 **	Side Effects:
1130 **		munges p.
1131 */
1132 
1133 char **
1134 makeargv(p)
1135 	register char *p;
1136 {
1137 	char *q;
1138 	int i;
1139 	char **avp;
1140 	char *argv[MAXPV + 1];
1141 
1142 	/* take apart the words */
1143 	i = 0;
1144 	while (*p != '\0' && i < MAXPV)
1145 	{
1146 		q = p;
1147 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1148 			p++;
1149 		while (isascii(*p) && isspace(*p))
1150 			*p++ = '\0';
1151 		argv[i++] = newstr(q);
1152 	}
1153 	argv[i++] = NULL;
1154 
1155 	/* now make a copy of the argv */
1156 	avp = (char **) xalloc(sizeof *avp * i);
1157 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
1158 
1159 	return (avp);
1160 }
1161 /*
1162 **  PRINTRULES -- print rewrite rules (for debugging)
1163 **
1164 **	Parameters:
1165 **		none.
1166 **
1167 **	Returns:
1168 **		none.
1169 **
1170 **	Side Effects:
1171 **		prints rewrite rules.
1172 */
1173 
1174 printrules()
1175 {
1176 	register struct rewrite *rwp;
1177 	register int ruleset;
1178 
1179 	for (ruleset = 0; ruleset < 10; ruleset++)
1180 	{
1181 		if (RewriteRules[ruleset] == NULL)
1182 			continue;
1183 		printf("\n----Rule Set %d:", ruleset);
1184 
1185 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1186 		{
1187 			printf("\nLHS:");
1188 			printav(rwp->r_lhs);
1189 			printf("RHS:");
1190 			printav(rwp->r_rhs);
1191 		}
1192 	}
1193 }
1194 /*
1195 **  PRINTMAILER -- print mailer structure (for debugging)
1196 **
1197 **	Parameters:
1198 **		m -- the mailer to print
1199 **
1200 **	Returns:
1201 **		none.
1202 */
1203 
1204 printmailer(m)
1205 	register MAILER *m;
1206 {
1207 	int j;
1208 
1209 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
1210 		m->m_mno, m->m_name,
1211 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
1212 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
1213 		m->m_uid, m->m_gid);
1214 	for (j = '\0'; j <= '\177'; j++)
1215 		if (bitnset(j, m->m_flags))
1216 			(void) putchar(j);
1217 	printf(" L=%d E=", m->m_linelimit);
1218 	xputs(m->m_eol);
1219 	if (m->m_defcharset != NULL)
1220 		printf(" C=%s", m->m_defcharset);
1221 	printf(" T=%s/%s/%s",
1222 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
1223 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
1224 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
1225 	if (m->m_argv != NULL)
1226 	{
1227 		char **a = m->m_argv;
1228 
1229 		printf(" A=");
1230 		while (*a != NULL)
1231 		{
1232 			if (a != m->m_argv)
1233 				printf(" ");
1234 			xputs(*a++);
1235 		}
1236 	}
1237 	printf("\n");
1238 }
1239 /*
1240 **  SETOPTION -- set global processing option
1241 **
1242 **	Parameters:
1243 **		opt -- option name.
1244 **		val -- option value (as a text string).
1245 **		safe -- set if this came from a configuration file.
1246 **			Some options (if set from the command line) will
1247 **			reset the user id to avoid security problems.
1248 **		sticky -- if set, don't let other setoptions override
1249 **			this value.
1250 **		e -- the main envelope.
1251 **
1252 **	Returns:
1253 **		none.
1254 **
1255 **	Side Effects:
1256 **		Sets options as implied by the arguments.
1257 */
1258 
1259 static BITMAP	StickyOpt;		/* set if option is stuck */
1260 
1261 
1262 #if NAMED_BIND
1263 
1264 struct resolverflags
1265 {
1266 	char	*rf_name;	/* name of the flag */
1267 	long	rf_bits;	/* bits to set/clear */
1268 } ResolverFlags[] =
1269 {
1270 	"debug",	RES_DEBUG,
1271 	"aaonly",	RES_AAONLY,
1272 	"usevc",	RES_USEVC,
1273 	"primary",	RES_PRIMARY,
1274 	"igntc",	RES_IGNTC,
1275 	"recurse",	RES_RECURSE,
1276 	"defnames",	RES_DEFNAMES,
1277 	"stayopen",	RES_STAYOPEN,
1278 	"dnsrch",	RES_DNSRCH,
1279 	"true",		0,		/* to avoid error on old syntax */
1280 	NULL,		0
1281 };
1282 
1283 #endif
1284 
1285 struct optioninfo
1286 {
1287 	char	*o_name;	/* long name of option */
1288 	u_char	o_code;		/* short name of option */
1289 	bool	o_safe;		/* safe for random people to use */
1290 } OptionTab[] =
1291 {
1292 	"SevenBitInput",	'7',		TRUE,
1293 #if MIME8TO7
1294 	"EightBitMode",		'8',		TRUE,
1295 #endif
1296 	"AliasFile",		'A',		FALSE,
1297 	"AliasWait",		'a',		FALSE,
1298 	"BlankSub",		'B',		FALSE,
1299 	"MinFreeBlocks",	'b',		TRUE,
1300 	"CheckpointInterval",	'C',		TRUE,
1301 	"HoldExpensive",	'c',		FALSE,
1302 	"AutoRebuildAliases",	'D',		FALSE,
1303 	"DeliveryMode",		'd',		TRUE,
1304 	"ErrorHeader",		'E',		FALSE,
1305 	"ErrorMode",		'e',		TRUE,
1306 	"TempFileMode",		'F',		FALSE,
1307 	"SaveFromLine",		'f',		FALSE,
1308 	"MatchGECOS",		'G',		FALSE,
1309 	"HelpFile",		'H',		FALSE,
1310 	"MaxHopCount",		'h',		FALSE,
1311 	"ResolverOptions",	'I',		FALSE,
1312 	"IgnoreDots",		'i',		TRUE,
1313 	"ForwardPath",		'J',		FALSE,
1314 	"SendMimeErrors",	'j',		TRUE,
1315 	"ConnectionCacheSize",	'k',		FALSE,
1316 	"ConnectionCacheTimeout", 'K',		FALSE,
1317 	"UseErrorsTo",		'l',		FALSE,
1318 	"LogLevel",		'L',		FALSE,
1319 	"MeToo",		'm',		TRUE,
1320 	"CheckAliases",		'n',		FALSE,
1321 	"OldStyleHeaders",	'o',		TRUE,
1322 	"DaemonPortOptions",	'O',		FALSE,
1323 	"PrivacyOptions",	'p',		TRUE,
1324 	"PostmasterCopy",	'P',		FALSE,
1325 	"QueueFactor",		'q',		FALSE,
1326 	"QueueDirectory",	'Q',		FALSE,
1327 	"DontPruneRoutes",	'R',		FALSE,
1328 	"Timeout",		'r',		TRUE,
1329 	"StatusFile",		'S',		FALSE,
1330 	"SuperSafe",		's',		TRUE,
1331 	"QueueTimeout",		'T',		FALSE,
1332 	"TimeZoneSpec",		't',		FALSE,
1333 	"UserDatabaseSpec",	'U',		FALSE,
1334 	"DefaultUser",		'u',		FALSE,
1335 	"FallbackMXhost",	'V',		FALSE,
1336 	"Verbose",		'v',		TRUE,
1337 	"TryNullMXList",	'w',		TRUE,
1338 	"QueueLA",		'x',		FALSE,
1339 	"RefuseLA",		'X',		FALSE,
1340 	"RecipientFactor",	'y',		FALSE,
1341 	"ForkEachJob",		'Y',		FALSE,
1342 	"ClassFactor",		'z',		FALSE,
1343 	"RetryFactor",		'Z',		FALSE,
1344 #define O_QUEUESORTORD	0x81
1345 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
1346 #define O_HOSTSFILE	0x82
1347 	"HostsFile",		O_HOSTSFILE,	FALSE,
1348 #define O_MQA		0x83
1349 	"MinQueueAge",		O_MQA,		TRUE,
1350 #define O_MHSA		0x84
1351 /*
1352 	"MaxHostStatAge",	O_MHSA,		TRUE,
1353 */
1354 #define O_DEFCHARSET	0x85
1355 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
1356 #define O_SSFILE	0x86
1357 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
1358 #define O_DIALDELAY	0x87
1359 	"DialDelay",		O_DIALDELAY,	TRUE,
1360 #define O_NORCPTACTION	0x88
1361 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
1362 #define O_SAFEFILEENV	0x89
1363 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
1364 #define O_MAXMSGSIZE	0x8a
1365 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
1366 #define O_COLONOKINADDR	0x8b
1367 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
1368 
1369 	NULL,			'\0',		FALSE,
1370 };
1371 
1372 
1373 
1374 setoption(opt, val, safe, sticky, e)
1375 	u_char opt;
1376 	char *val;
1377 	bool safe;
1378 	bool sticky;
1379 	register ENVELOPE *e;
1380 {
1381 	register char *p;
1382 	register struct optioninfo *o;
1383 	char *subopt;
1384 	extern bool atobool();
1385 	extern time_t convtime();
1386 	extern int QueueLA;
1387 	extern int RefuseLA;
1388 	extern bool Warn_Q_option;
1389 
1390 	errno = 0;
1391 	if (opt == ' ')
1392 	{
1393 		/* full word options */
1394 		struct optioninfo *sel;
1395 
1396 		p = strchr(val, '=');
1397 		if (p == NULL)
1398 			p = &val[strlen(val)];
1399 		while (*--p == ' ')
1400 			continue;
1401 		while (*++p == ' ')
1402 			*p = '\0';
1403 		if (p == val)
1404 		{
1405 			syserr("readcf: null option name");
1406 			return;
1407 		}
1408 		if (*p == '=')
1409 			*p++ = '\0';
1410 		while (*p == ' ')
1411 			p++;
1412 		subopt = strchr(val, '.');
1413 		if (subopt != NULL)
1414 			*subopt++ = '\0';
1415 		sel = NULL;
1416 		for (o = OptionTab; o->o_name != NULL; o++)
1417 		{
1418 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
1419 				continue;
1420 			if (strlen(o->o_name) == strlen(val))
1421 			{
1422 				/* completely specified -- this must be it */
1423 				sel = NULL;
1424 				break;
1425 			}
1426 			if (sel != NULL)
1427 				break;
1428 			sel = o;
1429 		}
1430 		if (sel != NULL && o->o_name == NULL)
1431 			o = sel;
1432 		else if (o->o_name == NULL)
1433 		{
1434 			syserr("readcf: unknown option name %s", val);
1435 			return;
1436 		}
1437 		else if (sel != NULL)
1438 		{
1439 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
1440 				val, sel->o_name, o->o_name);
1441 			return;
1442 		}
1443 		if (strlen(val) != strlen(o->o_name))
1444 		{
1445 			bool oldVerbose = Verbose;
1446 
1447 			Verbose = TRUE;
1448 			message("Option %s used as abbreviation for %s",
1449 				val, o->o_name);
1450 			Verbose = oldVerbose;
1451 		}
1452 		opt = o->o_code;
1453 		val = p;
1454 	}
1455 	else
1456 	{
1457 		for (o = OptionTab; o->o_name != NULL; o++)
1458 		{
1459 			if (o->o_code == opt)
1460 				break;
1461 		}
1462 		subopt = NULL;
1463 	}
1464 
1465 	if (tTd(37, 1))
1466 	{
1467 		printf(isascii(opt) && isprint(opt) ?
1468 			    "setoption %s (%c).%s=%s" :
1469 			    "setoption %s (0x%x).%s=%s",
1470 			o->o_name == NULL ? "<unknown>" : o->o_name,
1471 			opt,
1472 			subopt == NULL ? "" : subopt,
1473 			val);
1474 	}
1475 
1476 	/*
1477 	**  See if this option is preset for us.
1478 	*/
1479 
1480 	if (!sticky && bitnset(opt, StickyOpt))
1481 	{
1482 		if (tTd(37, 1))
1483 			printf(" (ignored)\n");
1484 		return;
1485 	}
1486 
1487 	/*
1488 	**  Check to see if this option can be specified by this user.
1489 	*/
1490 
1491 	if (!safe && RealUid == 0)
1492 		safe = TRUE;
1493 	if (!safe && !o->o_safe)
1494 	{
1495 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1496 		{
1497 			if (tTd(37, 1))
1498 				printf(" (unsafe)");
1499 			if (RealUid != geteuid())
1500 			{
1501 				if (tTd(37, 1))
1502 					printf("(Resetting uid)");
1503 				(void) setgid(RealGid);
1504 				(void) setuid(RealUid);
1505 			}
1506 		}
1507 	}
1508 	if (tTd(37, 1))
1509 		printf("\n");
1510 
1511 	switch (opt & 0xff)
1512 	{
1513 	  case '7':		/* force seven-bit input */
1514 		SevenBitInput = atobool(val);
1515 		break;
1516 
1517 #if MIME8TO7
1518 	  case '8':		/* handling of 8-bit input */
1519 		switch (*val)
1520 		{
1521 		  case 'm':		/* convert 8-bit, convert MIME */
1522 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1523 			break;
1524 
1525 		  case 'p':		/* pass 8 bit, convert MIME */
1526 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
1527 			break;
1528 
1529 		  case 's':		/* strict adherence */
1530 			MimeMode = MM_CVTMIME;
1531 			break;
1532 
1533 #if 0
1534 		  case 'r':		/* reject 8-bit, don't convert MIME */
1535 			MimeMode = 0;
1536 			break;
1537 
1538 		  case 'j':		/* "just send 8" */
1539 			MimeMode = MM_PASS8BIT;
1540 			break;
1541 
1542 		  case 'a':		/* encode 8 bit if available */
1543 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1544 			break;
1545 
1546 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1547 			MimeMode = MM_MIME8BIT;
1548 			break;
1549 #endif
1550 
1551 		  default:
1552 			syserr("Unknown 8-bit mode %c", *val);
1553 			exit(EX_USAGE);
1554 		}
1555 		break;
1556 #endif
1557 
1558 	  case 'A':		/* set default alias file */
1559 		if (val[0] == '\0')
1560 			setalias("aliases");
1561 		else
1562 			setalias(val);
1563 		break;
1564 
1565 	  case 'a':		/* look N minutes for "@:@" in alias file */
1566 		if (val[0] == '\0')
1567 			SafeAlias = 5 * 60;		/* five minutes */
1568 		else
1569 			SafeAlias = convtime(val, 'm');
1570 		break;
1571 
1572 	  case 'B':		/* substitution for blank character */
1573 		SpaceSub = val[0];
1574 		if (SpaceSub == '\0')
1575 			SpaceSub = ' ';
1576 		break;
1577 
1578 	  case 'b':		/* min blocks free on queue fs/max msg size */
1579 		p = strchr(val, '/');
1580 		if (p != NULL)
1581 		{
1582 			*p++ = '\0';
1583 			MaxMessageSize = atol(p);
1584 		}
1585 		MinBlocksFree = atol(val);
1586 		break;
1587 
1588 	  case 'c':		/* don't connect to "expensive" mailers */
1589 		NoConnect = atobool(val);
1590 		break;
1591 
1592 	  case 'C':		/* checkpoint every N addresses */
1593 		CheckpointInterval = atoi(val);
1594 		break;
1595 
1596 	  case 'd':		/* delivery mode */
1597 		switch (*val)
1598 		{
1599 		  case '\0':
1600 			e->e_sendmode = SM_DELIVER;
1601 			break;
1602 
1603 		  case SM_QUEUE:	/* queue only */
1604 #ifndef QUEUE
1605 			syserr("need QUEUE to set -odqueue");
1606 #endif /* QUEUE */
1607 			/* fall through..... */
1608 
1609 		  case SM_DELIVER:	/* do everything */
1610 		  case SM_FORK:		/* fork after verification */
1611 			e->e_sendmode = *val;
1612 			break;
1613 
1614 		  default:
1615 			syserr("Unknown delivery mode %c", *val);
1616 			exit(EX_USAGE);
1617 		}
1618 		break;
1619 
1620 	  case 'D':		/* rebuild alias database as needed */
1621 		AutoRebuild = atobool(val);
1622 		break;
1623 
1624 	  case 'E':		/* error message header/header file */
1625 		if (*val != '\0')
1626 			ErrMsgFile = newstr(val);
1627 		break;
1628 
1629 	  case 'e':		/* set error processing mode */
1630 		switch (*val)
1631 		{
1632 		  case EM_QUIET:	/* be silent about it */
1633 		  case EM_MAIL:		/* mail back */
1634 		  case EM_BERKNET:	/* do berknet error processing */
1635 		  case EM_WRITE:	/* write back (or mail) */
1636 		  case EM_PRINT:	/* print errors normally (default) */
1637 			e->e_errormode = *val;
1638 			break;
1639 		}
1640 		break;
1641 
1642 	  case 'F':		/* file mode */
1643 		FileMode = atooct(val) & 0777;
1644 		break;
1645 
1646 	  case 'f':		/* save Unix-style From lines on front */
1647 		SaveFrom = atobool(val);
1648 		break;
1649 
1650 	  case 'G':		/* match recipients against GECOS field */
1651 		MatchGecos = atobool(val);
1652 		break;
1653 
1654 	  case 'g':		/* default gid */
1655   g_opt:
1656 		if (isascii(*val) && isdigit(*val))
1657 			DefGid = atoi(val);
1658 		else
1659 		{
1660 			register struct group *gr;
1661 
1662 			DefGid = -1;
1663 			gr = getgrnam(val);
1664 			if (gr == NULL)
1665 				syserr("readcf: option %c: unknown group %s",
1666 					opt, val);
1667 			else
1668 				DefGid = gr->gr_gid;
1669 		}
1670 		break;
1671 
1672 	  case 'H':		/* help file */
1673 		if (val[0] == '\0')
1674 			HelpFile = "sendmail.hf";
1675 		else
1676 			HelpFile = newstr(val);
1677 		break;
1678 
1679 	  case 'h':		/* maximum hop count */
1680 		MaxHopCount = atoi(val);
1681 		break;
1682 
1683 	  case 'I':		/* use internet domain name server */
1684 #if NAMED_BIND
1685 		for (p = val; *p != 0; )
1686 		{
1687 			bool clearmode;
1688 			char *q;
1689 			struct resolverflags *rfp;
1690 
1691 			while (*p == ' ')
1692 				p++;
1693 			if (*p == '\0')
1694 				break;
1695 			clearmode = FALSE;
1696 			if (*p == '-')
1697 				clearmode = TRUE;
1698 			else if (*p != '+')
1699 				p--;
1700 			p++;
1701 			q = p;
1702 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1703 				p++;
1704 			if (*p != '\0')
1705 				*p++ = '\0';
1706 			if (strcasecmp(q, "HasWildcardMX") == 0)
1707 			{
1708 				NoMXforCanon = !clearmode;
1709 				continue;
1710 			}
1711 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1712 			{
1713 				if (strcasecmp(q, rfp->rf_name) == 0)
1714 					break;
1715 			}
1716 			if (rfp->rf_name == NULL)
1717 				syserr("readcf: I option value %s unrecognized", q);
1718 			else if (clearmode)
1719 				_res.options &= ~rfp->rf_bits;
1720 			else
1721 				_res.options |= rfp->rf_bits;
1722 		}
1723 		if (tTd(8, 2))
1724 			printf("_res.options = %x, HasWildcardMX = %d\n",
1725 				_res.options, !NoMXforCanon);
1726 #else
1727 		usrerr("name server (I option) specified but BIND not compiled in");
1728 #endif
1729 		break;
1730 
1731 	  case 'i':		/* ignore dot lines in message */
1732 		IgnrDot = atobool(val);
1733 		break;
1734 
1735 	  case 'j':		/* send errors in MIME (RFC 1341) format */
1736 		SendMIMEErrors = atobool(val);
1737 		break;
1738 
1739 	  case 'J':		/* .forward search path */
1740 		ForwardPath = newstr(val);
1741 		break;
1742 
1743 	  case 'k':		/* connection cache size */
1744 		MaxMciCache = atoi(val);
1745 		if (MaxMciCache < 0)
1746 			MaxMciCache = 0;
1747 		break;
1748 
1749 	  case 'K':		/* connection cache timeout */
1750 		MciCacheTimeout = convtime(val, 'm');
1751 		break;
1752 
1753 	  case 'l':		/* use Errors-To: header */
1754 		UseErrorsTo = atobool(val);
1755 		break;
1756 
1757 	  case 'L':		/* log level */
1758 		if (safe || LogLevel < atoi(val))
1759 			LogLevel = atoi(val);
1760 		break;
1761 
1762 	  case 'M':		/* define macro */
1763 		p = newstr(&val[1]);
1764 		if (!safe)
1765 			cleanstrcpy(p, p, MAXNAME);
1766 		define(val[0], p, CurEnv);
1767 		sticky = FALSE;
1768 		break;
1769 
1770 	  case 'm':		/* send to me too */
1771 		MeToo = atobool(val);
1772 		break;
1773 
1774 	  case 'n':		/* validate RHS in newaliases */
1775 		CheckAliases = atobool(val);
1776 		break;
1777 
1778 	    /* 'N' available -- was "net name" */
1779 
1780 	  case 'O':		/* daemon options */
1781 		setdaemonoptions(val);
1782 		break;
1783 
1784 	  case 'o':		/* assume old style headers */
1785 		if (atobool(val))
1786 			CurEnv->e_flags |= EF_OLDSTYLE;
1787 		else
1788 			CurEnv->e_flags &= ~EF_OLDSTYLE;
1789 		break;
1790 
1791 	  case 'p':		/* select privacy level */
1792 		p = val;
1793 		for (;;)
1794 		{
1795 			register struct prival *pv;
1796 			extern struct prival PrivacyValues[];
1797 
1798 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
1799 				p++;
1800 			if (*p == '\0')
1801 				break;
1802 			val = p;
1803 			while (isascii(*p) && isalnum(*p))
1804 				p++;
1805 			if (*p != '\0')
1806 				*p++ = '\0';
1807 
1808 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
1809 			{
1810 				if (strcasecmp(val, pv->pv_name) == 0)
1811 					break;
1812 			}
1813 			if (pv->pv_name == NULL)
1814 				syserr("readcf: Op line: %s unrecognized", val);
1815 			PrivacyFlags |= pv->pv_flag;
1816 		}
1817 		sticky = FALSE;
1818 		break;
1819 
1820 	  case 'P':		/* postmaster copy address for returned mail */
1821 		PostMasterCopy = newstr(val);
1822 		break;
1823 
1824 	  case 'q':		/* slope of queue only function */
1825 		QueueFactor = atoi(val);
1826 		break;
1827 
1828 	  case 'Q':		/* queue directory */
1829 		if (val[0] == '\0')
1830 			QueueDir = "mqueue";
1831 		else
1832 			QueueDir = newstr(val);
1833 		if (RealUid != 0 && !safe)
1834 			Warn_Q_option = TRUE;
1835 		break;
1836 
1837 	  case 'R':		/* don't prune routes */
1838 		DontPruneRoutes = atobool(val);
1839 		break;
1840 
1841 	  case 'r':		/* read timeout */
1842 		if (subopt == NULL)
1843 			inittimeouts(val);
1844 		else
1845 			settimeout(subopt, val);
1846 		break;
1847 
1848 	  case 'S':		/* status file */
1849 		if (val[0] == '\0')
1850 			StatFile = "sendmail.st";
1851 		else
1852 			StatFile = newstr(val);
1853 		break;
1854 
1855 	  case 's':		/* be super safe, even if expensive */
1856 		SuperSafe = atobool(val);
1857 		break;
1858 
1859 	  case 'T':		/* queue timeout */
1860 		p = strchr(val, '/');
1861 		if (p != NULL)
1862 		{
1863 			*p++ = '\0';
1864 			settimeout("queuewarn", p);
1865 		}
1866 		settimeout("queuereturn", val);
1867 		break;
1868 
1869 	  case 't':		/* time zone name */
1870 		TimeZoneSpec = newstr(val);
1871 		break;
1872 
1873 	  case 'U':		/* location of user database */
1874 		UdbSpec = newstr(val);
1875 		break;
1876 
1877 	  case 'u':		/* set default uid */
1878 		for (p = val; *p != '\0'; p++)
1879 		{
1880 			if (*p == '.' || *p == '/' || *p == ':')
1881 			{
1882 				*p++ = '\0';
1883 				break;
1884 			}
1885 		}
1886 		if (isascii(*val) && isdigit(*val))
1887 			DefUid = atoi(val);
1888 		else
1889 		{
1890 			register struct passwd *pw;
1891 
1892 			DefUid = -1;
1893 			pw = sm_getpwnam(val);
1894 			if (pw == NULL)
1895 				syserr("readcf: option u: unknown user %s", val);
1896 			else
1897 			{
1898 				DefUid = pw->pw_uid;
1899 				DefGid = pw->pw_gid;
1900 			}
1901 		}
1902 		setdefuser();
1903 
1904 		/* handle the group if it is there */
1905 		if (*p == '\0')
1906 			break;
1907 		val = p;
1908 		goto g_opt;
1909 
1910 	  case 'V':		/* fallback MX host */
1911 		FallBackMX = newstr(val);
1912 		break;
1913 
1914 	  case 'v':		/* run in verbose mode */
1915 		Verbose = atobool(val);
1916 		break;
1917 
1918 	  case 'w':		/* if we are best MX, try host directly */
1919 		TryNullMXList = atobool(val);
1920 		break;
1921 
1922 	    /* 'W' available -- was wizard password */
1923 
1924 	  case 'x':		/* load avg at which to auto-queue msgs */
1925 		QueueLA = atoi(val);
1926 		break;
1927 
1928 	  case 'X':		/* load avg at which to auto-reject connections */
1929 		RefuseLA = atoi(val);
1930 		break;
1931 
1932 	  case 'y':		/* work recipient factor */
1933 		WkRecipFact = atoi(val);
1934 		break;
1935 
1936 	  case 'Y':		/* fork jobs during queue runs */
1937 		ForkQueueRuns = atobool(val);
1938 		break;
1939 
1940 	  case 'z':		/* work message class factor */
1941 		WkClassFact = atoi(val);
1942 		break;
1943 
1944 	  case 'Z':		/* work time factor */
1945 		WkTimeFact = atoi(val);
1946 		break;
1947 
1948 	  case O_QUEUESORTORD:	/* queue sorting order */
1949 		switch (*val)
1950 		{
1951 		  case 'h':	/* Host first */
1952 		  case 'H':
1953 			QueueSortOrder = QS_BYHOST;
1954 			break;
1955 
1956 		  case 'p':	/* Priority order */
1957 		  case 'P':
1958 			QueueSortOrder = QS_BYPRIORITY;
1959 			break;
1960 
1961 		  default:
1962 			syserr("Invalid queue sort order \"%s\"", val);
1963 		}
1964 		break;
1965 
1966 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
1967 		HostsFile = newstr(val);
1968 		break;
1969 
1970 	  case O_MQA:		/* minimum queue age between deliveries */
1971 		MinQueueAge = convtime(val, 'm');
1972 		break;
1973 
1974 	  case O_MHSA:		/* maximum age of cached host status */
1975 		MaxHostStatAge = convtime(val, 'm');
1976 		break;
1977 
1978 	  case O_DEFCHARSET:	/* default character set for mimefying */
1979 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
1980 		break;
1981 
1982 	  case O_SSFILE:	/* service switch file */
1983 		ServiceSwitchFile = newstr(val);
1984 		break;
1985 
1986 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
1987 		DialDelay = convtime(val, 's');
1988 		break;
1989 
1990 	  case O_NORCPTACTION:	/* what to do if no recipient */
1991 		if (strcasecmp(val, "none") == 0)
1992 			NoRecipientAction = NRA_NO_ACTION;
1993 		else if (strcasecmp(val, "add-to") == 0)
1994 			NoRecipientAction = NRA_ADD_TO;
1995 		else if (strcasecmp(val, "add-apparently-to") == 0)
1996 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
1997 		else if (strcasecmp(val, "add-bcc") == 0)
1998 			NoRecipientAction = NRA_ADD_BCC;
1999 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
2000 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
2001 		else
2002 			syserr("Invalid NoRecipientAction: %s", val);
2003 
2004 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
2005 		SafeFileEnv = newstr(val);
2006 		break;
2007 
2008 	  case O_MAXMSGSIZE:	/* maximum message size */
2009 		MaxMessageSize = atol(p);
2010 		break;
2011 
2012 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
2013 		ColonOkInAddr = atobool(p);
2014 		break;
2015 
2016 	  default:
2017 		if (tTd(37, 1))
2018 		{
2019 			if (isascii(opt) && isprint(opt))
2020 				printf("Warning: option %c unknown\n", opt);
2021 			else
2022 				printf("Warning: option 0x%x unknown\n", opt);
2023 		}
2024 		break;
2025 	}
2026 	if (sticky)
2027 		setbitn(opt, StickyOpt);
2028 	return;
2029 }
2030 /*
2031 **  SETCLASS -- set a string into a class
2032 **
2033 **	Parameters:
2034 **		class -- the class to put the string in.
2035 **		str -- the string to enter
2036 **
2037 **	Returns:
2038 **		none.
2039 **
2040 **	Side Effects:
2041 **		puts the word into the symbol table.
2042 */
2043 
2044 setclass(class, str)
2045 	int class;
2046 	char *str;
2047 {
2048 	register STAB *s;
2049 
2050 	if (tTd(37, 8))
2051 		printf("setclass(%c, %s)\n", class, str);
2052 	s = stab(str, ST_CLASS, ST_ENTER);
2053 	setbitn(class, s->s_class);
2054 }
2055 /*
2056 **  MAKEMAPENTRY -- create a map entry
2057 **
2058 **	Parameters:
2059 **		line -- the config file line
2060 **
2061 **	Returns:
2062 **		TRUE if it successfully entered the map entry.
2063 **		FALSE otherwise (usually syntax error).
2064 **
2065 **	Side Effects:
2066 **		Enters the map into the dictionary.
2067 */
2068 
2069 void
2070 makemapentry(line)
2071 	char *line;
2072 {
2073 	register char *p;
2074 	char *mapname;
2075 	char *classname;
2076 	register STAB *s;
2077 	STAB *class;
2078 
2079 	for (p = line; isascii(*p) && isspace(*p); p++)
2080 		continue;
2081 	if (!(isascii(*p) && isalnum(*p)))
2082 	{
2083 		syserr("readcf: config K line: no map name");
2084 		return;
2085 	}
2086 
2087 	mapname = p;
2088 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
2089 		continue;
2090 	if (*p != '\0')
2091 		*p++ = '\0';
2092 	while (isascii(*p) && isspace(*p))
2093 		p++;
2094 	if (!(isascii(*p) && isalnum(*p)))
2095 	{
2096 		syserr("readcf: config K line, map %s: no map class", mapname);
2097 		return;
2098 	}
2099 	classname = p;
2100 	while (isascii(*++p) && isalnum(*p))
2101 		continue;
2102 	if (*p != '\0')
2103 		*p++ = '\0';
2104 	while (isascii(*p) && isspace(*p))
2105 		p++;
2106 
2107 	/* look up the class */
2108 	class = stab(classname, ST_MAPCLASS, ST_FIND);
2109 	if (class == NULL)
2110 	{
2111 		syserr("readcf: map %s: class %s not available", mapname, classname);
2112 		return;
2113 	}
2114 
2115 	/* enter the map */
2116 	s = stab(mapname, ST_MAP, ST_ENTER);
2117 	s->s_map.map_class = &class->s_mapclass;
2118 	s->s_map.map_mname = newstr(mapname);
2119 
2120 	if (class->s_mapclass.map_parse(&s->s_map, p))
2121 		s->s_map.map_mflags |= MF_VALID;
2122 
2123 	if (tTd(37, 5))
2124 	{
2125 		printf("map %s, class %s, flags %x, file %s,\n",
2126 			s->s_map.map_mname, s->s_map.map_class->map_cname,
2127 			s->s_map.map_mflags,
2128 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
2129 		printf("\tapp %s, domain %s, rebuild %s\n",
2130 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
2131 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
2132 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
2133 	}
2134 }
2135 /*
2136 **  INITTIMEOUTS -- parse and set timeout values
2137 **
2138 **	Parameters:
2139 **		val -- a pointer to the values.  If NULL, do initial
2140 **			settings.
2141 **
2142 **	Returns:
2143 **		none.
2144 **
2145 **	Side Effects:
2146 **		Initializes the TimeOuts structure
2147 */
2148 
2149 #define SECONDS
2150 #define MINUTES	* 60
2151 #define HOUR	* 3600
2152 
2153 inittimeouts(val)
2154 	register char *val;
2155 {
2156 	register char *p;
2157 	extern time_t convtime();
2158 
2159 	if (val == NULL)
2160 	{
2161 		TimeOuts.to_initial = (time_t) 5 MINUTES;
2162 		TimeOuts.to_helo = (time_t) 5 MINUTES;
2163 		TimeOuts.to_mail = (time_t) 10 MINUTES;
2164 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
2165 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
2166 		TimeOuts.to_datablock = (time_t) 1 HOUR;
2167 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
2168 		TimeOuts.to_rset = (time_t) 5 MINUTES;
2169 		TimeOuts.to_quit = (time_t) 2 MINUTES;
2170 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
2171 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
2172 #if IDENTPROTO
2173 		TimeOuts.to_ident = (time_t) 30 SECONDS;
2174 #else
2175 		TimeOuts.to_ident = (time_t) 0 SECONDS;
2176 #endif
2177 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
2178 		return;
2179 	}
2180 
2181 	for (;; val = p)
2182 	{
2183 		while (isascii(*val) && isspace(*val))
2184 			val++;
2185 		if (*val == '\0')
2186 			break;
2187 		for (p = val; *p != '\0' && *p != ','; p++)
2188 			continue;
2189 		if (*p != '\0')
2190 			*p++ = '\0';
2191 
2192 		if (isascii(*val) && isdigit(*val))
2193 		{
2194 			/* old syntax -- set everything */
2195 			TimeOuts.to_mail = convtime(val, 'm');
2196 			TimeOuts.to_rcpt = TimeOuts.to_mail;
2197 			TimeOuts.to_datainit = TimeOuts.to_mail;
2198 			TimeOuts.to_datablock = TimeOuts.to_mail;
2199 			TimeOuts.to_datafinal = TimeOuts.to_mail;
2200 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
2201 			continue;
2202 		}
2203 		else
2204 		{
2205 			register char *q = strchr(val, ':');
2206 
2207 			if (q == NULL && (q = strchr(val, '=')) == NULL)
2208 			{
2209 				/* syntax error */
2210 				continue;
2211 			}
2212 			*q++ = '\0';
2213 			settimeout(val, q);
2214 		}
2215 	}
2216 }
2217 /*
2218 **  SETTIMEOUT -- set an individual timeout
2219 **
2220 **	Parameters:
2221 **		name -- the name of the timeout.
2222 **		val -- the value of the timeout.
2223 **
2224 **	Returns:
2225 **		none.
2226 */
2227 
2228 settimeout(name, val)
2229 	char *name;
2230 	char *val;
2231 {
2232 	register char *p;
2233 	time_t to;
2234 	extern time_t convtime();
2235 
2236 	to = convtime(val, 'm');
2237 	p = strchr(name, '.');
2238 	if (p != NULL)
2239 		*p++ = '\0';
2240 
2241 	if (strcasecmp(name, "initial") == 0)
2242 		TimeOuts.to_initial = to;
2243 	else if (strcasecmp(name, "mail") == 0)
2244 		TimeOuts.to_mail = to;
2245 	else if (strcasecmp(name, "rcpt") == 0)
2246 		TimeOuts.to_rcpt = to;
2247 	else if (strcasecmp(name, "datainit") == 0)
2248 		TimeOuts.to_datainit = to;
2249 	else if (strcasecmp(name, "datablock") == 0)
2250 		TimeOuts.to_datablock = to;
2251 	else if (strcasecmp(name, "datafinal") == 0)
2252 		TimeOuts.to_datafinal = to;
2253 	else if (strcasecmp(name, "command") == 0)
2254 		TimeOuts.to_nextcommand = to;
2255 	else if (strcasecmp(name, "rset") == 0)
2256 		TimeOuts.to_rset = to;
2257 	else if (strcasecmp(name, "helo") == 0)
2258 		TimeOuts.to_helo = to;
2259 	else if (strcasecmp(name, "quit") == 0)
2260 		TimeOuts.to_quit = to;
2261 	else if (strcasecmp(name, "misc") == 0)
2262 		TimeOuts.to_miscshort = to;
2263 	else if (strcasecmp(name, "ident") == 0)
2264 		TimeOuts.to_ident = to;
2265 	else if (strcasecmp(name, "fileopen") == 0)
2266 		TimeOuts.to_fileopen = to;
2267 	else if (strcasecmp(name, "queuewarn") == 0)
2268 	{
2269 		to = convtime(val, 'h');
2270 		if (p == NULL || strcmp(p, "*") == 0)
2271 		{
2272 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2273 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2274 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2275 		}
2276 		else if (strcasecmp(p, "normal") == 0)
2277 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2278 		else if (strcasecmp(p, "urgent") == 0)
2279 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2280 		else if (strcasecmp(p, "non-urgent") == 0)
2281 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2282 		else
2283 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
2284 	}
2285 	else if (strcasecmp(name, "queuereturn") == 0)
2286 	{
2287 		to = convtime(val, 'd');
2288 		if (p == NULL || strcmp(p, "*") == 0)
2289 		{
2290 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2291 			TimeOuts.to_q_return[TOC_URGENT] = to;
2292 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2293 		}
2294 		else if (strcasecmp(p, "normal") == 0)
2295 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2296 		else if (strcasecmp(p, "urgent") == 0)
2297 			TimeOuts.to_q_return[TOC_URGENT] = to;
2298 		else if (strcasecmp(p, "non-urgent") == 0)
2299 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2300 		else
2301 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
2302 	}
2303 	else
2304 		syserr("settimeout: invalid timeout %s", name);
2305 }
2306