**			   m -- maximum messages per connection **			   / -- new root directory
static char	**makeargv __P((char *));static void	settimeout __P((char *, char *, bool));static void	toomany __P((int, int));/***  READCF -- read configuration file.****	This routine reads the configuration file and builds the internal**	form.****	The file is formatted as a sequence of lines, each taken**	atomically.  The first character of each line describes how**	the line is to be interpreted.  The lines are:**		Dxval		Define macro x to have value val.**		Cxword		Put word into class x.**		Fxfile [fmt]	Read file for lines to put into**				class x.  Use scanf string 'fmt'**				or "%s" if not present.  Fmt should**				only produce one string-valued result.**		Hname: value	Define header with field-name 'name'**				and value as specified; this will be**				macro expanded immediately before**				use.**		Sn		Use rewriting set n.**		Rlhs rhs	Rewrite addresses that match lhs to**				be rhs.**		Mn arg=val...	Define mailer.  n is the internal name.**				Args specify mailer parameters.**		Oxvalue		Set option x to value.**		Pname=value	Set precedence name to value.**		Vversioncode[/vendorcode]**				Version level/vendor name of**				configuration syntax.**		Kmapname mapclass arguments....**				Define keyed lookup of a given class.**				Arguments are class dependent.**		Eenvar=value	Set the environment value to the given value.****	Parameters:**		cfname -- configuration file name.**		safe -- TRUE if this is the system config file;**			FALSE otherwise.**		e -- the main envelope.****	Returns:**		none.****	Side Effects:**		Builds several internal tables.*/voidreadcf(cfname, safe, e)	char *cfname;	bool safe;	register ENVELOPE *e;{	FILE *cf;	int ruleset = -1;	char *q;	struct rewrite *rwp = NULL;	char *bp;	auto char *ep;	int nfuzzy;	char *file;	bool optional;	int mid;	register char *p;	long sff = SFF_OPENASROOT;	struct stat statb;	char buf[MAXLINE];	char exbuf[MAXLINE];	char pvpbuf[MAXLINE + MAXATOM];	static char *null_list[1] = { NULL };	extern u_char TokTypeNoC[];	FileName = cfname;	LineNumber = 0;	if (DontLockReadFiles)		sff |= SFF_NOLOCK;	cf = safefopen(cfname, O_RDONLY, 0444, sff);	if (cf == NULL)	{		syserr("cannot open");		finis(FALSE, EX_OSFILE);	}	if (fstat(fileno(cf), &statb) < 0)	{		syserr("cannot fstat");		finis(FALSE, EX_OSFILE);	}	if (!S_ISREG(statb.st_mode))	{		syserr("not a plain file");		finis(FALSE, EX_OSFILE);	}	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))	{		if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",				FileName);		if (LogLevel > 0)			sm_syslog(LOG_CRIT, NOQID,				  "%s: WARNING: dangerous write permissions",				  FileName);	}#ifdef XLA	xla_zero();#endif /* XLA */	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)	{		if (bp[0] == '#')		{			if (bp != buf)				free(bp);			continue;		}		/* do macro expansion mappings */		translate_dollars(bp);		/* interpret this line */		errno = 0;		switch (bp[0])		{		  case '\0':		  case '#':		/* comment */			break;		  case 'R':		/* rewriting rule */			if (ruleset < 0)			{				syserr("missing valid ruleset for \"%s\"", bp);				break;			}			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)				continue;			if (*p == '\0')			{				syserr("invalid rewrite line \"%s\" (tab expected)", bp);				break;			}			/* allocate space for the rule header */			if (rwp == NULL)			{				RewriteRules[ruleset] = rwp =					(struct rewrite *) xalloc(sizeof *rwp);			}			else			{				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);				rwp = rwp->r_next;			}			rwp->r_next = NULL;			/* expand and save the LHS */			*p = '\0';			expand(&bp[1], exbuf, sizeof exbuf, e);			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,					     sizeof pvpbuf, NULL,					     ConfigLevel >= 9 ? TokTypeNoC : NULL);			nfuzzy = 0;			if (rwp->r_lhs != NULL)			{				register char **ap;				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);				/* count the number of fuzzy matches in LHS */				for (ap = rwp->r_lhs; *ap != NULL; ap++)				{					char *botch;					botch = NULL;					switch (**ap & 0377)					{					  case MATCHZANY:					  case MATCHANY:					  case MATCHONE:					  case MATCHCLASS:					  case MATCHNCLASS:						nfuzzy++;						break;					  case MATCHREPL:						botch = "$0-$9";						break;					  case CANONUSER:						botch = "$:";						break;					  case CALLSUBR:						botch = "$>";						break;					  case CONDIF:						botch = "$?";						break;					  case CONDFI:						botch = "$.";						break;					  case HOSTBEGIN:						botch = "$[";						break;					  case HOSTEND:						botch = "$]";						break;					  case LOOKUPBEGIN:						botch = "$(";						break;					  case LOOKUPEND:						botch = "$)";						break;					}					if (botch != NULL)						syserr("Inappropriate use of %s on LHS",							botch);				}				rwp->r_line = LineNumber;			}			else			{				syserr("R line: null LHS");				rwp->r_lhs = null_list;			}			/* expand and save the RHS */			while (*++p == '\t')				continue;			q = p;			while (*p != '\0' && *p != '\t')				p++;			*p = '\0';			expand(q, exbuf, sizeof exbuf, e);			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,					     sizeof pvpbuf, NULL,					     ConfigLevel >= 9 ? TokTypeNoC : NULL);			if (rwp->r_rhs != NULL)			{				register char **ap;				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);				/* check no out-of-bounds replacements */				nfuzzy += '0';				for (ap = rwp->r_rhs; *ap != NULL; ap++)				{					char *botch;					botch = NULL;					switch (**ap & 0377)					{					  case MATCHREPL:						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)						{							syserr("replacement $%c out of bounds",								(*ap)[1]);						}						break;					  case MATCHZANY:						botch = "$*";						break;					  case MATCHANY:						botch = "$+";						break;					  case MATCHONE:						botch = "$-";						break;					  case MATCHCLASS:						botch = "$=";						break;					  case MATCHNCLASS:						botch = "$~";						break;					}					if (botch != NULL)						syserr("Inappropriate use of %s on RHS",							botch);				}			}			else			{				syserr("R line: null RHS");				rwp->r_rhs = null_list;			}			break;		  case 'S':		/* select rewriting set */			expand(&bp[1], exbuf, sizeof exbuf, e);			ruleset = strtorwset(exbuf, NULL, ST_ENTER);			if (ruleset < 0)				break;			rwp = RewriteRules[ruleset];			if (rwp != NULL)			{				if (OpMode == MD_TEST)					printf("WARNING: Ruleset %s has multiple definitions\n",					       &bp[1]);				if (tTd(37, 1))					dprintf("WARNING: Ruleset %s has multiple definitions\n",						&bp[1]);				while (rwp->r_next != NULL)					rwp = rwp->r_next;			}			break;		  case 'D':		/* macro definition */			mid = macid(&bp[1], &ep);			if (mid == 0)				break;			p = munchstring(ep, NULL, '\0');			define(mid, newstr(p), e);			break;		  case 'H':		/* required header line */			(void) chompheader(&bp[1], CHHDR_DEF, NULL, e);			break;		  case 'C':		/* word class */		  case 'T':		/* trusted user (set class `t') */			if (bp[0] == 'C')			{				mid = macid(&bp[1], &ep);				if (mid == 0)					break;				expand(ep, exbuf, sizeof exbuf, e);				p = exbuf;			}			else			{				mid = 't';				p = &bp[1];			}			while (*p != '\0')			{				register char *wd;				char delim;				while (*p != '\0' && isascii(*p) && isspace(*p))					p++;				wd = p;				while (*p != '\0' && !(isascii(*p) && isspace(*p)))					p++;				delim = *p;				*p = '\0';				if (wd[0] != '\0')					setclass(mid, wd);				*p = delim;			}			break;		  case 'F':		/* word class from file */			mid = macid(&bp[1], &ep);			if (mid == 0)				break;			for (p = ep; isascii(*p) && isspace(*p); )				p++;			if (p[0] == '-' && p[1] == 'o')			{				optional = TRUE;				while (*p != '\0' && !(isascii(*p) && isspace(*p)))					p++;				while (isascii(*p) && isspace(*p))					p++;			}			else				optional = FALSE;			file = p;			q = p;			while (*q != '\0' && !(isascii(*q) && isspace(*q)))				q++;			if (*file == '|')				p = "%s";			else			{				p = q;				if (*p == '\0')					p = "%s";				else				{					*p = '\0';					while (isascii(*++p) && isspace(*p))						continue;				}			}			fileclass(mid, file, p, safe, optional);			break;#ifdef XLA		  case 'L':		/* extended load average description */			xla_init(&bp[1]);			break;#endif /* XLA */#if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO)		  case 'L':		/* lookup macro */		  case 'G':		/* lookup class */			/* reserved for Sun -- NIS+ database lookup */			if (VendorCode != VENDOR_SUN)				goto badline;			sun_lg_config_line(bp, e);			break;#endif /* defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) */		  case 'M':		/* define mailer */			makemailer(&bp[1]);			break;		  case 'O':		/* set option */			setoption(bp[1], &bp[2], safe, FALSE, e);			break;		  case 'P':		/* set precedence */			if (NumPriorities >= MAXPRIORITIES)			{				toomany('P', MAXPRIORITIES);				break;			}			for (p = &bp[1]; *p != '\0' && *p != '='; p++)				continue;			if (*p == '\0')				goto badline;			*p = '\0';			Priorities[NumPriorities].pri_name = newstr(&bp[1]);			Priorities[NumPriorities].pri_val = atoi(++p);			NumPriorities++;			break;		  case 'V':		/* configuration syntax version */			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)				continue;			if (!isascii(*p) || !isdigit(*p))			{				syserr("invalid argument to V line: \"%.20s\"",					&bp[1]);				break;			}			ConfigLevel = strtol(p, &ep, 10);			/*			**  Do heuristic tweaking for back compatibility.			*/			if (ConfigLevel >= 5)			{				/* level 5 configs have short name in $w */				p = macvalue('w', e);				if (p != NULL && (p = strchr(p, '.')) != NULL)					*p = '\0';				define('w', macvalue('w', e), e);			}			if (ConfigLevel >= 6)			{				ColonOkInAddr = FALSE;			}			/*			**  Look for vendor code.			*/			if (*ep++ == '/')			{				/* extract vendor code */				for (p = ep; isascii(*p) && isalpha(*p); )					p++;				*p = '\0';				if (!setvendor(ep))					syserr("invalid V line vendor code: \"%s\"",						ep);			}			break;		  case 'K':			expand(&bp[1], exbuf, sizeof exbuf, e);			(void) makemapentry(exbuf);			break;		  case 'E':			p = strchr(bp, '=');			if (p != NULL)				*p++ = '\0';			setuserenv(&bp[1], p);			break;#if _FFR_MILTER		  case 'X':		/* mail filter */			milter_setup(&bp[1]);			break;#endif /* _FFR_MILTER */		  default:		  badline:			syserr("unknown configuration line \"%s\"", bp);		}		if (bp != buf)			free(bp);	}	if (ferror(cf))	{		syserr("I/O read error");		finis(FALSE, EX_OSFILE);	}	(void) fclose(cf);	FileName = NULL;	/* initialize host maps from local service tables */	inithostmaps();	/* initialize daemon (if not defined yet) */	initdaemon();	/* determine if we need to do special name-server frotz */	{		int nmaps;		char *maptype[MAXMAPSTACK];		short mapreturn[MAXMAPACTIONS];		nmaps = switch_map_find("hosts", maptype, mapreturn);		UseNameServer = FALSE;		if (nmaps > 0 && nmaps <= MAXMAPSTACK)		{			register int mapno;			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)			{				if (strcmp(maptype[mapno], "dns") == 0)					UseNameServer = TRUE;			}		}#ifdef HESIOD		nmaps = switch_map_find("passwd", maptype, mapreturn);		UseHesiod = FALSE;		if (nmaps > 0 && nmaps <= MAXMAPSTACK)		{			register int mapno;			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)			{				if (strcmp(maptype[mapno], "hesiod") == 0)					UseHesiod = TRUE;			}		}#endif /* HESIOD */	}}/***  TRANSLATE_DOLLARS -- convert $x into internal form****	Actually does all appropriate pre-processing of a config line**	to turn it into internal form.****	Parameters:**		bp -- the buffer to translate.****	Returns:**		None.  The buffer is translated in place.  Since the**		translations always make the buffer shorter, this is**		safe without a size parameter.*/voidtranslate_dollars(bp)	char *bp;{	register char *p;	auto char *ep;	for (p = bp; *p != '\0'; p++)	{		if (*p == '#' && p > bp && ConfigLevel >= 3)		{			/* this is an on-line comment */			register char *e;			switch (*--p & 0377)			{			  case MACROEXPAND:				/* it's from $# -- let it go through */				p++;				break;			  case '\\':				/* it's backslash escaped */				(void) strlcpy(p, p + 1, strlen(p));				break;			  default:				/* delete leading white space */				while (isascii(*p) && isspace(*p) &&				       *p != '\n' && p > bp)					p--;				if ((e = strchr(++p, '\n')) != NULL)					(void) strlcpy(p, e, strlen(p));				else					*p-- = '\0';				break;			}			continue;		}		if (*p != '$' || p[1] == '\0')			continue;		if (p[1] == '$')		{			/* actual dollar sign.... */			(void) strlcpy(p, p + 1, strlen(p));			continue;		}		/* convert to macro expansion character */		*p++ = MACROEXPAND;		/* special handling for $=, $~, $&, and $? */		if (*p == '=' || *p == '~' || *p == '&' || *p == '?')			p++;		/* convert macro name to code */		*p = macid(p, &ep);		if (ep != p + 1)			(void) strlcpy(p + 1, ep, strlen(p + 1));	}	/* strip trailing white space from the line */	while (--p > bp && isascii(*p) && isspace(*p))		*p = '\0';}/***  TOOMANY -- signal too many of some option****	Parameters:**		id -- the id of the error line**		maxcnt -- the maximum possible values****	Returns:**		none.****	Side Effects:**		gives a syserr.*/static voidtoomany(id, maxcnt)	int id;	int maxcnt;{	syserr("too many %c lines, %d max", id, maxcnt);}/***  FILECLASS -- read members of a class from a file****	Parameters:**		class -- class to define.**		filename -- name of file to read.**		fmt -- scanf string to use for match.**		safe -- if set, this is a safe read.**		optional -- if set, it is not an error for the file to**			not exist.****	Returns:**		none****	Side Effects:****		puts all lines in filename that match a scanf into**			the named class.*/static voidfileclass(class, filename, fmt, safe, optional)	int class;	char *filename;	char *fmt;	bool safe;	bool optional;{	FILE *f;	long sff;	pid_t pid;	register char *p;	char buf[MAXLINE];	if (tTd(37, 2))		dprintf("fileclass(%s, fmt=%s)\n", filename, fmt);	if (filename[0] == '|')	{		auto int fd;		int i;		char *argv[MAXPV + 1];		i = 0;		for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))		{			if (i >= MAXPV)				break;			argv[i++] = p;		}		argv[i] = NULL;		pid = prog_open(argv, &fd, CurEnv);		if (pid < 0)			f = NULL;		else			f = fdopen(fd, "r");	}	else	{		pid = -1;		sff = SFF_REGONLY;		if (!bitnset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail))			sff |= SFF_SAFEDIRPATH;		if (!bitnset(DBS_LINKEDCLASSFILEINWRITABLEDIR,			     DontBlameSendmail))			sff |= SFF_NOWLINK;		if (safe)			sff |= SFF_OPENASROOT;		if (DontLockReadFiles)			sff |= SFF_NOLOCK;		f = safefopen(filename, O_RDONLY, 0, sff);	}	if (f == NULL)	{		if (!optional)			syserr("fileclass: cannot open '%s'", filename);		return;	}	while (fgets(buf, sizeof buf, f) != NULL)	{#if SCANF		char wordbuf[MAXLINE + 1];#endif /* SCANF */		if (buf[0] == '#')			continue;#if SCANF		if (sscanf(buf, fmt, wordbuf) != 1)			continue;		p = wordbuf;#else /* SCANF */		p = buf;#endif /* SCANF */		/*		**  Break up the match into words.		*/		while (*p != '\0')		{			register char *q;			/* strip leading spaces */			while (isascii(*p) && isspace(*p))				p++;			if (*p == '\0')				break;			/* find the end of the word */			q = p;			while (*p != '\0' && !(isascii(*p) && isspace(*p)))				p++;			if (*p != '\0')				*p++ = '\0';			/* enter the word in the symbol table */			setclass(class, q);		}	}	(void) fclose(f);	if (pid > 0)		(void) waitfor(pid);}/***  MAKEMAILER -- define a new mailer.****	Parameters:**		line -- description of mailer.  This is in labeled**			fields.  The fields are:**			   A -- the argv for this mailer**			   C -- the character set for MIME conversions**			   D -- the directory to run in**			   E -- the eol string**			   F -- the flags associated with the mailer**			   L -- the maximum line length**			   M -- the maximum message size**			   N -- the niceness at which to run**			   P -- the path to the mailer**			   R -- the recipient rewriting set**			   S -- the sender rewriting set**			   T -- the mailer type (for DSNs)**			   U -- the uid to run as**			   W -- the time to wait at the end**			The first word is the canonical name of the mailer.****	Returns:**		none.****	Side Effects:**		enters the mailer into the mailer table.*/voidmakemailer(line)	char *line;{	register char *p;	register struct mailer *m;	register STAB *s;	int i;	char fcode;	auto char *endp;	extern int NextMailer;	/* allocate a mailer and set up defaults */	m = (struct mailer *) xalloc(sizeof *m);	memset((char *) m, '\0', sizeof *m);	/* collect the mailer name */	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)		continue;	if (*p != '\0')		*p++ = '\0';	if (line[0] == '\0')	{		syserr("name required for mailer");		return;	}	m->m_name = newstr(line);	/* now scan through and assign info from the fields */	while (*p != '\0')	{		auto char *delimptr;		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))			p++;		/* p now points to field code */		fcode = *p;		while (*p != '\0' && *p != '=' && *p != ',')			p++;		if (*p++ != '=')		{			syserr("mailer %s: `=' expected", m->m_name);			return;		}		while (isascii(*p) && isspace(*p))			p++;		/* p now points to the field body */		p = munchstring(p, &delimptr, ',');		/* install the field into the mailer struct */		switch (fcode)		{		  case 'P':		/* pathname */			if (*p == '\0')				syserr("mailer %s: empty path name", m->m_name);			else				m->m_mailer = newstr(p);			break;		  case 'F':		/* flags */			for (; *p != '\0'; p++)				if (!(isascii(*p) && isspace(*p)))					setbitn(bitidx(*p), m->m_flags);			break;		  case 'S':		/* sender rewriting ruleset */		  case 'R':		/* recipient rewriting ruleset */			i = strtorwset(p, &endp, ST_ENTER);			if (i < 0)				return;			if (fcode == 'S')				m->m_sh_rwset = m->m_se_rwset = i;			else				m->m_rh_rwset = m->m_re_rwset = i;			p = endp;			if (*p++ == '/')			{				i = strtorwset(p, NULL, ST_ENTER);				if (i < 0)					return;				if (fcode == 'S')					m->m_sh_rwset = i;				else					m->m_rh_rwset = i;			}			break;		  case 'E':		/* end of line string */			if (*p == '\0')				syserr("mailer %s: null end-of-line string",					m->m_name);			else				m->m_eol = newstr(p);			break;		  case 'A':		/* argument vector */			if (*p == '\0')				syserr("mailer %s: null argument vector",					m->m_name);			else				m->m_argv = makeargv(p);			break;		  case 'M':		/* maximum message size */			m->m_maxsize = atol(p);			break;		  case 'm':		/* maximum messages per connection */			m->m_maxdeliveries = atoi(p);			break;#if _FFR_DYNAMIC_TOBUF		  case 'r':		/* max recipient per envelope */			m->m_maxrcpt = atoi(p);			break;#endif /* _FFR_DYNAMIC_TOBUF */		  case 'L':		/* maximum line length */			m->m_linelimit = atoi(p);			if (m->m_linelimit < 0)				m->m_linelimit = 0;			break;		  case 'N':		/* run niceness */			m->m_nice = atoi(p);			break;		  case 'D':		/* working directory */			if (*p == '\0')				syserr("mailer %s: null working directory",					m->m_name);			else				m->m_execdir = newstr(p);			break;		  case 'C':		/* default charset */			if (*p == '\0')				syserr("mailer %s: null charset", m->m_name);			else				m->m_defcharset = newstr(p);			break;		  case 'T':		/* MTA-Name/Address/Diagnostic types */			/* extract MTA name type; default to "dns" */			m->m_mtatype = newstr(p);			p = strchr(m->m_mtatype, '/');			if (p != NULL)			{				*p++ = '\0';				if (*p == '\0')					p = NULL;			}			if (*m->m_mtatype == '\0')				m->m_mtatype = "dns";			/* extract address type; default to "rfc822" */			m->m_addrtype = p;			if (p != NULL)				p = strchr(p, '/');			if (p != NULL)			{				*p++ = '\0';				if (*p == '\0')					p = NULL;			}			if (m->m_addrtype == NULL || *m->m_addrtype == '\0')				m->m_addrtype = "rfc822";			/* extract diagnostic type; default to "smtp" */			m->m_diagtype = p;			if (m->m_diagtype == NULL || *m->m_diagtype == '\0')				m->m_diagtype = "smtp";			break;		  case 'U':		/* user id */			if (isascii(*p) && !isdigit(*p))			{				char *q = p;				struct passwd *pw;				while (*p != '\0' && isascii(*p) &&				       (isalnum(*p) || strchr("-_", *p) != NULL))					p++;				while (isascii(*p) && isspace(*p))					*p++ = '\0';				if (*p != '\0')					*p++ = '\0';				if (*q == '\0')				{					syserr("mailer %s: null user name",						m->m_name);					break;				}				pw = sm_getpwnam(q);				if (pw == NULL)				{					syserr("readcf: mailer U= flag: unknown user %s", q);					break;				}				else				{					m->m_uid = pw->pw_uid;					m->m_gid = pw->pw_gid;				}			}			else			{				auto char *q;				m->m_uid = strtol(p, &q, 0);				p = q;				while (isascii(*p) && isspace(*p))					p++;				if (*p != '\0')					p++;			}			while (isascii(*p) && isspace(*p))				p++;			if (*p == '\0')				break;			if (isascii(*p) && !isdigit(*p))			{				char *q = p;				struct group *gr;				while (isascii(*p) && isalnum(*p))					p++;				*p++ = '\0';				if (*q == '\0')				{					syserr("mailer %s: null group name",						m->m_name);					break;				}				gr = getgrnam(q);				if (gr == NULL)				{					syserr("readcf: mailer U= flag: unknown group %s", q);					break;				}				else					m->m_gid = gr->gr_gid;			}			else			{				m->m_gid = strtol(p, NULL, 0);			}			break;		  case 'W':		/* wait timeout */			m->m_wait = convtime(p, 's');			break;		  case '/':		/* new root directory */			if (*p == '\0')				syserr("mailer %s: null root directory",					m->m_name);			else				m->m_rootdir = newstr(p);			break;		  default:			syserr("M%s: unknown mailer equate %c=",			       m->m_name, fcode);			break;		}		p = delimptr;	}	/* do some rationality checking */	if (m->m_argv == NULL)	{		syserr("M%s: A= argument required", m->m_name);		return;	}	if (m->m_mailer == NULL)	{		syserr("M%s: P= argument required", m->m_name);		return;	}	if (NextMailer >= MAXMAILERS)	{		syserr("too many mailers defined (%d max)", MAXMAILERS);		return;	}#if _FFR_DYNAMIC_TOBUF	if (m->m_maxrcpt <= 0)		m->m_maxrcpt = DEFAULT_MAX_RCPT;#endif /* _FFR_DYNAMIC_TOBUF */	/* do some heuristic cleanup for back compatibility */	if (bitnset(M_LIMITS, m->m_flags))	{		if (m->m_linelimit == 0)			m->m_linelimit = SMTPLINELIM;		if (ConfigLevel < 2)			setbitn(M_7BITS, m->m_flags);	}	if (strcmp(m->m_mailer, "[TCP]") == 0)	{#if _FFR_REMOVE_TCP_MAILER_PATH		syserr("M%s: P=[TCP] is deprecated, use P=[IPC] instead\n",		       m->m_name);		return;#else /* _FFR_REMOVE_TCP_MAILER_PATH */		printf("M%s: Warning: P=[TCP] is deprecated, use P=[IPC] instead\n",		       m->m_name);#endif /* _FFR_REMOVE_TCP_MAILER_PATH */	}	if (strcmp(m->m_mailer, "[IPC]") == 0#if !_FFR_REMOVE_TCP_MAILER_PATH	    || strcmp(m->m_mailer, "[TCP]") == 0#endif /* !_FFR_REMOVE_TCP_MAILER_PATH */	    )	{		/* Use the second argument for host or path to socket */		if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||		    m->m_argv[1][0] == '\0')		{			syserr("M%s: too few parameters for %s mailer",			       m->m_name, m->m_mailer);			return;		}		if (strcmp(m->m_argv[0], "TCP") != 0#if NETUNIX		    && strcmp(m->m_argv[0], "FILE") != 0#endif /* NETUNIX */#if !_FFR_DEPRECATE_IPC_MAILER_ARG		    && strcmp(m->m_argv[0], "IPC") != 0#endif /* !_FFR_DEPRECATE_IPC_MAILER_ARG */		    )		{			printf("M%s: Warning: first argument in %s mailer must be %s\n",			       m->m_name, m->m_mailer,#if NETUNIX			       "TCP or FILE"#else /* NETUNIX */			       "TCP"#endif /* NETUNIX */			       );		}	}	else if (strcmp(m->m_mailer, "[FILE]") == 0)	{		/* Use the second argument for filename */		if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||		    m->m_argv[2] != NULL)		{			syserr("M%s: too %s parameters for [FILE] mailer",			       m->m_name,			       (m->m_argv[0] == NULL ||				m->m_argv[1] == NULL) ? "few" : "many");			return;		}		else if (strcmp(m->m_argv[0], "FILE") != 0)		{			syserr("M%s: first argument in [FILE] mailer must be FILE",			       m->m_name);			return;		}	}	if (strcmp(m->m_mailer, "[IPC]") == 0 ||	    strcmp(m->m_mailer, "[TCP]") == 0)	{		if (m->m_mtatype == NULL)			m->m_mtatype = "dns";		if (m->m_addrtype == NULL)			m->m_addrtype = "rfc822";		if (m->m_diagtype == NULL)		{			if (m->m_argv[0] != NULL &&			    strcmp(m->m_argv[0], "FILE") == 0)				m->m_diagtype = "x-unix";			else				m->m_diagtype = "smtp";		}	}	if (m->m_eol == NULL)	{		char **pp;		/* default for SMTP is \r\n; use \n for local delivery */		for (pp = m->m_argv; *pp != NULL; pp++)		{			for (p = *pp; *p != '\0'; )			{				if ((*p++ & 0377) == MACROEXPAND && *p == 'u')					break;			}			if (*p != '\0')				break;		}		if (*pp == NULL)			m->m_eol = "\r\n";		else			m->m_eol = "\n";	}	/* enter the mailer into the symbol table */	s = stab(m->m_name, ST_MAILER, ST_ENTER);	if (s->s_mailer != NULL)	{		i = s->s_mailer->m_mno;		free(s->s_mailer);	}	else	{		i = NextMailer++;	}	Mailer[i] = s->s_mailer = m;	m->m_mno = i;}/***  MUNCHSTRING -- translate a string into internal form.****	Parameters:**		p -- the string to munch.**		delimptr -- if non-NULL, set to the pointer of the**			field delimiter character.**		delim -- the delimiter for the field.****	Returns:**		the munched string.****	Side Effects:**		the munched string is a local static buffer.**		it must be copied before the function is called again.*/char *munchstring(p, delimptr, delim)	register char *p;	char **delimptr;	int delim;{	register char *q;	bool backslash = FALSE;	bool quotemode = FALSE;	static char buf[MAXLINE];	for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++)	{		if (backslash)		{			/* everything is roughly literal */			backslash = FALSE;			switch (*p)			{			  case 'r':		/* carriage return */				*q++ = '\r';				continue;			  case 'n':		/* newline */				*q++ = '\n';				continue;			  case 'f':		/* form feed */				*q++ = '\f';				continue;			  case 'b':		/* backspace */				*q++ = '\b';				continue;			}			*q++ = *p;		}		else		{			if (*p == '\\')				backslash = TRUE;			else if (*p == '"')				quotemode = !quotemode;			else if (quotemode || *p != delim)				*q++ = *p;			else				break;		}	}	if (delimptr != NULL)		*delimptr = p;	*q++ = '\0';	return buf;}/***  MAKEARGV -- break up a string into words****	Parameters:**		p -- the string to break up.****	Returns:**		a char **argv (dynamically allocated)****	Side Effects:**		munges p.*/static char **makeargv(p)	register char *p;{	char *q;	int i;	char **avp;	char *argv[MAXPV + 1];	/* take apart the words */	i = 0;	while (*p != '\0' && i < MAXPV)	{		q = p;		while (*p != '\0' && !(isascii(*p) && isspace(*p)))			p++;		while (isascii(*p) && isspace(*p))			*p++ = '\0';		argv[i++] = newstr(q);	}	argv[i++] = NULL;	/* now make a copy of the argv */	avp = (char **) xalloc(sizeof *avp * i);	memmove((char *) avp, (char *) argv, sizeof *avp * i);	return avp;}