xref: /original-bsd/usr.sbin/sendmail/src/util.c (revision 82a828f5)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)util.c	8.16 (Berkeley) 11/08/93";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include <sysexits.h>
15 /*
16 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
17 **
18 **	Runs through a string and strips off unquoted quote
19 **	characters and quote bits.  This is done in place.
20 **
21 **	Parameters:
22 **		s -- the string to strip.
23 **
24 **	Returns:
25 **		none.
26 **
27 **	Side Effects:
28 **		none.
29 **
30 **	Called By:
31 **		deliver
32 */
33 
34 stripquotes(s)
35 	char *s;
36 {
37 	register char *p;
38 	register char *q;
39 	register char c;
40 
41 	if (s == NULL)
42 		return;
43 
44 	p = q = s;
45 	do
46 	{
47 		c = *p++;
48 		if (c == '\\')
49 			c = *p++;
50 		else if (c == '"')
51 			continue;
52 		*q++ = c;
53 	} while (c != '\0');
54 }
55 /*
56 **  XALLOC -- Allocate memory and bitch wildly on failure.
57 **
58 **	THIS IS A CLUDGE.  This should be made to give a proper
59 **	error -- but after all, what can we do?
60 **
61 **	Parameters:
62 **		sz -- size of area to allocate.
63 **
64 **	Returns:
65 **		pointer to data region.
66 **
67 **	Side Effects:
68 **		Memory is allocated.
69 */
70 
71 char *
72 xalloc(sz)
73 	register int sz;
74 {
75 	register char *p;
76 
77 	p = malloc((unsigned) sz);
78 	if (p == NULL)
79 	{
80 		syserr("Out of memory!!");
81 		abort();
82 		/* exit(EX_UNAVAILABLE); */
83 	}
84 	return (p);
85 }
86 /*
87 **  COPYPLIST -- copy list of pointers.
88 **
89 **	This routine is the equivalent of newstr for lists of
90 **	pointers.
91 **
92 **	Parameters:
93 **		list -- list of pointers to copy.
94 **			Must be NULL terminated.
95 **		copycont -- if TRUE, copy the contents of the vector
96 **			(which must be a string) also.
97 **
98 **	Returns:
99 **		a copy of 'list'.
100 **
101 **	Side Effects:
102 **		none.
103 */
104 
105 char **
106 copyplist(list, copycont)
107 	char **list;
108 	bool copycont;
109 {
110 	register char **vp;
111 	register char **newvp;
112 
113 	for (vp = list; *vp != NULL; vp++)
114 		continue;
115 
116 	vp++;
117 
118 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
119 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
120 
121 	if (copycont)
122 	{
123 		for (vp = newvp; *vp != NULL; vp++)
124 			*vp = newstr(*vp);
125 	}
126 
127 	return (newvp);
128 }
129 /*
130 **  COPYQUEUE -- copy address queue.
131 **
132 **	This routine is the equivalent of newstr for address queues
133 **	addresses marked with QDONTSEND aren't copied
134 **
135 **	Parameters:
136 **		addr -- list of address structures to copy.
137 **
138 **	Returns:
139 **		a copy of 'addr'.
140 **
141 **	Side Effects:
142 **		none.
143 */
144 
145 ADDRESS *
146 copyqueue(addr)
147 	ADDRESS *addr;
148 {
149 	register ADDRESS *newaddr;
150 	ADDRESS *ret;
151 	register ADDRESS **tail = &ret;
152 
153 	while (addr != NULL)
154 	{
155 		if (!bitset(QDONTSEND, addr->q_flags))
156 		{
157 			newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
158 			STRUCTCOPY(*addr, *newaddr);
159 			*tail = newaddr;
160 			tail = &newaddr->q_next;
161 		}
162 		addr = addr->q_next;
163 	}
164 	*tail = NULL;
165 
166 	return ret;
167 }
168 /*
169 **  PRINTAV -- print argument vector.
170 **
171 **	Parameters:
172 **		av -- argument vector.
173 **
174 **	Returns:
175 **		none.
176 **
177 **	Side Effects:
178 **		prints av.
179 */
180 
181 printav(av)
182 	register char **av;
183 {
184 	while (*av != NULL)
185 	{
186 		if (tTd(0, 44))
187 			printf("\n\t%08x=", *av);
188 		else
189 			(void) putchar(' ');
190 		xputs(*av++);
191 	}
192 	(void) putchar('\n');
193 }
194 /*
195 **  LOWER -- turn letter into lower case.
196 **
197 **	Parameters:
198 **		c -- character to turn into lower case.
199 **
200 **	Returns:
201 **		c, in lower case.
202 **
203 **	Side Effects:
204 **		none.
205 */
206 
207 char
208 lower(c)
209 	register char c;
210 {
211 	return((isascii(c) && isupper(c)) ? tolower(c) : c);
212 }
213 /*
214 **  XPUTS -- put string doing control escapes.
215 **
216 **	Parameters:
217 **		s -- string to put.
218 **
219 **	Returns:
220 **		none.
221 **
222 **	Side Effects:
223 **		output to stdout
224 */
225 
226 xputs(s)
227 	register char *s;
228 {
229 	register int c;
230 	register struct metamac *mp;
231 	extern struct metamac MetaMacros[];
232 
233 	if (s == NULL)
234 	{
235 		printf("<null>");
236 		return;
237 	}
238 	while ((c = (*s++ & 0377)) != '\0')
239 	{
240 		if (!isascii(c))
241 		{
242 			if (c == MATCHREPL || c == MACROEXPAND)
243 			{
244 				putchar('$');
245 				continue;
246 			}
247 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
248 			{
249 				if ((mp->metaval & 0377) == c)
250 				{
251 					printf("$%c", mp->metaname);
252 					break;
253 				}
254 			}
255 			if (mp->metaname != '\0')
256 				continue;
257 			(void) putchar('\\');
258 			c &= 0177;
259 		}
260 		if (isprint(c))
261 		{
262 			putchar(c);
263 			continue;
264 		}
265 
266 		/* wasn't a meta-macro -- find another way to print it */
267 		switch (c)
268 		{
269 		  case '\0':
270 			continue;
271 
272 		  case '\n':
273 			c = 'n';
274 			break;
275 
276 		  case '\r':
277 			c = 'r';
278 			break;
279 
280 		  case '\t':
281 			c = 't';
282 			break;
283 
284 		  default:
285 			(void) putchar('^');
286 			(void) putchar(c ^ 0100);
287 			continue;
288 		}
289 	}
290 	(void) fflush(stdout);
291 }
292 /*
293 **  MAKELOWER -- Translate a line into lower case
294 **
295 **	Parameters:
296 **		p -- the string to translate.  If NULL, return is
297 **			immediate.
298 **
299 **	Returns:
300 **		none.
301 **
302 **	Side Effects:
303 **		String pointed to by p is translated to lower case.
304 **
305 **	Called By:
306 **		parse
307 */
308 
309 makelower(p)
310 	register char *p;
311 {
312 	register char c;
313 
314 	if (p == NULL)
315 		return;
316 	for (; (c = *p) != '\0'; p++)
317 		if (isascii(c) && isupper(c))
318 			*p = tolower(c);
319 }
320 /*
321 **  BUILDFNAME -- build full name from gecos style entry.
322 **
323 **	This routine interprets the strange entry that would appear
324 **	in the GECOS field of the password file.
325 **
326 **	Parameters:
327 **		p -- name to build.
328 **		login -- the login name of this user (for &).
329 **		buf -- place to put the result.
330 **
331 **	Returns:
332 **		none.
333 **
334 **	Side Effects:
335 **		none.
336 */
337 
338 buildfname(gecos, login, buf)
339 	register char *gecos;
340 	char *login;
341 	char *buf;
342 {
343 	register char *p;
344 	register char *bp = buf;
345 	int l;
346 
347 	if (*gecos == '*')
348 		gecos++;
349 
350 	/* find length of final string */
351 	l = 0;
352 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
353 	{
354 		if (*p == '&')
355 			l += strlen(login);
356 		else
357 			l++;
358 	}
359 
360 	/* now fill in buf */
361 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
362 	{
363 		if (*p == '&')
364 		{
365 			(void) strcpy(bp, login);
366 			*bp = toupper(*bp);
367 			while (*bp != '\0')
368 				bp++;
369 		}
370 		else
371 			*bp++ = *p;
372 	}
373 	*bp = '\0';
374 }
375 /*
376 **  SAFEFILE -- return true if a file exists and is safe for a user.
377 **
378 **	Parameters:
379 **		fn -- filename to check.
380 **		uid -- user id to compare against.
381 **		gid -- group id to compare against.
382 **		uname -- user name to compare against (used for group
383 **			sets).
384 **		mustown -- to be safe, this uid must own the file.
385 **		mode -- mode bits that must match.
386 **
387 **	Returns:
388 **		0 if fn exists, is owned by uid, and matches mode.
389 **		An errno otherwise.  The actual errno is cleared.
390 **
391 **	Side Effects:
392 **		none.
393 */
394 
395 #include <grp.h>
396 
397 #ifndef S_IXOTH
398 # define S_IXOTH	(S_IEXEC >> 6)
399 #endif
400 
401 #ifndef S_IXGRP
402 # define S_IXGRP	(S_IEXEC >> 3)
403 #endif
404 
405 #ifndef S_IXUSR
406 # define S_IXUSR	(S_IEXEC)
407 #endif
408 
409 int
410 safefile(fn, uid, gid, uname, mustown, mode)
411 	char *fn;
412 	uid_t uid;
413 	gid_t gid;
414 	char *uname;
415 	bool mustown;
416 	int mode;
417 {
418 	register char *p;
419 	register struct group *gr = NULL;
420 	struct stat stbuf;
421 
422 	if (tTd(54, 4))
423 		printf("safefile(%s, uid=%d, gid=%d, mustown=%d, mode=%o):\n",
424 			fn, uid, gid, mustown, mode);
425 	errno = 0;
426 
427 	for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
428 	{
429 		*p = '\0';
430 		if (stat(fn, &stbuf) < 0)
431 			break;
432 		if (stbuf.st_uid == uid && bitset(S_IXUSR, stbuf.st_mode))
433 			continue;
434 		if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode))
435 			continue;
436 #ifndef NO_GROUP_SET
437 		if (uname != NULL &&
438 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
439 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
440 		{
441 			register char **gp;
442 
443 			for (gp = gr->gr_mem; *gp != NULL; gp++)
444 				if (strcmp(*gp, uname) == 0)
445 					break;
446 			if (*gp != NULL && bitset(S_IXGRP, stbuf.st_mode))
447 				continue;
448 		}
449 #endif
450 		if (!bitset(S_IXOTH, stbuf.st_mode))
451 			break;
452 	}
453 	if (p != NULL)
454 	{
455 		int ret = errno;
456 
457 		if (ret == 0)
458 			ret = EACCES;
459 		if (tTd(54, 4))
460 			printf("\t[dir %s] %s\n", fn, errstring(ret));
461 		*p = '/';
462 		return ret;
463 	}
464 
465 	if (stat(fn, &stbuf) < 0)
466 	{
467 		int ret = errno;
468 
469 		if (tTd(54, 4))
470 			printf("\t%s\n", errstring(ret));
471 
472 		errno = 0;
473 		return ret;
474 	}
475 	if (uid == 0)
476 		mode >>= 6;
477 	else if (stbuf.st_uid != uid)
478 	{
479 		mode >>= 3;
480 		if (stbuf.st_gid == gid)
481 			;
482 #ifndef NO_GROUP_SET
483 		else if (uname != NULL &&
484 			 ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
485 			  (gr = getgrgid(stbuf.st_gid)) != NULL))
486 		{
487 			register char **gp;
488 
489 			for (gp = gr->gr_mem; *gp != NULL; gp++)
490 				if (strcmp(*gp, uname) == 0)
491 					break;
492 			if (*gp == NULL)
493 				mode >>= 3;
494 		}
495 #endif
496 		else
497 			mode >>= 3;
498 	}
499 	if (tTd(54, 4))
500 		printf("\t[uid %d, stat %o, mode %o] ",
501 			stbuf.st_uid, stbuf.st_mode, mode);
502 	if ((stbuf.st_uid == uid || stbuf.st_uid == 0 || !mustown) &&
503 	    (stbuf.st_mode & mode) == mode)
504 	{
505 		if (tTd(54, 4))
506 			printf("\tOK\n");
507 		return 0;
508 	}
509 	if (tTd(54, 4))
510 		printf("\tEACCES\n");
511 	return EACCES;
512 }
513 /*
514 **  FIXCRLF -- fix <CR><LF> in line.
515 **
516 **	Looks for the <CR><LF> combination and turns it into the
517 **	UNIX canonical <NL> character.  It only takes one line,
518 **	i.e., it is assumed that the first <NL> found is the end
519 **	of the line.
520 **
521 **	Parameters:
522 **		line -- the line to fix.
523 **		stripnl -- if true, strip the newline also.
524 **
525 **	Returns:
526 **		none.
527 **
528 **	Side Effects:
529 **		line is changed in place.
530 */
531 
532 fixcrlf(line, stripnl)
533 	char *line;
534 	bool stripnl;
535 {
536 	register char *p;
537 
538 	p = strchr(line, '\n');
539 	if (p == NULL)
540 		return;
541 	if (p > line && p[-1] == '\r')
542 		p--;
543 	if (!stripnl)
544 		*p++ = '\n';
545 	*p = '\0';
546 }
547 /*
548 **  DFOPEN -- determined file open
549 **
550 **	This routine has the semantics of fopen, except that it will
551 **	keep trying a few times to make this happen.  The idea is that
552 **	on very loaded systems, we may run out of resources (inodes,
553 **	whatever), so this tries to get around it.
554 */
555 
556 #ifndef O_ACCMODE
557 # define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
558 #endif
559 
560 struct omodes
561 {
562 	int	mask;
563 	int	mode;
564 	char	*farg;
565 } OpenModes[] =
566 {
567 	O_ACCMODE,		O_RDONLY,		"r",
568 	O_ACCMODE|O_APPEND,	O_WRONLY,		"w",
569 	O_ACCMODE|O_APPEND,	O_WRONLY|O_APPEND,	"a",
570 	O_TRUNC,		0,			"w+",
571 	O_APPEND,		O_APPEND,		"a+",
572 	0,			0,			"r+",
573 };
574 
575 FILE *
576 dfopen(filename, omode, cmode)
577 	char *filename;
578 	int omode;
579 	int cmode;
580 {
581 	register int tries;
582 	int fd;
583 	register struct omodes *om;
584 	struct stat st;
585 
586 	for (om = OpenModes; om->mask != 0; om++)
587 		if ((omode & om->mask) == om->mode)
588 			break;
589 
590 	for (tries = 0; tries < 10; tries++)
591 	{
592 		sleep((unsigned) (10 * tries));
593 		errno = 0;
594 		fd = open(filename, omode, cmode);
595 		if (fd >= 0)
596 			break;
597 		if (errno != ENFILE && errno != EINTR)
598 			break;
599 	}
600 	if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
601 	{
602 		int locktype;
603 
604 		/* lock the file to avoid accidental conflicts */
605 		if ((omode & O_ACCMODE) != O_RDONLY)
606 			locktype = LOCK_EX;
607 		else
608 			locktype = LOCK_SH;
609 		(void) lockfile(fd, filename, NULL, locktype);
610 		errno = 0;
611 	}
612 	if (fd < 0)
613 		return NULL;
614 	else
615 		return fdopen(fd, om->farg);
616 }
617 /*
618 **  PUTLINE -- put a line like fputs obeying SMTP conventions
619 **
620 **	This routine always guarantees outputing a newline (or CRLF,
621 **	as appropriate) at the end of the string.
622 **
623 **	Parameters:
624 **		l -- line to put.
625 **		fp -- file to put it onto.
626 **		m -- the mailer used to control output.
627 **
628 **	Returns:
629 **		none
630 **
631 **	Side Effects:
632 **		output of l to fp.
633 */
634 
635 putline(l, fp, m)
636 	register char *l;
637 	FILE *fp;
638 	MAILER *m;
639 {
640 	register char *p;
641 	register char svchar;
642 
643 	/* strip out 0200 bits -- these can look like TELNET protocol */
644 	if (bitnset(M_7BITS, m->m_flags))
645 	{
646 		for (p = l; (svchar = *p) != '\0'; ++p)
647 			if (bitset(0200, svchar))
648 				*p = svchar &~ 0200;
649 	}
650 
651 	do
652 	{
653 		/* find the end of the line */
654 		p = strchr(l, '\n');
655 		if (p == NULL)
656 			p = &l[strlen(l)];
657 
658 		if (TrafficLogFile != NULL)
659 			fprintf(TrafficLogFile, "%05d >>> ", getpid());
660 
661 		/* check for line overflow */
662 		while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
663 		{
664 			register char *q = &l[m->m_linelimit - 1];
665 
666 			svchar = *q;
667 			*q = '\0';
668 			if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
669 			{
670 				(void) putc('.', fp);
671 				if (TrafficLogFile != NULL)
672 					(void) putc('.', TrafficLogFile);
673 			}
674 			fputs(l, fp);
675 			(void) putc('!', fp);
676 			fputs(m->m_eol, fp);
677 			if (TrafficLogFile != NULL)
678 				fprintf(TrafficLogFile, "%s!\n%05d >>> ",
679 					l, getpid());
680 			*q = svchar;
681 			l = q;
682 		}
683 
684 		/* output last part */
685 		if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
686 		{
687 			(void) putc('.', fp);
688 			if (TrafficLogFile != NULL)
689 				(void) putc('.', TrafficLogFile);
690 		}
691 		if (TrafficLogFile != NULL)
692 			fprintf(TrafficLogFile, "%.*s\n", p - l, l);
693 		for ( ; l < p; ++l)
694 			(void) putc(*l, fp);
695 		fputs(m->m_eol, fp);
696 		if (*l == '\n')
697 			++l;
698 	} while (l[0] != '\0');
699 }
700 /*
701 **  XUNLINK -- unlink a file, doing logging as appropriate.
702 **
703 **	Parameters:
704 **		f -- name of file to unlink.
705 **
706 **	Returns:
707 **		none.
708 **
709 **	Side Effects:
710 **		f is unlinked.
711 */
712 
713 xunlink(f)
714 	char *f;
715 {
716 	register int i;
717 
718 # ifdef LOG
719 	if (LogLevel > 98)
720 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
721 # endif /* LOG */
722 
723 	i = unlink(f);
724 # ifdef LOG
725 	if (i < 0 && LogLevel > 97)
726 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
727 # endif /* LOG */
728 }
729 /*
730 **  XFCLOSE -- close a file, doing logging as appropriate.
731 **
732 **	Parameters:
733 **		fp -- file pointer for the file to close
734 **		a, b -- miscellaneous crud to print for debugging
735 **
736 **	Returns:
737 **		none.
738 **
739 **	Side Effects:
740 **		fp is closed.
741 */
742 
743 xfclose(fp, a, b)
744 	FILE *fp;
745 	char *a, *b;
746 {
747 	if (tTd(53, 99))
748 		printf("xfclose(%x) %s %s\n", fp, a, b);
749 #ifdef XDEBUG
750 	if (fileno(fp) == 1)
751 		syserr("xfclose(%s %s): fd = 1", a, b);
752 #endif
753 	if (fclose(fp) < 0 && tTd(53, 99))
754 		printf("xfclose FAILURE: %s\n", errstring(errno));
755 }
756 /*
757 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
758 **
759 **	Parameters:
760 **		buf -- place to put the input line.
761 **		siz -- size of buf.
762 **		fp -- file to read from.
763 **		timeout -- the timeout before error occurs.
764 **		during -- what we are trying to read (for error messages).
765 **
766 **	Returns:
767 **		NULL on error (including timeout).  This will also leave
768 **			buf containing a null string.
769 **		buf otherwise.
770 **
771 **	Side Effects:
772 **		none.
773 */
774 
775 static jmp_buf	CtxReadTimeout;
776 static int	readtimeout();
777 
778 char *
779 sfgets(buf, siz, fp, timeout, during)
780 	char *buf;
781 	int siz;
782 	FILE *fp;
783 	time_t timeout;
784 	char *during;
785 {
786 	register EVENT *ev = NULL;
787 	register char *p;
788 
789 	/* set the timeout */
790 	if (timeout != 0)
791 	{
792 		if (setjmp(CtxReadTimeout) != 0)
793 		{
794 # ifdef LOG
795 			syslog(LOG_NOTICE,
796 			    "timeout waiting for input from %s during %s\n",
797 			    CurHostName? CurHostName: "local", during);
798 # endif
799 			errno = 0;
800 			usrerr("451 timeout waiting for input during %s",
801 				during);
802 			buf[0] = '\0';
803 #ifdef XDEBUG
804 			checkfd012(during);
805 #endif
806 			return (NULL);
807 		}
808 		ev = setevent(timeout, readtimeout, 0);
809 	}
810 
811 	/* try to read */
812 	p = NULL;
813 	while (p == NULL && !feof(fp) && !ferror(fp))
814 	{
815 		errno = 0;
816 		p = fgets(buf, siz, fp);
817 		if (errno == EINTR)
818 			clearerr(fp);
819 	}
820 
821 	/* clear the event if it has not sprung */
822 	clrevent(ev);
823 
824 	/* clean up the books and exit */
825 	LineNumber++;
826 	if (p == NULL)
827 	{
828 		buf[0] = '\0';
829 		if (TrafficLogFile != NULL)
830 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
831 		return (NULL);
832 	}
833 	if (TrafficLogFile != NULL)
834 		fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
835 	if (SevenBit)
836 		for (p = buf; *p != '\0'; p++)
837 			*p &= ~0200;
838 	return (buf);
839 }
840 
841 static
842 readtimeout()
843 {
844 	longjmp(CtxReadTimeout, 1);
845 }
846 /*
847 **  FGETFOLDED -- like fgets, but know about folded lines.
848 **
849 **	Parameters:
850 **		buf -- place to put result.
851 **		n -- bytes available.
852 **		f -- file to read from.
853 **
854 **	Returns:
855 **		input line(s) on success, NULL on error or EOF.
856 **		This will normally be buf -- unless the line is too
857 **			long, when it will be xalloc()ed.
858 **
859 **	Side Effects:
860 **		buf gets lines from f, with continuation lines (lines
861 **		with leading white space) appended.  CRLF's are mapped
862 **		into single newlines.  Any trailing NL is stripped.
863 */
864 
865 char *
866 fgetfolded(buf, n, f)
867 	char *buf;
868 	register int n;
869 	FILE *f;
870 {
871 	register char *p = buf;
872 	char *bp = buf;
873 	register int i;
874 
875 	n--;
876 	while ((i = getc(f)) != EOF)
877 	{
878 		if (i == '\r')
879 		{
880 			i = getc(f);
881 			if (i != '\n')
882 			{
883 				if (i != EOF)
884 					(void) ungetc(i, f);
885 				i = '\r';
886 			}
887 		}
888 		if (--n <= 0)
889 		{
890 			/* allocate new space */
891 			char *nbp;
892 			int nn;
893 
894 			nn = (p - bp);
895 			if (nn < MEMCHUNKSIZE)
896 				nn *= 2;
897 			else
898 				nn += MEMCHUNKSIZE;
899 			nbp = xalloc(nn);
900 			bcopy(bp, nbp, p - bp);
901 			p = &nbp[p - bp];
902 			if (bp != buf)
903 				free(bp);
904 			bp = nbp;
905 			n = nn - (p - bp);
906 		}
907 		*p++ = i;
908 		if (i == '\n')
909 		{
910 			LineNumber++;
911 			i = getc(f);
912 			if (i != EOF)
913 				(void) ungetc(i, f);
914 			if (i != ' ' && i != '\t')
915 				break;
916 		}
917 	}
918 	if (p == bp)
919 		return (NULL);
920 	*--p = '\0';
921 	return (bp);
922 }
923 /*
924 **  CURTIME -- return current time.
925 **
926 **	Parameters:
927 **		none.
928 **
929 **	Returns:
930 **		the current time.
931 **
932 **	Side Effects:
933 **		none.
934 */
935 
936 time_t
937 curtime()
938 {
939 	auto time_t t;
940 
941 	(void) time(&t);
942 	return (t);
943 }
944 /*
945 **  ATOBOOL -- convert a string representation to boolean.
946 **
947 **	Defaults to "TRUE"
948 **
949 **	Parameters:
950 **		s -- string to convert.  Takes "tTyY" as true,
951 **			others as false.
952 **
953 **	Returns:
954 **		A boolean representation of the string.
955 **
956 **	Side Effects:
957 **		none.
958 */
959 
960 bool
961 atobool(s)
962 	register char *s;
963 {
964 	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
965 		return (TRUE);
966 	return (FALSE);
967 }
968 /*
969 **  ATOOCT -- convert a string representation to octal.
970 **
971 **	Parameters:
972 **		s -- string to convert.
973 **
974 **	Returns:
975 **		An integer representing the string interpreted as an
976 **		octal number.
977 **
978 **	Side Effects:
979 **		none.
980 */
981 
982 atooct(s)
983 	register char *s;
984 {
985 	register int i = 0;
986 
987 	while (*s >= '0' && *s <= '7')
988 		i = (i << 3) | (*s++ - '0');
989 	return (i);
990 }
991 /*
992 **  WAITFOR -- wait for a particular process id.
993 **
994 **	Parameters:
995 **		pid -- process id to wait for.
996 **
997 **	Returns:
998 **		status of pid.
999 **		-1 if pid never shows up.
1000 **
1001 **	Side Effects:
1002 **		none.
1003 */
1004 
1005 int
1006 waitfor(pid)
1007 	int pid;
1008 {
1009 #ifdef WAITUNION
1010 	union wait st;
1011 #else
1012 	auto int st;
1013 #endif
1014 	int i;
1015 
1016 	do
1017 	{
1018 		errno = 0;
1019 		i = wait(&st);
1020 	} while ((i >= 0 || errno == EINTR) && i != pid);
1021 	if (i < 0)
1022 		return -1;
1023 #ifdef WAITUNION
1024 	return st.w_status;
1025 #else
1026 	return st;
1027 #endif
1028 }
1029 /*
1030 **  BITINTERSECT -- tell if two bitmaps intersect
1031 **
1032 **	Parameters:
1033 **		a, b -- the bitmaps in question
1034 **
1035 **	Returns:
1036 **		TRUE if they have a non-null intersection
1037 **		FALSE otherwise
1038 **
1039 **	Side Effects:
1040 **		none.
1041 */
1042 
1043 bool
1044 bitintersect(a, b)
1045 	BITMAP a;
1046 	BITMAP b;
1047 {
1048 	int i;
1049 
1050 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1051 		if ((a[i] & b[i]) != 0)
1052 			return (TRUE);
1053 	return (FALSE);
1054 }
1055 /*
1056 **  BITZEROP -- tell if a bitmap is all zero
1057 **
1058 **	Parameters:
1059 **		map -- the bit map to check
1060 **
1061 **	Returns:
1062 **		TRUE if map is all zero.
1063 **		FALSE if there are any bits set in map.
1064 **
1065 **	Side Effects:
1066 **		none.
1067 */
1068 
1069 bool
1070 bitzerop(map)
1071 	BITMAP map;
1072 {
1073 	int i;
1074 
1075 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1076 		if (map[i] != 0)
1077 			return (FALSE);
1078 	return (TRUE);
1079 }
1080 /*
1081 **  STRCONTAINEDIN -- tell if one string is contained in another
1082 **
1083 **	Parameters:
1084 **		a -- possible substring.
1085 **		b -- possible superstring.
1086 **
1087 **	Returns:
1088 **		TRUE if a is contained in b.
1089 **		FALSE otherwise.
1090 */
1091 
1092 bool
1093 strcontainedin(a, b)
1094 	register char *a;
1095 	register char *b;
1096 {
1097 	int l;
1098 
1099 	l = strlen(a);
1100 	for (;;)
1101 	{
1102 		b = strchr(b, a[0]);
1103 		if (b == NULL)
1104 			return FALSE;
1105 		if (strncmp(a, b, l) == 0)
1106 			return TRUE;
1107 		b++;
1108 	}
1109 }
1110 /*
1111 **  CHECKFD012 -- check low numbered file descriptors
1112 **
1113 **	File descriptors 0, 1, and 2 should be open at all times.
1114 **	This routine verifies that, and fixes it if not true.
1115 **
1116 **	Parameters:
1117 **		where -- a tag printed if the assertion failed
1118 **
1119 **	Returns:
1120 **		none
1121 */
1122 
1123 checkfd012(where)
1124 	char *where;
1125 {
1126 #ifdef XDEBUG
1127 	register int i;
1128 	struct stat stbuf;
1129 
1130 	for (i = 0; i < 3; i++)
1131 	{
1132 		if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
1133 		{
1134 			/* oops.... */
1135 			int fd;
1136 
1137 			syserr("%s: fd %d not open", where, i);
1138 			fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
1139 			if (fd != i)
1140 			{
1141 				(void) dup2(fd, i);
1142 				(void) close(fd);
1143 			}
1144 		}
1145 	}
1146 #endif /* XDEBUG */
1147 }
1148 /*
1149 **  PRINTOPENFDS -- print the open file descriptors (for debugging)
1150 **
1151 **	Parameters:
1152 **		logit -- if set, send output to syslog; otherwise
1153 **			print for debugging.
1154 **
1155 **	Returns:
1156 **		none.
1157 */
1158 
1159 #include <netdb.h>
1160 #include <arpa/inet.h>
1161 
1162 printopenfds(logit)
1163 	bool logit;
1164 {
1165 	register int fd;
1166 	extern int DtableSize;
1167 
1168 	for (fd = 0; fd < DtableSize; fd++)
1169 		dumpfd(fd, FALSE, logit);
1170 }
1171 /*
1172 **  DUMPFD -- dump a file descriptor
1173 **
1174 **	Parameters:
1175 **		fd -- the file descriptor to dump.
1176 **		printclosed -- if set, print a notification even if
1177 **			it is closed; otherwise print nothing.
1178 **		logit -- if set, send output to syslog instead of stdout.
1179 */
1180 
1181 dumpfd(fd, printclosed, logit)
1182 	int fd;
1183 	bool printclosed;
1184 	bool logit;
1185 {
1186 	register struct hostent *hp;
1187 	register char *p;
1188 	struct sockaddr_in sin;
1189 	auto int slen;
1190 	struct stat st;
1191 	char buf[200];
1192 
1193 	p = buf;
1194 	sprintf(p, "%3d: ", fd);
1195 	p += strlen(p);
1196 
1197 	if (fstat(fd, &st) < 0)
1198 	{
1199 		if (printclosed || errno != EBADF)
1200 		{
1201 			sprintf(p, "CANNOT STAT (%s)", errstring(errno));
1202 			goto printit;
1203 		}
1204 		return;
1205 	}
1206 
1207 	slen = fcntl(fd, F_GETFL, NULL);
1208 	if (slen != -1)
1209 	{
1210 		sprintf(p, "fl=0x%x, ", slen);
1211 		p += strlen(p);
1212 	}
1213 
1214 	sprintf(p, "mode=%o: ", st.st_mode);
1215 	p += strlen(p);
1216 	switch (st.st_mode & S_IFMT)
1217 	{
1218 #ifdef S_IFSOCK
1219 	  case S_IFSOCK:
1220 		sprintf(p, "SOCK ");
1221 		p += strlen(p);
1222 		slen = sizeof sin;
1223 		if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0)
1224 			sprintf(p, "(badsock)");
1225 		else
1226 		{
1227 			hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
1228 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
1229 						   : hp->h_name, ntohs(sin.sin_port));
1230 		}
1231 		p += strlen(p);
1232 		sprintf(p, "->");
1233 		p += strlen(p);
1234 		slen = sizeof sin;
1235 		if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0)
1236 			sprintf(p, "(badsock)");
1237 		else
1238 		{
1239 			hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
1240 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
1241 						   : hp->h_name, ntohs(sin.sin_port));
1242 		}
1243 		break;
1244 #endif
1245 
1246 	  case S_IFCHR:
1247 		sprintf(p, "CHR: ");
1248 		p += strlen(p);
1249 		goto defprint;
1250 
1251 	  case S_IFBLK:
1252 		sprintf(p, "BLK: ");
1253 		p += strlen(p);
1254 		goto defprint;
1255 
1256 	  default:
1257 defprint:
1258 		sprintf(p, "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld",
1259 			major(st.st_dev), minor(st.st_dev), st.st_ino,
1260 			st.st_nlink, st.st_uid, st.st_gid, st.st_size);
1261 		break;
1262 	}
1263 
1264 printit:
1265 	if (logit)
1266 		syslog(LOG_INFO, "%s", buf);
1267 	else
1268 		printf("%s\n", buf);
1269 }
1270