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