util.c revision 77349
1/*
2 * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#ifndef lint
15static char id[] = "@(#)$Id: util.c,v 8.225.2.1.2.23 2001/05/17 18:10:18 gshapiro Exp $";
16#endif /* ! lint */
17
18#include <sendmail.h>
19#include <sysexits.h>
20
21
22static void	readtimeout __P((time_t));
23
24/*
25**  STRIPQUOTES -- Strip quotes & quote bits from a string.
26**
27**	Runs through a string and strips off unquoted quote
28**	characters and quote bits.  This is done in place.
29**
30**	Parameters:
31**		s -- the string to strip.
32**
33**	Returns:
34**		none.
35**
36**	Side Effects:
37**		none.
38*/
39
40void
41stripquotes(s)
42	char *s;
43{
44	register char *p;
45	register char *q;
46	register char c;
47
48	if (s == NULL)
49		return;
50
51	p = q = s;
52	do
53	{
54		c = *p++;
55		if (c == '\\')
56			c = *p++;
57		else if (c == '"')
58			continue;
59		*q++ = c;
60	} while (c != '\0');
61}
62/*
63**  ADDQUOTES -- Adds quotes & quote bits to a string.
64**
65**	Runs through a string and adds characters and quote bits.
66**
67**	Parameters:
68**		s -- the string to modify.
69**
70**	Returns:
71**		pointer to quoted string.
72**
73**	Side Effects:
74**		none.
75**
76*/
77
78char *
79addquotes(s)
80	char *s;
81{
82	int len = 0;
83	char c;
84	char *p = s, *q, *r;
85
86	if (s == NULL)
87		return NULL;
88
89	/* Find length of quoted string */
90	while ((c = *p++) != '\0')
91	{
92		len++;
93		if (c == '\\' || c == '"')
94			len++;
95	}
96
97	q = r = xalloc(len + 3);
98	p = s;
99
100	/* add leading quote */
101	*q++ = '"';
102	while ((c = *p++) != '\0')
103	{
104		/* quote \ or " */
105		if (c == '\\' || c == '"')
106			*q++ = '\\';
107		*q++ = c;
108	}
109	*q++ = '"';
110	*q = '\0';
111	return r;
112}
113/*
114**  RFC822_STRING -- Checks string for proper RFC822 string quoting.
115**
116**	Runs through a string and verifies RFC822 special characters
117**	are only found inside comments, quoted strings, or backslash
118**	escaped.  Also verified balanced quotes and parenthesis.
119**
120**	Parameters:
121**		s -- the string to modify.
122**
123**	Returns:
124**		TRUE -- if the string is RFC822 compliant.
125**		FALSE -- if the string is not RFC822 compliant.
126**
127**	Side Effects:
128**		none.
129**
130*/
131
132bool
133rfc822_string(s)
134	char *s;
135{
136	bool quoted = FALSE;
137	int commentlev = 0;
138	char *c = s;
139
140	if (s == NULL)
141		return FALSE;
142
143	while (*c != '\0')
144	{
145		/* escaped character */
146		if (*c == '\\')
147		{
148			c++;
149			if (*c == '\0')
150				return FALSE;
151		}
152		else if (commentlev == 0 && *c == '"')
153			quoted = !quoted;
154		else if (!quoted)
155		{
156			if (*c == ')')
157			{
158				/* unbalanced ')' */
159				if (commentlev == 0)
160					return FALSE;
161				else
162					commentlev--;
163			}
164			else if (*c == '(')
165				commentlev++;
166			else if (commentlev == 0 &&
167				 strchr(MustQuoteChars, *c) != NULL)
168				return FALSE;
169		}
170		c++;
171	}
172	/* unbalanced '"' or '(' */
173	if (quoted || commentlev != 0)
174		return FALSE;
175	else
176		return TRUE;
177}
178/*
179**  SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
180**
181**	Arbitrarily shorten (in place) an RFC822 string and rebalance
182**	comments and quotes.
183**
184**	Parameters:
185**		string -- the string to shorten
186**		length -- the maximum size, 0 if no maximum
187**
188**	Returns:
189**		TRUE if string is changed, FALSE otherwise
190**
191**	Side Effects:
192**		Changes string in place, possibly resulting
193**		in a shorter string.
194*/
195
196bool
197shorten_rfc822_string(string, length)
198	char *string;
199	size_t length;
200{
201	bool backslash = FALSE;
202	bool modified = FALSE;
203	bool quoted = FALSE;
204	size_t slen;
205	int parencount = 0;
206	char *ptr = string;
207
208	/*
209	**  If have to rebalance an already short enough string,
210	**  need to do it within allocated space.
211	*/
212
213	slen = strlen(string);
214	if (length == 0 || slen < length)
215		length = slen;
216
217	while (*ptr != '\0')
218	{
219		if (backslash)
220		{
221			backslash = FALSE;
222			goto increment;
223		}
224
225		if (*ptr == '\\')
226			backslash = TRUE;
227		else if (*ptr == '(')
228		{
229			if (!quoted)
230				parencount++;
231		}
232		else if (*ptr == ')')
233		{
234			if (--parencount < 0)
235				parencount = 0;
236		}
237
238		/* Inside a comment, quotes don't matter */
239		if (parencount <= 0 && *ptr == '"')
240			quoted = !quoted;
241
242increment:
243		/* Check for sufficient space for next character */
244		if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
245						parencount +
246						(quoted ? 1 : 0)))
247		{
248			/* Not enough, backtrack */
249			if (*ptr == '\\')
250				backslash = FALSE;
251			else if (*ptr == '(' && !quoted)
252				parencount--;
253			else if (*ptr == '"' && parencount == 0)
254				quoted = FALSE;
255			break;
256		}
257		ptr++;
258	}
259
260	/* Rebalance */
261	while (parencount-- > 0)
262	{
263		if (*ptr != ')')
264		{
265			modified = TRUE;
266			*ptr = ')';
267		}
268		ptr++;
269	}
270	if (quoted)
271	{
272		if (*ptr != '"')
273		{
274			modified = TRUE;
275			*ptr = '"';
276		}
277		ptr++;
278	}
279	if (*ptr != '\0')
280	{
281		modified = TRUE;
282		*ptr = '\0';
283	}
284	return modified;
285}
286/*
287**  FIND_CHARACTER -- find an unquoted character in an RFC822 string
288**
289**	Find an unquoted, non-commented character in an RFC822
290**	string and return a pointer to its location in the
291**	string.
292**
293**	Parameters:
294**		string -- the string to search
295**		character -- the character to find
296**
297**	Returns:
298**		pointer to the character, or
299**		a pointer to the end of the line if character is not found
300*/
301
302char *
303find_character(string, character)
304	char *string;
305	int character;
306{
307	bool backslash = FALSE;
308	bool quoted = FALSE;
309	int parencount = 0;
310
311	while (string != NULL && *string != '\0')
312	{
313		if (backslash)
314		{
315			backslash = FALSE;
316			if (!quoted && character == '\\' && *string == '\\')
317				break;
318			string++;
319			continue;
320		}
321		switch (*string)
322		{
323		  case '\\':
324			backslash = TRUE;
325			break;
326
327		  case '(':
328			if (!quoted)
329				parencount++;
330			break;
331
332		  case ')':
333			if (--parencount < 0)
334				parencount = 0;
335			break;
336		}
337
338		/* Inside a comment, nothing matters */
339		if (parencount > 0)
340		{
341			string++;
342			continue;
343		}
344
345		if (*string == '"')
346			quoted = !quoted;
347		else if (*string == character && !quoted)
348			break;
349		string++;
350	}
351
352	/* Return pointer to the character */
353	return string;
354}
355/*
356**  XALLOC -- Allocate memory and bitch wildly on failure.
357**
358**	THIS IS A CLUDGE.  This should be made to give a proper
359**	error -- but after all, what can we do?
360**
361**	Parameters:
362**		sz -- size of area to allocate.
363**
364**	Returns:
365**		pointer to data region.
366**
367**	Side Effects:
368**		Memory is allocated.
369*/
370
371char *
372xalloc(sz)
373	register int sz;
374{
375	register char *p;
376
377	/* some systems can't handle size zero mallocs */
378	if (sz <= 0)
379		sz = 1;
380
381	ENTER_CRITICAL();
382	p = malloc((unsigned) sz);
383	LEAVE_CRITICAL();
384	if (p == NULL)
385	{
386		syserr("!Out of memory!!");
387
388		/* NOTREACHED */
389		exit(EX_UNAVAILABLE);
390	}
391	return p;
392}
393/*
394**  XREALLOC -- Reallocate memory and bitch wildly on failure.
395**
396**	THIS IS A CLUDGE.  This should be made to give a proper
397**	error -- but after all, what can we do?
398**
399**	Parameters:
400**		ptr -- original area.
401**		sz -- size of new area to allocate.
402**
403**	Returns:
404**		pointer to data region.
405**
406**	Side Effects:
407**		Memory is allocated.
408*/
409
410char *
411xrealloc(ptr, sz)
412	void *ptr;
413	size_t sz;
414{
415	register char *p;
416
417	/* some systems can't handle size zero mallocs */
418	if (sz <= 0)
419		sz = 1;
420
421	ENTER_CRITICAL();
422	p = realloc(ptr, (unsigned) sz);
423	LEAVE_CRITICAL();
424	if (p == NULL)
425	{
426		syserr("!Out of memory!!");
427
428		/* NOTREACHED */
429		exit(EX_UNAVAILABLE);
430	}
431	return p;
432}
433/*
434**  XCALLOC -- Allocate memory and bitch wildly on failure.
435**
436**	THIS IS A CLUDGE.  This should be made to give a proper
437**	error -- but after all, what can we do?
438**
439**	Parameters:
440**		num -- number of items to allocate
441**		sz -- size of new area to allocate.
442**
443**	Returns:
444**		pointer to data region.
445**
446**	Side Effects:
447**		Memory is allocated.
448*/
449
450char *
451xcalloc(num, sz)
452	size_t num;
453	size_t sz;
454{
455	register char *p;
456
457	/* some systems can't handle size zero mallocs */
458	if (num <= 0)
459		num = 1;
460	if (sz <= 0)
461		sz = 1;
462
463	ENTER_CRITICAL();
464	p = calloc((unsigned) num, (unsigned) sz);
465	LEAVE_CRITICAL();
466	if (p == NULL)
467	{
468		syserr("!Out of memory!!");
469
470		/* NOTREACHED */
471		exit(EX_UNAVAILABLE);
472	}
473	return p;
474}
475/*
476**  SM_FREE -- Free memory safely.
477**
478**	Parameters:
479**		ptr -- area to free
480**
481**	Returns:
482**		none.
483**
484**	Side Effects:
485**		Memory is freed.
486*/
487
488void
489sm_free(ptr)
490	void *ptr;
491{
492	ENTER_CRITICAL();
493	free(ptr);
494	LEAVE_CRITICAL();
495}
496/*
497**  COPYPLIST -- copy list of pointers.
498**
499**	This routine is the equivalent of newstr for lists of
500**	pointers.
501**
502**	Parameters:
503**		list -- list of pointers to copy.
504**			Must be NULL terminated.
505**		copycont -- if TRUE, copy the contents of the vector
506**			(which must be a string) also.
507**
508**	Returns:
509**		a copy of 'list'.
510**
511**	Side Effects:
512**		none.
513*/
514
515char **
516copyplist(list, copycont)
517	char **list;
518	bool copycont;
519{
520	register char **vp;
521	register char **newvp;
522
523	for (vp = list; *vp != NULL; vp++)
524		continue;
525
526	vp++;
527
528	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
529	memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp);
530
531	if (copycont)
532	{
533		for (vp = newvp; *vp != NULL; vp++)
534			*vp = newstr(*vp);
535	}
536
537	return newvp;
538}
539/*
540**  COPYQUEUE -- copy address queue.
541**
542**	This routine is the equivalent of newstr for address queues
543**	addresses marked as QS_IS_DEAD() aren't copied
544**
545**	Parameters:
546**		addr -- list of address structures to copy.
547**
548**	Returns:
549**		a copy of 'addr'.
550**
551**	Side Effects:
552**		none.
553*/
554
555ADDRESS *
556copyqueue(addr)
557	ADDRESS *addr;
558{
559	register ADDRESS *newaddr;
560	ADDRESS *ret;
561	register ADDRESS **tail = &ret;
562
563	while (addr != NULL)
564	{
565		if (!QS_IS_DEAD(addr->q_state))
566		{
567			newaddr = (ADDRESS *) xalloc(sizeof *newaddr);
568			STRUCTCOPY(*addr, *newaddr);
569			*tail = newaddr;
570			tail = &newaddr->q_next;
571		}
572		addr = addr->q_next;
573	}
574	*tail = NULL;
575
576	return ret;
577}
578/*
579**  LOG_SENDMAIL_PID -- record sendmail pid and command line.
580**
581**	Parameters:
582**		e -- the current envelope.
583**
584**	Returns:
585**		none.
586**
587**	Side Effects:
588**		writes pidfile.
589*/
590
591void
592log_sendmail_pid(e)
593	ENVELOPE *e;
594{
595	long sff;
596	FILE *pidf;
597	char pidpath[MAXPATHLEN + 1];
598
599	/* write the pid to the log file for posterity */
600	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
601	if (TrustedUid != 0 && RealUid == TrustedUid)
602		sff |= SFF_OPENASROOT;
603	expand(PidFile, pidpath, sizeof pidpath, e);
604	pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, 0644, sff);
605	if (pidf == NULL)
606	{
607		sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
608			  pidpath, errstring(errno));
609	}
610	else
611	{
612		pid_t pid;
613		extern char *CommandLineArgs;
614
615		pid = getpid();
616
617		/* write the process id on line 1 */
618		fprintf(pidf, "%ld\n", (long) pid);
619
620		/* line 2 contains all command line flags */
621		fprintf(pidf, "%s\n", CommandLineArgs);
622
623		/* flush and close */
624		(void) fclose(pidf);
625	}
626}
627/*
628**  SET_DELIVERY_MODE -- set and record the delivery mode
629**
630**	Parameters:
631**		mode -- delivery mode
632**		e -- the current envelope.
633**
634**	Returns:
635**		none.
636**
637**	Side Effects:
638**		sets $&{deliveryMode} macro
639*/
640
641void
642set_delivery_mode(mode, e)
643	int mode;
644	ENVELOPE *e;
645{
646	char buf[2];
647
648	e->e_sendmode = (char)mode;
649	buf[0] = (char)mode;
650	buf[1] = '\0';
651	define(macid("{deliveryMode}", NULL), newstr(buf), e);
652}
653/*
654**  PRINTAV -- print argument vector.
655**
656**	Parameters:
657**		av -- argument vector.
658**
659**	Returns:
660**		none.
661**
662**	Side Effects:
663**		prints av.
664*/
665
666void
667printav(av)
668	register char **av;
669{
670	while (*av != NULL)
671	{
672		if (tTd(0, 44))
673			dprintf("\n\t%08lx=", (u_long) *av);
674		else
675			(void) putchar(' ');
676		xputs(*av++);
677	}
678	(void) putchar('\n');
679}
680/*
681**  LOWER -- turn letter into lower case.
682**
683**	Parameters:
684**		c -- character to turn into lower case.
685**
686**	Returns:
687**		c, in lower case.
688**
689**	Side Effects:
690**		none.
691*/
692
693char
694lower(c)
695	register int c;
696{
697	return ((isascii(c) && isupper(c)) ? tolower(c) : c);
698}
699/*
700**  XPUTS -- put string doing control escapes.
701**
702**	Parameters:
703**		s -- string to put.
704**
705**	Returns:
706**		none.
707**
708**	Side Effects:
709**		output to stdout
710*/
711
712void
713xputs(s)
714	register const char *s;
715{
716	register int c;
717	register struct metamac *mp;
718	bool shiftout = FALSE;
719	extern struct metamac MetaMacros[];
720
721	if (s == NULL)
722	{
723		printf("%s<null>%s", TermEscape.te_rv_on, TermEscape.te_rv_off);
724		return;
725	}
726	while ((c = (*s++ & 0377)) != '\0')
727	{
728		if (shiftout)
729		{
730			printf("%s", TermEscape.te_rv_off);
731			shiftout = FALSE;
732		}
733		if (!isascii(c))
734		{
735			if (c == MATCHREPL)
736			{
737				printf("%s$", TermEscape.te_rv_on);
738				shiftout = TRUE;
739				if (*s == '\0')
740					continue;
741				c = *s++ & 0377;
742				goto printchar;
743			}
744			if (c == MACROEXPAND || c == MACRODEXPAND)
745			{
746				printf("%s$", TermEscape.te_rv_on);
747				if (c == MACRODEXPAND)
748					(void) putchar('&');
749				shiftout = TRUE;
750				if (*s == '\0')
751					continue;
752				if (strchr("=~&?", *s) != NULL)
753					(void) putchar(*s++);
754				if (bitset(0200, *s))
755					printf("{%s}", macname(bitidx(*s++)));
756				else
757					printf("%c", *s++);
758				continue;
759			}
760			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
761			{
762				if ((mp->metaval & 0377) == c)
763				{
764					printf("%s$%c",
765						TermEscape.te_rv_on,
766						mp->metaname);
767					shiftout = TRUE;
768					break;
769				}
770			}
771			if (c == MATCHCLASS || c == MATCHNCLASS)
772			{
773				if (bitset(0200, *s))
774					printf("{%s}", macname(*s++ & 0377));
775				else if (*s != '\0')
776					printf("%c", *s++);
777			}
778			if (mp->metaname != '\0')
779				continue;
780
781			/* unrecognized meta character */
782			printf("%sM-", TermEscape.te_rv_on);
783			shiftout = TRUE;
784			c &= 0177;
785		}
786  printchar:
787		if (isprint(c))
788		{
789			(void) putchar(c);
790			continue;
791		}
792
793		/* wasn't a meta-macro -- find another way to print it */
794		switch (c)
795		{
796		  case '\n':
797			c = 'n';
798			break;
799
800		  case '\r':
801			c = 'r';
802			break;
803
804		  case '\t':
805			c = 't';
806			break;
807		}
808		if (!shiftout)
809		{
810			printf("%s", TermEscape.te_rv_on);
811			shiftout = TRUE;
812		}
813		if (isprint(c))
814		{
815			(void) putchar('\\');
816			(void) putchar(c);
817		}
818		else
819		{
820			(void) putchar('^');
821			(void) putchar(c ^ 0100);
822		}
823	}
824	if (shiftout)
825		printf("%s", TermEscape.te_rv_off);
826	(void) fflush(stdout);
827}
828/*
829**  MAKELOWER -- Translate a line into lower case
830**
831**	Parameters:
832**		p -- the string to translate.  If NULL, return is
833**			immediate.
834**
835**	Returns:
836**		none.
837**
838**	Side Effects:
839**		String pointed to by p is translated to lower case.
840*/
841
842void
843makelower(p)
844	register char *p;
845{
846	register char c;
847
848	if (p == NULL)
849		return;
850	for (; (c = *p) != '\0'; p++)
851		if (isascii(c) && isupper(c))
852			*p = tolower(c);
853}
854/*
855**  BUILDFNAME -- build full name from gecos style entry.
856**
857**	This routine interprets the strange entry that would appear
858**	in the GECOS field of the password file.
859**
860**	Parameters:
861**		p -- name to build.
862**		user -- the login name of this user (for &).
863**		buf -- place to put the result.
864**		buflen -- length of buf.
865**
866**	Returns:
867**		none.
868**
869**	Side Effects:
870**		none.
871*/
872
873void
874buildfname(gecos, user, buf, buflen)
875	register char *gecos;
876	char *user;
877	char *buf;
878	int buflen;
879{
880	register char *p;
881	register char *bp = buf;
882
883	if (*gecos == '*')
884		gecos++;
885
886	/* copy gecos, interpolating & to be full name */
887	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
888	{
889		if (bp >= &buf[buflen - 1])
890		{
891			/* buffer overflow -- just use login name */
892			snprintf(buf, buflen, "%s", user);
893			return;
894		}
895		if (*p == '&')
896		{
897			/* interpolate full name */
898			snprintf(bp, buflen - (bp - buf), "%s", user);
899			*bp = toupper(*bp);
900			bp += strlen(bp);
901		}
902		else
903			*bp++ = *p;
904	}
905	*bp = '\0';
906}
907/*
908**  FIXCRLF -- fix <CR><LF> in line.
909**
910**	Looks for the <CR><LF> combination and turns it into the
911**	UNIX canonical <NL> character.  It only takes one line,
912**	i.e., it is assumed that the first <NL> found is the end
913**	of the line.
914**
915**	Parameters:
916**		line -- the line to fix.
917**		stripnl -- if true, strip the newline also.
918**
919**	Returns:
920**		none.
921**
922**	Side Effects:
923**		line is changed in place.
924*/
925
926void
927fixcrlf(line, stripnl)
928	char *line;
929	bool stripnl;
930{
931	register char *p;
932
933	p = strchr(line, '\n');
934	if (p == NULL)
935		return;
936	if (p > line && p[-1] == '\r')
937		p--;
938	if (!stripnl)
939		*p++ = '\n';
940	*p = '\0';
941}
942/*
943**  PUTLINE -- put a line like fputs obeying SMTP conventions
944**
945**	This routine always guarantees outputing a newline (or CRLF,
946**	as appropriate) at the end of the string.
947**
948**	Parameters:
949**		l -- line to put.
950**		mci -- the mailer connection information.
951**
952**	Returns:
953**		none
954**
955**	Side Effects:
956**		output of l to fp.
957*/
958
959void
960putline(l, mci)
961	register char *l;
962	register MCI *mci;
963{
964	putxline(l, strlen(l), mci, PXLF_MAPFROM);
965}
966/*
967**  PUTXLINE -- putline with flags bits.
968**
969**	This routine always guarantees outputing a newline (or CRLF,
970**	as appropriate) at the end of the string.
971**
972**	Parameters:
973**		l -- line to put.
974**		len -- the length of the line.
975**		mci -- the mailer connection information.
976**		pxflags -- flag bits:
977**		    PXLF_MAPFROM -- map From_ to >From_.
978**		    PXLF_STRIP8BIT -- strip 8th bit.
979**		    PXLF_HEADER -- map bare newline in header to newline space.
980**
981**	Returns:
982**		none
983**
984**	Side Effects:
985**		output of l to fp.
986*/
987
988void
989putxline(l, len, mci, pxflags)
990	register char *l;
991	size_t len;
992	register MCI *mci;
993	int pxflags;
994{
995	bool dead = FALSE;
996	register char *p, *end;
997	int slop = 0;
998
999	/* strip out 0200 bits -- these can look like TELNET protocol */
1000	if (bitset(MCIF_7BIT, mci->mci_flags) ||
1001	    bitset(PXLF_STRIP8BIT, pxflags))
1002	{
1003		register char svchar;
1004
1005		for (p = l; (svchar = *p) != '\0'; ++p)
1006			if (bitset(0200, svchar))
1007				*p = svchar &~ 0200;
1008	}
1009
1010	end = l + len;
1011	do
1012	{
1013		/* find the end of the line */
1014		p = memchr(l, '\n', end - l);
1015		if (p == NULL)
1016			p = end;
1017
1018		if (TrafficLogFile != NULL)
1019			fprintf(TrafficLogFile, "%05d >>> ", (int) getpid());
1020
1021		/* check for line overflow */
1022		while (mci->mci_mailer->m_linelimit > 0 &&
1023		       (p - l + slop) > mci->mci_mailer->m_linelimit)
1024		{
1025			char *l_base = l;
1026			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
1027
1028			if (l[0] == '.' && slop == 0 &&
1029			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
1030			{
1031				if (putc('.', mci->mci_out) == EOF)
1032					dead = TRUE;
1033				else
1034				{
1035					/* record progress for DATA timeout */
1036					DataProgress = TRUE;
1037				}
1038				if (TrafficLogFile != NULL)
1039					(void) putc('.', TrafficLogFile);
1040			}
1041			else if (l[0] == 'F' && slop == 0 &&
1042				 bitset(PXLF_MAPFROM, pxflags) &&
1043				 strncmp(l, "From ", 5) == 0 &&
1044				 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1045			{
1046				if (putc('>', mci->mci_out) == EOF)
1047					dead = TRUE;
1048				else
1049				{
1050					/* record progress for DATA timeout */
1051					DataProgress = TRUE;
1052				}
1053				if (TrafficLogFile != NULL)
1054					(void) putc('>', TrafficLogFile);
1055			}
1056			if (dead)
1057				break;
1058
1059			while (l < q)
1060			{
1061				if (putc((unsigned char) *l++, mci->mci_out) ==
1062				    EOF)
1063				{
1064					dead = TRUE;
1065					break;
1066				}
1067				else
1068				{
1069					/* record progress for DATA timeout */
1070					DataProgress = TRUE;
1071				}
1072			}
1073			if (dead)
1074				break;
1075
1076			if (putc('!', mci->mci_out) == EOF ||
1077			    fputs(mci->mci_mailer->m_eol,
1078				  mci->mci_out) == EOF ||
1079			    putc(' ', mci->mci_out) == EOF)
1080			{
1081				dead = TRUE;
1082				break;
1083			}
1084			else
1085			{
1086				/* record progress for DATA timeout */
1087				DataProgress = TRUE;
1088			}
1089			if (TrafficLogFile != NULL)
1090			{
1091				for (l = l_base; l < q; l++)
1092					(void) putc((unsigned char)*l,
1093						    TrafficLogFile);
1094				fprintf(TrafficLogFile, "!\n%05d >>>  ",
1095					(int) getpid());
1096			}
1097			slop = 1;
1098		}
1099
1100		if (dead)
1101			break;
1102
1103		/* output last part */
1104		if (l[0] == '.' && slop == 0 &&
1105		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
1106		{
1107			if (putc('.', mci->mci_out) == EOF)
1108				break;
1109			else
1110			{
1111				/* record progress for DATA timeout */
1112				DataProgress = TRUE;
1113			}
1114			if (TrafficLogFile != NULL)
1115				(void) putc('.', TrafficLogFile);
1116		}
1117		else if (l[0] == 'F' && slop == 0 &&
1118			 bitset(PXLF_MAPFROM, pxflags) &&
1119			 strncmp(l, "From ", 5) == 0 &&
1120			 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1121		{
1122			if (putc('>', mci->mci_out) == EOF)
1123				break;
1124			else
1125			{
1126				/* record progress for DATA timeout */
1127				DataProgress = TRUE;
1128			}
1129			if (TrafficLogFile != NULL)
1130				(void) putc('>', TrafficLogFile);
1131		}
1132		for ( ; l < p; ++l)
1133		{
1134			if (TrafficLogFile != NULL)
1135				(void) putc((unsigned char)*l, TrafficLogFile);
1136			if (putc((unsigned char) *l, mci->mci_out) == EOF)
1137			{
1138				dead = TRUE;
1139				break;
1140			}
1141			else
1142			{
1143				/* record progress for DATA timeout */
1144				DataProgress = TRUE;
1145			}
1146		}
1147		if (dead)
1148			break;
1149
1150		if (TrafficLogFile != NULL)
1151			(void) putc('\n', TrafficLogFile);
1152		if (fputs(mci->mci_mailer->m_eol, mci->mci_out) == EOF)
1153			break;
1154		else
1155		{
1156			/* record progress for DATA timeout */
1157			DataProgress = TRUE;
1158		}
1159		if (l < end && *l == '\n')
1160		{
1161			if (*++l != ' ' && *l != '\t' && *l != '\0' &&
1162			    bitset(PXLF_HEADER, pxflags))
1163			{
1164				if (putc(' ', mci->mci_out) == EOF)
1165					break;
1166				else
1167				{
1168					/* record progress for DATA timeout */
1169					DataProgress = TRUE;
1170				}
1171				if (TrafficLogFile != NULL)
1172					(void) putc(' ', TrafficLogFile);
1173			}
1174		}
1175	} while (l < end);
1176}
1177/*
1178**  XUNLINK -- unlink a file, doing logging as appropriate.
1179**
1180**	Parameters:
1181**		f -- name of file to unlink.
1182**
1183**	Returns:
1184**		none.
1185**
1186**	Side Effects:
1187**		f is unlinked.
1188*/
1189
1190void
1191xunlink(f)
1192	char *f;
1193{
1194	register int i;
1195
1196	if (LogLevel > 98)
1197		sm_syslog(LOG_DEBUG, CurEnv->e_id,
1198			  "unlink %s",
1199			  f);
1200
1201	i = unlink(f);
1202	if (i < 0 && LogLevel > 97)
1203		sm_syslog(LOG_DEBUG, CurEnv->e_id,
1204			  "%s: unlink-fail %d",
1205			  f, errno);
1206}
1207/*
1208**  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
1209**
1210**	Parameters:
1211**		buf -- place to put the input line.
1212**		siz -- size of buf.
1213**		fp -- file to read from.
1214**		timeout -- the timeout before error occurs.
1215**		during -- what we are trying to read (for error messages).
1216**
1217**	Returns:
1218**		NULL on error (including timeout).  This will also leave
1219**			buf containing a null string.
1220**		buf otherwise.
1221**
1222**	Side Effects:
1223**		none.
1224*/
1225
1226
1227static jmp_buf	CtxReadTimeout;
1228
1229char *
1230sfgets(buf, siz, fp, timeout, during)
1231	char *buf;
1232	int siz;
1233	FILE *fp;
1234	time_t timeout;
1235	char *during;
1236{
1237	register EVENT *ev = NULL;
1238	register char *p;
1239	int save_errno;
1240
1241	if (fp == NULL)
1242	{
1243		buf[0] = '\0';
1244		return NULL;
1245	}
1246
1247	/* set the timeout */
1248	if (timeout != 0)
1249	{
1250		if (setjmp(CtxReadTimeout) != 0)
1251		{
1252			if (LogLevel > 1)
1253				sm_syslog(LOG_NOTICE, CurEnv->e_id,
1254					  "timeout waiting for input from %.100s during %s",
1255					  CurHostName ? CurHostName : "local",
1256					  during);
1257			buf[0] = '\0';
1258#if XDEBUG
1259			checkfd012(during);
1260#endif /* XDEBUG */
1261			if (TrafficLogFile != NULL)
1262				fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n",
1263					(int) getpid());
1264			errno = 0;
1265			return NULL;
1266		}
1267		ev = setevent(timeout, readtimeout, 0);
1268	}
1269
1270	/* try to read */
1271	p = NULL;
1272	errno = 0;
1273	while (!feof(fp) && !ferror(fp))
1274	{
1275		errno = 0;
1276		p = fgets(buf, siz, fp);
1277		if (p != NULL || errno != EINTR)
1278			break;
1279		clearerr(fp);
1280	}
1281	save_errno = errno;
1282
1283	/* clear the event if it has not sprung */
1284	clrevent(ev);
1285
1286	/* clean up the books and exit */
1287	LineNumber++;
1288	if (p == NULL)
1289	{
1290		buf[0] = '\0';
1291		if (TrafficLogFile != NULL)
1292			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid());
1293		errno = save_errno;
1294		return NULL;
1295	}
1296	if (TrafficLogFile != NULL)
1297		fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf);
1298	if (SevenBitInput)
1299	{
1300		for (p = buf; *p != '\0'; p++)
1301			*p &= ~0200;
1302	}
1303	else if (!HasEightBits)
1304	{
1305		for (p = buf; *p != '\0'; p++)
1306		{
1307			if (bitset(0200, *p))
1308			{
1309				HasEightBits = TRUE;
1310				break;
1311			}
1312		}
1313	}
1314	return buf;
1315}
1316
1317/* ARGSUSED */
1318static void
1319readtimeout(timeout)
1320	time_t timeout;
1321{
1322	/*
1323	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
1324	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
1325	**	DOING.
1326	*/
1327
1328	errno = ETIMEDOUT;
1329	longjmp(CtxReadTimeout, 1);
1330}
1331/*
1332**  FGETFOLDED -- like fgets, but know about folded lines.
1333**
1334**	Parameters:
1335**		buf -- place to put result.
1336**		n -- bytes available.
1337**		f -- file to read from.
1338**
1339**	Returns:
1340**		input line(s) on success, NULL on error or EOF.
1341**		This will normally be buf -- unless the line is too
1342**			long, when it will be xalloc()ed.
1343**
1344**	Side Effects:
1345**		buf gets lines from f, with continuation lines (lines
1346**		with leading white space) appended.  CRLF's are mapped
1347**		into single newlines.  Any trailing NL is stripped.
1348*/
1349
1350char *
1351fgetfolded(buf, n, f)
1352	char *buf;
1353	register int n;
1354	FILE *f;
1355{
1356	register char *p = buf;
1357	char *bp = buf;
1358	register int i;
1359
1360	n--;
1361	while ((i = getc(f)) != EOF)
1362	{
1363		if (i == '\r')
1364		{
1365			i = getc(f);
1366			if (i != '\n')
1367			{
1368				if (i != EOF)
1369					(void) ungetc(i, f);
1370				i = '\r';
1371			}
1372		}
1373		if (--n <= 0)
1374		{
1375			/* allocate new space */
1376			char *nbp;
1377			int nn;
1378
1379			nn = (p - bp);
1380			if (nn < MEMCHUNKSIZE)
1381				nn *= 2;
1382			else
1383				nn += MEMCHUNKSIZE;
1384			nbp = xalloc(nn);
1385			memmove(nbp, bp, p - bp);
1386			p = &nbp[p - bp];
1387			if (bp != buf)
1388				sm_free(bp);
1389			bp = nbp;
1390			n = nn - (p - bp);
1391		}
1392		*p++ = i;
1393		if (i == '\n')
1394		{
1395			LineNumber++;
1396			i = getc(f);
1397			if (i != EOF)
1398				(void) ungetc(i, f);
1399			if (i != ' ' && i != '\t')
1400				break;
1401		}
1402	}
1403	if (p == bp)
1404		return NULL;
1405	if (p[-1] == '\n')
1406		p--;
1407	*p = '\0';
1408	return bp;
1409}
1410/*
1411**  CURTIME -- return current time.
1412**
1413**	Parameters:
1414**		none.
1415**
1416**	Returns:
1417**		the current time.
1418**
1419**	Side Effects:
1420**		none.
1421*/
1422
1423time_t
1424curtime()
1425{
1426	auto time_t t;
1427
1428	(void) time(&t);
1429	return t;
1430}
1431/*
1432**  ATOBOOL -- convert a string representation to boolean.
1433**
1434**	Defaults to "TRUE"
1435**
1436**	Parameters:
1437**		s -- string to convert.  Takes "tTyY" as true,
1438**			others as false.
1439**
1440**	Returns:
1441**		A boolean representation of the string.
1442**
1443**	Side Effects:
1444**		none.
1445*/
1446
1447bool
1448atobool(s)
1449	register char *s;
1450{
1451	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1452		return TRUE;
1453	return FALSE;
1454}
1455/*
1456**  ATOOCT -- convert a string representation to octal.
1457**
1458**	Parameters:
1459**		s -- string to convert.
1460**
1461**	Returns:
1462**		An integer representing the string interpreted as an
1463**		octal number.
1464**
1465**	Side Effects:
1466**		none.
1467*/
1468
1469int
1470atooct(s)
1471	register char *s;
1472{
1473	register int i = 0;
1474
1475	while (*s >= '0' && *s <= '7')
1476		i = (i << 3) | (*s++ - '0');
1477	return i;
1478}
1479/*
1480**  BITINTERSECT -- tell if two bitmaps intersect
1481**
1482**	Parameters:
1483**		a, b -- the bitmaps in question
1484**
1485**	Returns:
1486**		TRUE if they have a non-null intersection
1487**		FALSE otherwise
1488**
1489**	Side Effects:
1490**		none.
1491*/
1492
1493bool
1494bitintersect(a, b)
1495	BITMAP256 a;
1496	BITMAP256 b;
1497{
1498	int i;
1499
1500	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1501	{
1502		if ((a[i] & b[i]) != 0)
1503			return TRUE;
1504	}
1505	return FALSE;
1506}
1507/*
1508**  BITZEROP -- tell if a bitmap is all zero
1509**
1510**	Parameters:
1511**		map -- the bit map to check
1512**
1513**	Returns:
1514**		TRUE if map is all zero.
1515**		FALSE if there are any bits set in map.
1516**
1517**	Side Effects:
1518**		none.
1519*/
1520
1521bool
1522bitzerop(map)
1523	BITMAP256 map;
1524{
1525	int i;
1526
1527	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1528	{
1529		if (map[i] != 0)
1530			return FALSE;
1531	}
1532	return TRUE;
1533}
1534/*
1535**  STRCONTAINEDIN -- tell if one string is contained in another
1536**
1537**	Parameters:
1538**		a -- possible substring.
1539**		b -- possible superstring.
1540**
1541**	Returns:
1542**		TRUE if a is contained in b.
1543**		FALSE otherwise.
1544*/
1545
1546bool
1547strcontainedin(a, b)
1548	register char *a;
1549	register char *b;
1550{
1551	int la;
1552	int lb;
1553	int c;
1554
1555	la = strlen(a);
1556	lb = strlen(b);
1557	c = *a;
1558	if (isascii(c) && isupper(c))
1559		c = tolower(c);
1560	for (; lb-- >= la; b++)
1561	{
1562		if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
1563			continue;
1564		if (strncasecmp(a, b, la) == 0)
1565			return TRUE;
1566	}
1567	return FALSE;
1568}
1569/*
1570**  CHECKFD012 -- check low numbered file descriptors
1571**
1572**	File descriptors 0, 1, and 2 should be open at all times.
1573**	This routine verifies that, and fixes it if not true.
1574**
1575**	Parameters:
1576**		where -- a tag printed if the assertion failed
1577**
1578**	Returns:
1579**		none
1580*/
1581
1582void
1583checkfd012(where)
1584	char *where;
1585{
1586#if XDEBUG
1587	register int i;
1588
1589	for (i = 0; i < 3; i++)
1590		fill_fd(i, where);
1591#endif /* XDEBUG */
1592}
1593/*
1594**  CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1595**
1596**	Parameters:
1597**		fd -- file descriptor to check.
1598**		where -- tag to print on failure.
1599**
1600**	Returns:
1601**		none.
1602*/
1603
1604void
1605checkfdopen(fd, where)
1606	int fd;
1607	char *where;
1608{
1609#if XDEBUG
1610	struct stat st;
1611
1612	if (fstat(fd, &st) < 0 && errno == EBADF)
1613	{
1614		syserr("checkfdopen(%d): %s not open as expected!", fd, where);
1615		printopenfds(TRUE);
1616	}
1617#endif /* XDEBUG */
1618}
1619/*
1620**  CHECKFDS -- check for new or missing file descriptors
1621**
1622**	Parameters:
1623**		where -- tag for printing.  If null, take a base line.
1624**
1625**	Returns:
1626**		none
1627**
1628**	Side Effects:
1629**		If where is set, shows changes since the last call.
1630*/
1631
1632void
1633checkfds(where)
1634	char *where;
1635{
1636	int maxfd;
1637	register int fd;
1638	bool printhdr = TRUE;
1639	int save_errno = errno;
1640	static BITMAP256 baseline;
1641	extern int DtableSize;
1642
1643	if (DtableSize > BITMAPBITS)
1644		maxfd = BITMAPBITS;
1645	else
1646		maxfd = DtableSize;
1647	if (where == NULL)
1648		clrbitmap(baseline);
1649
1650	for (fd = 0; fd < maxfd; fd++)
1651	{
1652		struct stat stbuf;
1653
1654		if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
1655		{
1656			if (!bitnset(fd, baseline))
1657				continue;
1658			clrbitn(fd, baseline);
1659		}
1660		else if (!bitnset(fd, baseline))
1661			setbitn(fd, baseline);
1662		else
1663			continue;
1664
1665		/* file state has changed */
1666		if (where == NULL)
1667			continue;
1668		if (printhdr)
1669		{
1670			sm_syslog(LOG_DEBUG, CurEnv->e_id,
1671				  "%s: changed fds:",
1672				  where);
1673			printhdr = FALSE;
1674		}
1675		dumpfd(fd, TRUE, TRUE);
1676	}
1677	errno = save_errno;
1678}
1679/*
1680**  PRINTOPENFDS -- print the open file descriptors (for debugging)
1681**
1682**	Parameters:
1683**		logit -- if set, send output to syslog; otherwise
1684**			print for debugging.
1685**
1686**	Returns:
1687**		none.
1688*/
1689
1690#if NETINET || NETINET6
1691# include <arpa/inet.h>
1692#endif /* NETINET || NETINET6 */
1693
1694void
1695printopenfds(logit)
1696	bool logit;
1697{
1698	register int fd;
1699	extern int DtableSize;
1700
1701	for (fd = 0; fd < DtableSize; fd++)
1702		dumpfd(fd, FALSE, logit);
1703}
1704/*
1705**  DUMPFD -- dump a file descriptor
1706**
1707**	Parameters:
1708**		fd -- the file descriptor to dump.
1709**		printclosed -- if set, print a notification even if
1710**			it is closed; otherwise print nothing.
1711**		logit -- if set, send output to syslog instead of stdout.
1712*/
1713
1714void
1715dumpfd(fd, printclosed, logit)
1716	int fd;
1717	bool printclosed;
1718	bool logit;
1719{
1720	register char *p;
1721	char *hp;
1722#ifdef S_IFSOCK
1723	SOCKADDR sa;
1724#endif /* S_IFSOCK */
1725	auto SOCKADDR_LEN_T slen;
1726	int i;
1727#if STAT64 > 0
1728	struct stat64 st;
1729#else /* STAT64 > 0 */
1730	struct stat st;
1731#endif /* STAT64 > 0 */
1732	char buf[200];
1733
1734	p = buf;
1735	snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
1736	p += strlen(p);
1737
1738	if (
1739#if STAT64 > 0
1740	    fstat64(fd, &st)
1741#else /* STAT64 > 0 */
1742	    fstat(fd, &st)
1743#endif /* STAT64 > 0 */
1744	    < 0)
1745	{
1746		if (errno != EBADF)
1747		{
1748			snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)",
1749				errstring(errno));
1750			goto printit;
1751		}
1752		else if (printclosed)
1753		{
1754			snprintf(p, SPACELEFT(buf, p), "CLOSED");
1755			goto printit;
1756		}
1757		return;
1758	}
1759
1760	i = fcntl(fd, F_GETFL, NULL);
1761	if (i != -1)
1762	{
1763		snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
1764		p += strlen(p);
1765	}
1766
1767	snprintf(p, SPACELEFT(buf, p), "mode=%o: ", (int) st.st_mode);
1768	p += strlen(p);
1769	switch (st.st_mode & S_IFMT)
1770	{
1771#ifdef S_IFSOCK
1772	  case S_IFSOCK:
1773		snprintf(p, SPACELEFT(buf, p), "SOCK ");
1774		p += strlen(p);
1775		memset(&sa, '\0', sizeof sa);
1776		slen = sizeof sa;
1777		if (getsockname(fd, &sa.sa, &slen) < 0)
1778			snprintf(p, SPACELEFT(buf, p), "(%s)",
1779				 errstring(errno));
1780		else
1781		{
1782			hp = hostnamebyanyaddr(&sa);
1783			if (hp == NULL)
1784			{
1785				/* EMPTY */
1786				/* do nothing */
1787			}
1788# if NETINET
1789			else if (sa.sa.sa_family == AF_INET)
1790				snprintf(p, SPACELEFT(buf, p), "%s/%d",
1791					 hp, ntohs(sa.sin.sin_port));
1792# endif /* NETINET */
1793# if NETINET6
1794			else if (sa.sa.sa_family == AF_INET6)
1795				snprintf(p, SPACELEFT(buf, p), "%s/%d",
1796					 hp, ntohs(sa.sin6.sin6_port));
1797# endif /* NETINET6 */
1798			else
1799				snprintf(p, SPACELEFT(buf, p), "%s", hp);
1800		}
1801		p += strlen(p);
1802		snprintf(p, SPACELEFT(buf, p), "->");
1803		p += strlen(p);
1804		slen = sizeof sa;
1805		if (getpeername(fd, &sa.sa, &slen) < 0)
1806			snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno));
1807		else
1808		{
1809			hp = hostnamebyanyaddr(&sa);
1810			if (hp == NULL)
1811			{
1812				/* EMPTY */
1813				/* do nothing */
1814			}
1815# if NETINET
1816			else if (sa.sa.sa_family == AF_INET)
1817				snprintf(p, SPACELEFT(buf, p), "%s/%d",
1818					 hp, ntohs(sa.sin.sin_port));
1819# endif /* NETINET */
1820# if NETINET6
1821			else if (sa.sa.sa_family == AF_INET6)
1822				snprintf(p, SPACELEFT(buf, p), "%s/%d",
1823					 hp, ntohs(sa.sin6.sin6_port));
1824# endif /* NETINET6 */
1825			else
1826				snprintf(p, SPACELEFT(buf, p), "%s", hp);
1827		}
1828		break;
1829#endif /* S_IFSOCK */
1830
1831	  case S_IFCHR:
1832		snprintf(p, SPACELEFT(buf, p), "CHR: ");
1833		p += strlen(p);
1834		goto defprint;
1835
1836	  case S_IFBLK:
1837		snprintf(p, SPACELEFT(buf, p), "BLK: ");
1838		p += strlen(p);
1839		goto defprint;
1840
1841#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1842	  case S_IFIFO:
1843		snprintf(p, SPACELEFT(buf, p), "FIFO: ");
1844		p += strlen(p);
1845		goto defprint;
1846#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */
1847
1848#ifdef S_IFDIR
1849	  case S_IFDIR:
1850		snprintf(p, SPACELEFT(buf, p), "DIR: ");
1851		p += strlen(p);
1852		goto defprint;
1853#endif /* S_IFDIR */
1854
1855#ifdef S_IFLNK
1856	  case S_IFLNK:
1857		snprintf(p, SPACELEFT(buf, p), "LNK: ");
1858		p += strlen(p);
1859		goto defprint;
1860#endif /* S_IFLNK */
1861
1862	  default:
1863defprint:
1864		/*CONSTCOND*/
1865		if (sizeof st.st_ino > sizeof (long))
1866			snprintf(p, SPACELEFT(buf, p),
1867				 "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ",
1868				 major(st.st_dev), minor(st.st_dev),
1869				 quad_to_string(st.st_ino),
1870				 (int) st.st_nlink, (int) st.st_uid,
1871				 (int) st.st_gid);
1872		else
1873			snprintf(p, SPACELEFT(buf, p),
1874				 "dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ",
1875				 major(st.st_dev), minor(st.st_dev),
1876				 (unsigned long) st.st_ino,
1877				 (int) st.st_nlink, (int) st.st_uid,
1878				 (int) st.st_gid);
1879		/*CONSTCOND*/
1880		if (sizeof st.st_size > sizeof (long))
1881			snprintf(p, SPACELEFT(buf, p), "size=%s",
1882				 quad_to_string(st.st_size));
1883		else
1884			snprintf(p, SPACELEFT(buf, p), "size=%lu",
1885				 (unsigned long) st.st_size);
1886		break;
1887	}
1888
1889printit:
1890	if (logit)
1891		sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
1892			  "%.800s", buf);
1893	else
1894		printf("%s\n", buf);
1895}
1896/*
1897**  SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1898**
1899**	Parameters:
1900**		host -- the host to shorten (stripped in place).
1901**
1902**	Returns:
1903**		place where string was trunacted, NULL if not truncated.
1904*/
1905
1906char *
1907shorten_hostname(host)
1908	char host[];
1909{
1910	register char *p;
1911	char *mydom;
1912	int i;
1913	bool canon = FALSE;
1914
1915	/* strip off final dot */
1916	p = &host[strlen(host) - 1];
1917	if (*p == '.')
1918	{
1919		*p = '\0';
1920		canon = TRUE;
1921	}
1922
1923	/* see if there is any domain at all -- if not, we are done */
1924	p = strchr(host, '.');
1925	if (p == NULL)
1926		return NULL;
1927
1928	/* yes, we have a domain -- see if it looks like us */
1929	mydom = macvalue('m', CurEnv);
1930	if (mydom == NULL)
1931		mydom = "";
1932	i = strlen(++p);
1933	if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
1934	    (mydom[i] == '.' || mydom[i] == '\0'))
1935	{
1936		*--p = '\0';
1937		return p;
1938	}
1939	return NULL;
1940}
1941/*
1942**  PROG_OPEN -- open a program for reading
1943**
1944**	Parameters:
1945**		argv -- the argument list.
1946**		pfd -- pointer to a place to store the file descriptor.
1947**		e -- the current envelope.
1948**
1949**	Returns:
1950**		pid of the process -- -1 if it failed.
1951*/
1952
1953pid_t
1954prog_open(argv, pfd, e)
1955	char **argv;
1956	int *pfd;
1957	ENVELOPE *e;
1958{
1959	pid_t pid;
1960	int i;
1961	int save_errno;
1962	int fdv[2];
1963	char *p, *q;
1964	char buf[MAXLINE + 1];
1965	extern int DtableSize;
1966
1967	if (pipe(fdv) < 0)
1968	{
1969		syserr("%s: cannot create pipe for stdout", argv[0]);
1970		return -1;
1971	}
1972	pid = fork();
1973	if (pid < 0)
1974	{
1975		syserr("%s: cannot fork", argv[0]);
1976		(void) close(fdv[0]);
1977		(void) close(fdv[1]);
1978		return -1;
1979	}
1980	if (pid > 0)
1981	{
1982		/* parent */
1983		(void) close(fdv[1]);
1984		*pfd = fdv[0];
1985		return pid;
1986	}
1987
1988	/* child -- close stdin */
1989	(void) close(0);
1990
1991	/* Reset global flags */
1992	RestartRequest = NULL;
1993	ShutdownRequest = NULL;
1994	PendingSignal = 0;
1995
1996	/* stdout goes back to parent */
1997	(void) close(fdv[0]);
1998	if (dup2(fdv[1], 1) < 0)
1999	{
2000		syserr("%s: cannot dup2 for stdout", argv[0]);
2001		_exit(EX_OSERR);
2002	}
2003	(void) close(fdv[1]);
2004
2005	/* stderr goes to transcript if available */
2006	if (e->e_xfp != NULL)
2007	{
2008		int xfd;
2009
2010		xfd = fileno(e->e_xfp);
2011		if (xfd >= 0 && dup2(xfd, 2) < 0)
2012		{
2013			syserr("%s: cannot dup2 for stderr", argv[0]);
2014			_exit(EX_OSERR);
2015		}
2016	}
2017
2018	/* this process has no right to the queue file */
2019	if (e->e_lockfp != NULL)
2020		(void) close(fileno(e->e_lockfp));
2021
2022	/* chroot to the program mailer directory, if defined */
2023	if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
2024	{
2025		expand(ProgMailer->m_rootdir, buf, sizeof buf, e);
2026		if (chroot(buf) < 0)
2027		{
2028			syserr("prog_open: cannot chroot(%s)", buf);
2029			exit(EX_TEMPFAIL);
2030		}
2031		if (chdir("/") < 0)
2032		{
2033			syserr("prog_open: cannot chdir(/)");
2034			exit(EX_TEMPFAIL);
2035		}
2036	}
2037
2038	/* run as default user */
2039	endpwent();
2040	if (setgid(DefGid) < 0 && geteuid() == 0)
2041	{
2042		syserr("prog_open: setgid(%ld) failed", (long) DefGid);
2043		exit(EX_TEMPFAIL);
2044	}
2045	if (setuid(DefUid) < 0 && geteuid() == 0)
2046	{
2047		syserr("prog_open: setuid(%ld) failed", (long) DefUid);
2048		exit(EX_TEMPFAIL);
2049	}
2050
2051	/* run in some directory */
2052	if (ProgMailer != NULL)
2053		p = ProgMailer->m_execdir;
2054	else
2055		p = NULL;
2056	for (; p != NULL; p = q)
2057	{
2058		q = strchr(p, ':');
2059		if (q != NULL)
2060			*q = '\0';
2061		expand(p, buf, sizeof buf, e);
2062		if (q != NULL)
2063			*q++ = ':';
2064		if (buf[0] != '\0' && chdir(buf) >= 0)
2065			break;
2066	}
2067	if (p == NULL)
2068	{
2069		/* backup directories */
2070		if (chdir("/tmp") < 0)
2071			(void) chdir("/");
2072	}
2073
2074	/* arrange for all the files to be closed */
2075	for (i = 3; i < DtableSize; i++)
2076	{
2077		register int j;
2078
2079		if ((j = fcntl(i, F_GETFD, 0)) != -1)
2080			(void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
2081	}
2082
2083	/* now exec the process */
2084	(void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
2085
2086	/* woops!  failed */
2087	save_errno = errno;
2088	syserr("%s: cannot exec", argv[0]);
2089	if (transienterror(save_errno))
2090		_exit(EX_OSERR);
2091	_exit(EX_CONFIG);
2092	return -1;	/* avoid compiler warning on IRIX */
2093}
2094/*
2095**  GET_COLUMN -- look up a Column in a line buffer
2096**
2097**	Parameters:
2098**		line -- the raw text line to search.
2099**		col -- the column number to fetch.
2100**		delim -- the delimiter between columns.  If null,
2101**			use white space.
2102**		buf -- the output buffer.
2103**		buflen -- the length of buf.
2104**
2105**	Returns:
2106**		buf if successful.
2107**		NULL otherwise.
2108*/
2109
2110char *
2111get_column(line, col, delim, buf, buflen)
2112	char line[];
2113	int col;
2114	int delim;
2115	char buf[];
2116	int buflen;
2117{
2118	char *p;
2119	char *begin, *end;
2120	int i;
2121	char delimbuf[4];
2122
2123	if ((char)delim == '\0')
2124		(void) strlcpy(delimbuf, "\n\t ", sizeof delimbuf);
2125	else
2126	{
2127		delimbuf[0] = (char)delim;
2128		delimbuf[1] = '\0';
2129	}
2130
2131	p = line;
2132	if (*p == '\0')
2133		return NULL;			/* line empty */
2134	if (*p == (char)delim && col == 0)
2135		return NULL;			/* first column empty */
2136
2137	begin = line;
2138
2139	if (col == 0 && (char)delim == '\0')
2140	{
2141		while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2142			begin++;
2143	}
2144
2145	for (i = 0; i < col; i++)
2146	{
2147		if ((begin = strpbrk(begin, delimbuf)) == NULL)
2148			return NULL;		/* no such column */
2149		begin++;
2150		if ((char)delim == '\0')
2151		{
2152			while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2153				begin++;
2154		}
2155	}
2156
2157	end = strpbrk(begin, delimbuf);
2158	if (end == NULL)
2159		i = strlen(begin);
2160	else
2161		i = end - begin;
2162	if (i >= buflen)
2163		i = buflen - 1;
2164	(void) strlcpy(buf, begin, i + 1);
2165	return buf;
2166}
2167/*
2168**  CLEANSTRCPY -- copy string keeping out bogus characters
2169**
2170**	Parameters:
2171**		t -- "to" string.
2172**		f -- "from" string.
2173**		l -- length of space available in "to" string.
2174**
2175**	Returns:
2176**		none.
2177*/
2178
2179void
2180cleanstrcpy(t, f, l)
2181	register char *t;
2182	register char *f;
2183	int l;
2184{
2185	/* check for newlines and log if necessary */
2186	(void) denlstring(f, TRUE, TRUE);
2187
2188	if (l <= 0)
2189		syserr("!cleanstrcpy: length == 0");
2190
2191	l--;
2192	while (l > 0 && *f != '\0')
2193	{
2194		if (isascii(*f) &&
2195		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
2196		{
2197			l--;
2198			*t++ = *f;
2199		}
2200		f++;
2201	}
2202	*t = '\0';
2203}
2204
2205/*
2206**  DENLSTRING -- convert newlines in a string to spaces
2207**
2208**	Parameters:
2209**		s -- the input string
2210**		strict -- if set, don't permit continuation lines.
2211**		logattacks -- if set, log attempted attacks.
2212**
2213**	Returns:
2214**		A pointer to a version of the string with newlines
2215**		mapped to spaces.  This should be copied.
2216*/
2217
2218char *
2219denlstring(s, strict, logattacks)
2220	char *s;
2221	bool strict;
2222	bool logattacks;
2223{
2224	register char *p;
2225	int l;
2226	static char *bp = NULL;
2227	static int bl = 0;
2228
2229	p = s;
2230	while ((p = strchr(p, '\n')) != NULL)
2231		if (strict || (*++p != ' ' && *p != '\t'))
2232			break;
2233	if (p == NULL)
2234		return s;
2235
2236	l = strlen(s) + 1;
2237	if (bl < l)
2238	{
2239		/* allocate more space */
2240		if (bp != NULL)
2241			sm_free(bp);
2242		bp = xalloc(l);
2243		bl = l;
2244	}
2245	(void) strlcpy(bp, s, l);
2246	for (p = bp; (p = strchr(p, '\n')) != NULL; )
2247		*p++ = ' ';
2248
2249	if (logattacks)
2250	{
2251		sm_syslog(LOG_NOTICE, CurEnv->e_id,
2252			  "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
2253			  RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
2254			  shortenstring(bp, MAXSHORTSTR));
2255	}
2256
2257	return bp;
2258}
2259/*
2260**  PATH_IS_DIR -- check to see if file exists and is a directory.
2261**
2262**	There are some additional checks for security violations in
2263**	here.  This routine is intended to be used for the host status
2264**	support.
2265**
2266**	Parameters:
2267**		pathname -- pathname to check for directory-ness.
2268**		createflag -- if set, create directory if needed.
2269**
2270**	Returns:
2271**		TRUE -- if the indicated pathname is a directory
2272**		FALSE -- otherwise
2273*/
2274
2275int
2276path_is_dir(pathname, createflag)
2277	char *pathname;
2278	bool createflag;
2279{
2280	struct stat statbuf;
2281
2282#if HASLSTAT
2283	if (lstat(pathname, &statbuf) < 0)
2284#else /* HASLSTAT */
2285	if (stat(pathname, &statbuf) < 0)
2286#endif /* HASLSTAT */
2287	{
2288		if (errno != ENOENT || !createflag)
2289			return FALSE;
2290		if (mkdir(pathname, 0755) < 0)
2291			return FALSE;
2292		return TRUE;
2293	}
2294	if (!S_ISDIR(statbuf.st_mode))
2295	{
2296		errno = ENOTDIR;
2297		return FALSE;
2298	}
2299
2300	/* security: don't allow writable directories */
2301	if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
2302	{
2303		errno = EACCES;
2304		return FALSE;
2305	}
2306
2307	return TRUE;
2308}
2309/*
2310**  PROC_LIST_ADD -- add process id to list of our children
2311**
2312**	Parameters:
2313**		pid -- pid to add to list.
2314**		task -- task of pid.
2315**		type -- type of process.
2316**
2317**	Returns:
2318**		none
2319*/
2320
2321static struct procs	*volatile ProcListVec = NULL;
2322static int		ProcListSize = 0;
2323
2324void
2325proc_list_add(pid, task, type)
2326	pid_t pid;
2327	char *task;
2328	int type;
2329{
2330	int i;
2331
2332	for (i = 0; i < ProcListSize; i++)
2333	{
2334		if (ProcListVec[i].proc_pid == NO_PID)
2335			break;
2336	}
2337	if (i >= ProcListSize)
2338	{
2339		/* probe the existing vector to avoid growing infinitely */
2340		proc_list_probe();
2341
2342		/* now scan again */
2343		for (i = 0; i < ProcListSize; i++)
2344		{
2345			if (ProcListVec[i].proc_pid == NO_PID)
2346				break;
2347		}
2348	}
2349	if (i >= ProcListSize)
2350	{
2351		/* grow process list */
2352		struct procs *npv;
2353
2354		npv = (struct procs *) xalloc((sizeof *npv) *
2355					      (ProcListSize + PROC_LIST_SEG));
2356		if (ProcListSize > 0)
2357		{
2358			memmove(npv, ProcListVec,
2359				ProcListSize * sizeof (struct procs));
2360			sm_free(ProcListVec);
2361		}
2362		for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
2363		{
2364			npv[i].proc_pid = NO_PID;
2365			npv[i].proc_task = NULL;
2366			npv[i].proc_type = PROC_NONE;
2367		}
2368		i = ProcListSize;
2369		ProcListSize += PROC_LIST_SEG;
2370		ProcListVec = npv;
2371	}
2372	ProcListVec[i].proc_pid = pid;
2373	if (ProcListVec[i].proc_task != NULL)
2374		sm_free(ProcListVec[i].proc_task);
2375	ProcListVec[i].proc_task = newstr(task);
2376	ProcListVec[i].proc_type = type;
2377
2378	/* if process adding itself, it's not a child */
2379	if (pid != getpid())
2380		CurChildren++;
2381}
2382/*
2383**  PROC_LIST_SET -- set pid task in process list
2384**
2385**	Parameters:
2386**		pid -- pid to set
2387**		task -- task of pid
2388**
2389**	Returns:
2390**		none.
2391*/
2392
2393void
2394proc_list_set(pid, task)
2395	pid_t pid;
2396	char *task;
2397{
2398	int i;
2399
2400	for (i = 0; i < ProcListSize; i++)
2401	{
2402		if (ProcListVec[i].proc_pid == pid)
2403		{
2404			if (ProcListVec[i].proc_task != NULL)
2405				sm_free(ProcListVec[i].proc_task);
2406			ProcListVec[i].proc_task = newstr(task);
2407			break;
2408		}
2409	}
2410}
2411/*
2412**  PROC_LIST_DROP -- drop pid from process list
2413**
2414**	Parameters:
2415**		pid -- pid to drop
2416**
2417**	Returns:
2418**		type of process
2419**
2420**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2421**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2422**		DOING.
2423*/
2424
2425int
2426proc_list_drop(pid)
2427	pid_t pid;
2428{
2429	int i;
2430	int type = PROC_NONE;
2431
2432	for (i = 0; i < ProcListSize; i++)
2433	{
2434		if (ProcListVec[i].proc_pid == pid)
2435		{
2436			ProcListVec[i].proc_pid = NO_PID;
2437			type = ProcListVec[i].proc_type;
2438			break;
2439		}
2440	}
2441	if (CurChildren > 0)
2442		CurChildren--;
2443
2444
2445	return type;
2446}
2447/*
2448**  PROC_LIST_CLEAR -- clear the process list
2449**
2450**	Parameters:
2451**		none.
2452**
2453**	Returns:
2454**		none.
2455*/
2456
2457void
2458proc_list_clear()
2459{
2460	int i;
2461
2462	/* start from 1 since 0 is the daemon itself */
2463	for (i = 1; i < ProcListSize; i++)
2464	{
2465		ProcListVec[i].proc_pid = NO_PID;
2466	}
2467	CurChildren = 0;
2468}
2469/*
2470**  PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2471**
2472**	Parameters:
2473**		none
2474**
2475**	Returns:
2476**		none
2477*/
2478
2479void
2480proc_list_probe()
2481{
2482	int i;
2483
2484	/* start from 1 since 0 is the daemon itself */
2485	for (i = 1; i < ProcListSize; i++)
2486	{
2487		if (ProcListVec[i].proc_pid == NO_PID)
2488			continue;
2489		if (kill(ProcListVec[i].proc_pid, 0) < 0)
2490		{
2491			if (LogLevel > 3)
2492				sm_syslog(LOG_DEBUG, CurEnv->e_id,
2493					  "proc_list_probe: lost pid %d",
2494					  (int) ProcListVec[i].proc_pid);
2495			ProcListVec[i].proc_pid = NO_PID;
2496			CurChildren--;
2497		}
2498	}
2499	if (CurChildren < 0)
2500		CurChildren = 0;
2501}
2502/*
2503**  PROC_LIST_DISPLAY -- display the process list
2504**
2505**	Parameters:
2506**		out -- output file pointer
2507**
2508**	Returns:
2509**		none.
2510*/
2511
2512void
2513proc_list_display(out)
2514	FILE *out;
2515{
2516	int i;
2517
2518	for (i = 0; i < ProcListSize; i++)
2519	{
2520		if (ProcListVec[i].proc_pid == NO_PID)
2521			continue;
2522
2523		fprintf(out, "%d %s%s\n", (int) ProcListVec[i].proc_pid,
2524			ProcListVec[i].proc_task != NULL ?
2525			ProcListVec[i].proc_task : "(unknown)",
2526			(OpMode == MD_SMTP ||
2527			 OpMode == MD_DAEMON ||
2528			 OpMode == MD_ARPAFTP) ? "\r" : "");
2529	}
2530}
2531/*
2532**  SM_STRCASECMP -- 8-bit clean version of strcasecmp
2533**
2534**	Thank you, vendors, for making this all necessary.
2535*/
2536
2537/*
2538 * Copyright (c) 1987, 1993
2539 *	The Regents of the University of California.  All rights reserved.
2540 *
2541 * Redistribution and use in source and binary forms, with or without
2542 * modification, are permitted provided that the following conditions
2543 * are met:
2544 * 1. Redistributions of source code must retain the above copyright
2545 *    notice, this list of conditions and the following disclaimer.
2546 * 2. Redistributions in binary form must reproduce the above copyright
2547 *    notice, this list of conditions and the following disclaimer in the
2548 *    documentation and/or other materials provided with the distribution.
2549 * 3. All advertising materials mentioning features or use of this software
2550 *    must display the following acknowledgement:
2551 *	This product includes software developed by the University of
2552 *	California, Berkeley and its contributors.
2553 * 4. Neither the name of the University nor the names of its contributors
2554 *    may be used to endorse or promote products derived from this software
2555 *    without specific prior written permission.
2556 *
2557 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2558 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2559 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2560 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2561 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2562 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2563 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2564 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2565 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2566 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2567 * SUCH DAMAGE.
2568 */
2569
2570#if defined(LIBC_SCCS) && !defined(lint)
2571static char sccsid[] = "@(#)strcasecmp.c	8.1 (Berkeley) 6/4/93";
2572#endif /* defined(LIBC_SCCS) && !defined(lint) */
2573
2574/*
2575 * This array is designed for mapping upper and lower case letter
2576 * together for a case independent comparison.  The mappings are
2577 * based upon ascii character sequences.
2578 */
2579static const u_char charmap[] = {
2580	0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
2581	0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
2582	0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
2583	0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
2584	0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
2585	0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
2586	0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
2587	0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
2588	0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
2589	0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
2590	0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
2591	0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
2592	0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
2593	0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
2594	0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
2595	0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
2596	0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
2597	0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
2598	0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
2599	0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
2600	0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
2601	0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
2602	0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
2603	0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
2604	0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
2605	0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
2606	0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
2607	0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
2608	0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
2609	0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
2610	0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
2611	0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
2612};
2613
2614int
2615sm_strcasecmp(s1, s2)
2616	const char *s1, *s2;
2617{
2618	register const u_char *cm = charmap,
2619			*us1 = (const u_char *)s1,
2620			*us2 = (const u_char *)s2;
2621
2622	while (cm[*us1] == cm[*us2++])
2623		if (*us1++ == '\0')
2624			return 0;
2625	return (cm[*us1] - cm[*--us2]);
2626}
2627
2628int
2629sm_strncasecmp(s1, s2, n)
2630	const char *s1, *s2;
2631	register size_t n;
2632{
2633	if (n != 0) {
2634		register const u_char *cm = charmap,
2635				*us1 = (const u_char *)s1,
2636				*us2 = (const u_char *)s2;
2637
2638		do {
2639			if (cm[*us1] != cm[*us2++])
2640				return (cm[*us1] - cm[*--us2]);
2641			if (*us1++ == '\0')
2642				break;
2643		} while (--n != 0);
2644	}
2645	return 0;
2646}
2647