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