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