xref: /csrg-svn/usr.sbin/sendmail/src/util.c (revision 58247)
122717Sdist /*
242833Sbostic  * Copyright (c) 1983 Eric P. Allman
333731Sbostic  * Copyright (c) 1988 Regents of the University of California.
433731Sbostic  * All rights reserved.
533731Sbostic  *
642833Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822717Sdist 
922717Sdist #ifndef lint
10*58247Seric static char sccsid[] = "@(#)util.c	6.8 (Berkeley) 02/26/93";
1133731Sbostic #endif /* not lint */
1222717Sdist 
133151Seric # include <stdio.h>
144538Seric # include <sys/types.h>
154538Seric # include <sys/stat.h>
16298Seric # include <sysexits.h>
176890Seric # include <errno.h>
186890Seric # include "sendmail.h"
1957135Seric /*
20298Seric **  STRIPQUOTES -- Strip quotes & quote bits from a string.
21298Seric **
22298Seric **	Runs through a string and strips off unquoted quote
23298Seric **	characters and quote bits.  This is done in place.
24298Seric **
25298Seric **	Parameters:
26298Seric **		s -- the string to strip.
27298Seric **
28298Seric **	Returns:
29298Seric **		none.
30298Seric **
31298Seric **	Side Effects:
32298Seric **		none.
33298Seric **
34298Seric **	Called By:
35298Seric **		deliver
36298Seric */
37298Seric 
3854983Seric stripquotes(s)
39298Seric 	char *s;
40298Seric {
41298Seric 	register char *p;
42298Seric 	register char *q;
43298Seric 	register char c;
44298Seric 
454101Seric 	if (s == NULL)
464101Seric 		return;
474101Seric 
4854983Seric 	p = q = s;
4954983Seric 	do
50298Seric 	{
5154983Seric 		c = *p++;
5254983Seric 		if (c == '\\')
5354983Seric 			c = *p++;
5454983Seric 		else if (c == '"')
5554983Seric 			continue;
5654983Seric 		*q++ = c;
5754983Seric 	} while (c != '\0');
58298Seric }
59298Seric /*
602900Seric **  CAPITALIZE -- return a copy of a string, properly capitalized.
612900Seric **
622900Seric **	Parameters:
632900Seric **		s -- the string to capitalize.
642900Seric **
652900Seric **	Returns:
662900Seric **		a pointer to a properly capitalized string.
672900Seric **
682900Seric **	Side Effects:
692900Seric **		none.
702900Seric */
712900Seric 
722900Seric char *
732900Seric capitalize(s)
742900Seric 	register char *s;
752900Seric {
762900Seric 	static char buf[50];
772900Seric 	register char *p;
782900Seric 
792900Seric 	p = buf;
802900Seric 
812900Seric 	for (;;)
822900Seric 	{
8358050Seric 		while (!(isascii(*s) && isalpha(*s)) && *s != '\0')
842900Seric 			*p++ = *s++;
852900Seric 		if (*s == '\0')
862900Seric 			break;
8740999Sbostic 		*p++ = toupper(*s);
8840999Sbostic 		s++;
8958050Seric 		while (isascii(*s) && isalpha(*s))
902900Seric 			*p++ = *s++;
912900Seric 	}
922900Seric 
932900Seric 	*p = '\0';
942900Seric 	return (buf);
952900Seric }
962900Seric /*
97298Seric **  XALLOC -- Allocate memory and bitch wildly on failure.
98298Seric **
99298Seric **	THIS IS A CLUDGE.  This should be made to give a proper
100298Seric **	error -- but after all, what can we do?
101298Seric **
102298Seric **	Parameters:
103298Seric **		sz -- size of area to allocate.
104298Seric **
105298Seric **	Returns:
106298Seric **		pointer to data region.
107298Seric **
108298Seric **	Side Effects:
109298Seric **		Memory is allocated.
110298Seric */
111298Seric 
112298Seric char *
113298Seric xalloc(sz)
1147007Seric 	register int sz;
115298Seric {
116298Seric 	register char *p;
117298Seric 
11823121Seric 	p = malloc((unsigned) sz);
119298Seric 	if (p == NULL)
120298Seric 	{
121298Seric 		syserr("Out of memory!!");
12210685Seric 		abort();
12310685Seric 		/* exit(EX_UNAVAILABLE); */
124298Seric 	}
125298Seric 	return (p);
126298Seric }
127298Seric /*
1283151Seric **  COPYPLIST -- copy list of pointers.
1293151Seric **
1303151Seric **	This routine is the equivalent of newstr for lists of
1313151Seric **	pointers.
1323151Seric **
1333151Seric **	Parameters:
1343151Seric **		list -- list of pointers to copy.
1353151Seric **			Must be NULL terminated.
1363151Seric **		copycont -- if TRUE, copy the contents of the vector
1373151Seric **			(which must be a string) also.
1383151Seric **
1393151Seric **	Returns:
1403151Seric **		a copy of 'list'.
1413151Seric **
1423151Seric **	Side Effects:
1433151Seric **		none.
1443151Seric */
1453151Seric 
1463151Seric char **
1473151Seric copyplist(list, copycont)
1483151Seric 	char **list;
1493151Seric 	bool copycont;
1503151Seric {
1513151Seric 	register char **vp;
1523151Seric 	register char **newvp;
1533151Seric 
1543151Seric 	for (vp = list; *vp != NULL; vp++)
1553151Seric 		continue;
1563151Seric 
1573151Seric 	vp++;
1583151Seric 
15916897Seric 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
16016897Seric 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
1613151Seric 
1623151Seric 	if (copycont)
1633151Seric 	{
1643151Seric 		for (vp = newvp; *vp != NULL; vp++)
1653151Seric 			*vp = newstr(*vp);
1663151Seric 	}
1673151Seric 
1683151Seric 	return (newvp);
1693151Seric }
1703151Seric /*
17158170Seric **  COPYQUEUE -- copy address queue.
17258170Seric **
17358170Seric **	This routine is the equivalent of newstr for address queues
17458170Seric **	addresses marked with QDONTSEND aren't copied
17558170Seric **
17658170Seric **	Parameters:
17758170Seric **		addr -- list of address structures to copy.
17858170Seric **
17958170Seric **	Returns:
18058170Seric **		a copy of 'addr'.
18158170Seric **
18258170Seric **	Side Effects:
18358170Seric **		none.
18458170Seric */
18558170Seric 
18658170Seric ADDRESS *
18758170Seric copyqueue(addr)
18858170Seric 	ADDRESS *addr;
18958170Seric {
19058170Seric 	register ADDRESS *newaddr;
19158170Seric 	ADDRESS *ret;
19258170Seric 	register ADDRESS **tail = &ret;
19358170Seric 
19458170Seric 	while (addr != NULL)
19558170Seric 	{
19658170Seric 		if (!bitset(QDONTSEND, addr->q_flags))
19758170Seric 		{
19858170Seric 			newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
19958170Seric 			STRUCTCOPY(*addr, *newaddr);
20058170Seric 			*tail = newaddr;
20158170Seric 			tail = &newaddr->q_next;
20258170Seric 		}
20358170Seric 		addr = addr->q_next;
20458170Seric 	}
20558170Seric 	*tail = NULL;
20658170Seric 
20758170Seric 	return ret;
20858170Seric }
20958170Seric /*
2103151Seric **  PRINTAV -- print argument vector.
2113151Seric **
2123151Seric **	Parameters:
2133151Seric **		av -- argument vector.
2143151Seric **
2153151Seric **	Returns:
2163151Seric **		none.
2173151Seric **
2183151Seric **	Side Effects:
2193151Seric **		prints av.
2203151Seric */
2213151Seric 
2223151Seric printav(av)
2233151Seric 	register char **av;
2243151Seric {
2253151Seric 	while (*av != NULL)
2263151Seric 	{
2278063Seric 		if (tTd(0, 44))
2288063Seric 			printf("\n\t%08x=", *av);
2298063Seric 		else
23023105Seric 			(void) putchar(' ');
2313151Seric 		xputs(*av++);
2323151Seric 	}
23323105Seric 	(void) putchar('\n');
2343151Seric }
2353151Seric /*
2363151Seric **  LOWER -- turn letter into lower case.
2373151Seric **
2383151Seric **	Parameters:
2393151Seric **		c -- character to turn into lower case.
2403151Seric **
2413151Seric **	Returns:
2423151Seric **		c, in lower case.
2433151Seric **
2443151Seric **	Side Effects:
2453151Seric **		none.
2463151Seric */
2473151Seric 
2483151Seric char
2493151Seric lower(c)
2503151Seric 	register char c;
2513151Seric {
25258050Seric 	return((isascii(c) && isupper(c)) ? tolower(c) : c);
2533151Seric }
2543151Seric /*
2553151Seric **  XPUTS -- put string doing control escapes.
2563151Seric **
2573151Seric **	Parameters:
2583151Seric **		s -- string to put.
2593151Seric **
2603151Seric **	Returns:
2613151Seric **		none.
2623151Seric **
2633151Seric **	Side Effects:
2643151Seric **		output to stdout
2653151Seric */
2663151Seric 
2673151Seric xputs(s)
2683151Seric 	register char *s;
2693151Seric {
27058050Seric 	register int c;
27151781Seric 	register struct metamac *mp;
27251781Seric 	extern struct metamac MetaMacros[];
2733151Seric 
2748055Seric 	if (s == NULL)
2758055Seric 	{
2768055Seric 		printf("<null>");
2778055Seric 		return;
2788055Seric 	}
27958050Seric 	while ((c = (*s++ & 0377)) != '\0')
2803151Seric 	{
2813151Seric 		if (!isascii(c))
2823151Seric 		{
28358050Seric 			if (c == MATCHREPL || c == MACROEXPAND)
28458050Seric 			{
28558050Seric 				putchar('$');
28658050Seric 				continue;
28758050Seric 			}
28858050Seric 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
28958050Seric 			{
29058050Seric 				if ((mp->metaval & 0377) == c)
29158050Seric 				{
29258050Seric 					printf("$%c", mp->metaname);
29358050Seric 					break;
29458050Seric 				}
29558050Seric 			}
29658050Seric 			if (mp->metaname != '\0')
29758050Seric 				continue;
29823105Seric 			(void) putchar('\\');
2993151Seric 			c &= 0177;
3003151Seric 		}
30157589Seric 		if (isprint(c))
3023151Seric 		{
30357589Seric 			putchar(c);
30457589Seric 			continue;
30557589Seric 		}
30652050Seric 
30757589Seric 		/* wasn't a meta-macro -- find another way to print it */
30857589Seric 		switch (c)
30957589Seric 		{
31057589Seric 		  case '\0':
31157589Seric 			continue;
31252050Seric 
31357589Seric 		  case '\n':
31457589Seric 			c = 'n';
31557589Seric 			break;
31652050Seric 
31757589Seric 		  case '\r':
31857589Seric 			c = 'r';
31957589Seric 			break;
32052637Seric 
32157589Seric 		  case '\t':
32257589Seric 			c = 't';
32357589Seric 			break;
32457589Seric 
32557589Seric 		  default:
32657589Seric 			(void) putchar('^');
32757589Seric 			(void) putchar(c ^ 0100);
32857589Seric 			continue;
3293151Seric 		}
3303151Seric 	}
3314086Seric 	(void) fflush(stdout);
3323151Seric }
3333151Seric /*
3343151Seric **  MAKELOWER -- Translate a line into lower case
3353151Seric **
3363151Seric **	Parameters:
3373151Seric **		p -- the string to translate.  If NULL, return is
3383151Seric **			immediate.
3393151Seric **
3403151Seric **	Returns:
3413151Seric **		none.
3423151Seric **
3433151Seric **	Side Effects:
3443151Seric **		String pointed to by p is translated to lower case.
3453151Seric **
3463151Seric **	Called By:
3473151Seric **		parse
3483151Seric */
3493151Seric 
3503151Seric makelower(p)
3513151Seric 	register char *p;
3523151Seric {
3533151Seric 	register char c;
3543151Seric 
3553151Seric 	if (p == NULL)
3563151Seric 		return;
3573151Seric 	for (; (c = *p) != '\0'; p++)
3583151Seric 		if (isascii(c) && isupper(c))
35933724Sbostic 			*p = tolower(c);
3603151Seric }
3614059Seric /*
3625196Seric **  BUILDFNAME -- build full name from gecos style entry.
3634375Seric **
3645196Seric **	This routine interprets the strange entry that would appear
3655196Seric **	in the GECOS field of the password file.
3665196Seric **
3674375Seric **	Parameters:
3685196Seric **		p -- name to build.
3695196Seric **		login -- the login name of this user (for &).
3705196Seric **		buf -- place to put the result.
3714375Seric **
3724375Seric **	Returns:
3734375Seric **		none.
3744375Seric **
3754375Seric **	Side Effects:
3764375Seric **		none.
3774375Seric */
3784375Seric 
37954984Seric buildfname(gecos, login, buf)
38054984Seric 	register char *gecos;
3815196Seric 	char *login;
3824375Seric 	char *buf;
3834375Seric {
38454984Seric 	register char *p;
3854375Seric 	register char *bp = buf;
38654984Seric 	int l;
3874375Seric 
38854984Seric 	if (*gecos == '*')
38954984Seric 		gecos++;
39054984Seric 
39157123Seric 	/* find length of final string */
39254984Seric 	l = 0;
39354984Seric 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
39454984Seric 	{
39554984Seric 		if (*p == '&')
39654984Seric 			l += strlen(login);
39754984Seric 		else
39854984Seric 			l++;
39954984Seric 	}
40054984Seric 
40154984Seric 	/* now fill in buf */
40255193Seric 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
4034375Seric 	{
4044375Seric 		if (*p == '&')
4054375Seric 		{
4065196Seric 			(void) strcpy(bp, login);
4074375Seric 			*bp = toupper(*bp);
4084375Seric 			while (*bp != '\0')
4094375Seric 				bp++;
4104375Seric 		}
4114375Seric 		else
41255193Seric 			*bp++ = *p;
4134375Seric 	}
4144375Seric 	*bp = '\0';
4154375Seric }
4164375Seric /*
4174538Seric **  SAFEFILE -- return true if a file exists and is safe for a user.
4184538Seric **
4194538Seric **	Parameters:
4204538Seric **		fn -- filename to check.
4214538Seric **		uid -- uid to compare against.
4224538Seric **		mode -- mode bits that must match.
4234538Seric **
4244538Seric **	Returns:
425*58247Seric **		0 if fn exists, is owned by uid, and matches mode.
426*58247Seric **		An errno otherwise.  The actual errno is cleared.
4274538Seric **
4284538Seric **	Side Effects:
4294538Seric **		none.
4304538Seric */
4314538Seric 
432*58247Seric int
4334538Seric safefile(fn, uid, mode)
4344538Seric 	char *fn;
43555372Seric 	uid_t uid;
4364538Seric 	int mode;
4374538Seric {
4384538Seric 	struct stat stbuf;
4394538Seric 
440*58247Seric 	if (stat(fn, &stbuf) < 0)
441*58247Seric 	{
442*58247Seric 		int ret = errno;
443*58247Seric 
444*58247Seric 		errno = 0;
445*58247Seric 		return ret;
446*58247Seric 	}
447*58247Seric 	if (stbuf.st_uid == uid && (stbuf.st_mode & mode) == mode)
448*58247Seric 		return 0;
449*58247Seric 	return EPERM;
4504538Seric }
4514538Seric /*
4524557Seric **  FIXCRLF -- fix <CR><LF> in line.
4534557Seric **
4544557Seric **	Looks for the <CR><LF> combination and turns it into the
4554557Seric **	UNIX canonical <NL> character.  It only takes one line,
4564557Seric **	i.e., it is assumed that the first <NL> found is the end
4574557Seric **	of the line.
4584557Seric **
4594557Seric **	Parameters:
4604557Seric **		line -- the line to fix.
4614557Seric **		stripnl -- if true, strip the newline also.
4624557Seric **
4634557Seric **	Returns:
4644557Seric **		none.
4654557Seric **
4664557Seric **	Side Effects:
4674557Seric **		line is changed in place.
4684557Seric */
4694557Seric 
4704557Seric fixcrlf(line, stripnl)
4714557Seric 	char *line;
4724557Seric 	bool stripnl;
4734557Seric {
4744557Seric 	register char *p;
4754557Seric 
47656795Seric 	p = strchr(line, '\n');
4774557Seric 	if (p == NULL)
4784557Seric 		return;
47936291Sbostic 	if (p > line && p[-1] == '\r')
4804557Seric 		p--;
4814557Seric 	if (!stripnl)
4824557Seric 		*p++ = '\n';
4834557Seric 	*p = '\0';
4844557Seric }
4854557Seric /*
4866890Seric **  DFOPEN -- determined file open
4876890Seric **
4886890Seric **	This routine has the semantics of fopen, except that it will
4896890Seric **	keep trying a few times to make this happen.  The idea is that
4906890Seric **	on very loaded systems, we may run out of resources (inodes,
4916890Seric **	whatever), so this tries to get around it.
4926890Seric */
4936890Seric 
4946890Seric FILE *
4956890Seric dfopen(filename, mode)
4966890Seric 	char *filename;
4976890Seric 	char *mode;
4986890Seric {
4996890Seric 	register int tries;
5006890Seric 	register FILE *fp;
5016890Seric 
5026890Seric 	for (tries = 0; tries < 10; tries++)
5036890Seric 	{
50425618Seric 		sleep((unsigned) (10 * tries));
5056890Seric 		errno = 0;
5066890Seric 		fp = fopen(filename, mode);
5079376Seric 		if (fp != NULL)
5086890Seric 			break;
5099376Seric 		if (errno != ENFILE && errno != EINTR)
5109376Seric 			break;
5116890Seric 	}
51256328Seric 	if (fp != NULL)
51356328Seric 	{
51456328Seric #ifdef FLOCK
51556328Seric 		int locktype;
51656328Seric 
51756328Seric 		/* lock the file to avoid accidental conflicts */
51856328Seric 		if (*mode == 'w' || *mode == 'a')
51956328Seric 			locktype = LOCK_EX;
52056328Seric 		else
52156328Seric 			locktype = LOCK_SH;
52256328Seric 		(void) flock(fileno(fp), locktype);
52356328Seric #endif
52456328Seric 		errno = 0;
52556328Seric 	}
5266890Seric 	return (fp);
5276890Seric }
5287124Seric /*
5297124Seric **  PUTLINE -- put a line like fputs obeying SMTP conventions
5307124Seric **
5317753Seric **	This routine always guarantees outputing a newline (or CRLF,
5327753Seric **	as appropriate) at the end of the string.
5337753Seric **
5347124Seric **	Parameters:
5357124Seric **		l -- line to put.
5367124Seric **		fp -- file to put it onto.
53710172Seric **		m -- the mailer used to control output.
5387124Seric **
5397124Seric **	Returns:
5407124Seric **		none
5417124Seric **
5427124Seric **	Side Effects:
5437124Seric **		output of l to fp.
5447124Seric */
5457124Seric 
54610172Seric putline(l, fp, m)
5477753Seric 	register char *l;
5487124Seric 	FILE *fp;
54910172Seric 	MAILER *m;
5507124Seric {
5517124Seric 	register char *p;
55247157Sbostic 	register char svchar;
5537124Seric 
55411275Seric 	/* strip out 0200 bits -- these can look like TELNET protocol */
55552106Seric 	if (bitnset(M_7BITS, m->m_flags))
55611275Seric 	{
55747157Sbostic 		for (p = l; svchar = *p; ++p)
55847157Sbostic 			if (svchar & 0200)
55947157Sbostic 				*p = svchar &~ 0200;
56011275Seric 	}
56111275Seric 
5627753Seric 	do
5637124Seric 	{
5647753Seric 		/* find the end of the line */
56556795Seric 		p = strchr(l, '\n');
5667753Seric 		if (p == NULL)
5677753Seric 			p = &l[strlen(l)];
5687124Seric 
5697753Seric 		/* check for line overflow */
57052106Seric 		while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
5717753Seric 		{
57252106Seric 			register char *q = &l[m->m_linelimit - 1];
5737124Seric 
5747753Seric 			svchar = *q;
5757753Seric 			*q = '\0';
57610685Seric 			if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
57723105Seric 				(void) putc('.', fp);
5787753Seric 			fputs(l, fp);
57923105Seric 			(void) putc('!', fp);
58010326Seric 			fputs(m->m_eol, fp);
5817753Seric 			*q = svchar;
5827753Seric 			l = q;
5837753Seric 		}
5847124Seric 
5857753Seric 		/* output last part */
58610685Seric 		if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
58723105Seric 			(void) putc('.', fp);
58847157Sbostic 		for ( ; l < p; ++l)
58947157Sbostic 			(void) putc(*l, fp);
59010326Seric 		fputs(m->m_eol, fp);
5917753Seric 		if (*l == '\n')
59247157Sbostic 			++l;
5937753Seric 	} while (l[0] != '\0');
5947124Seric }
5957676Seric /*
5967676Seric **  XUNLINK -- unlink a file, doing logging as appropriate.
5977676Seric **
5987676Seric **	Parameters:
5997676Seric **		f -- name of file to unlink.
6007676Seric **
6017676Seric **	Returns:
6027676Seric **		none.
6037676Seric **
6047676Seric **	Side Effects:
6057676Seric **		f is unlinked.
6067676Seric */
6077676Seric 
6087676Seric xunlink(f)
6097676Seric 	char *f;
6107676Seric {
6117676Seric 	register int i;
6127676Seric 
6137676Seric # ifdef LOG
61458020Seric 	if (LogLevel > 98)
61558020Seric 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
61656795Seric # endif /* LOG */
6177676Seric 
6187676Seric 	i = unlink(f);
6197676Seric # ifdef LOG
62058020Seric 	if (i < 0 && LogLevel > 97)
6217942Seric 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
62256795Seric # endif /* LOG */
6237676Seric }
6247685Seric /*
62514885Seric **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
6267685Seric **
6277685Seric **	Parameters:
6287685Seric **		buf -- place to put the input line.
6297685Seric **		siz -- size of buf.
6307685Seric **		fp -- file to read from.
63157384Seric **		timeout -- the timeout before error occurs.
6327685Seric **
6337685Seric **	Returns:
63415533Seric **		NULL on error (including timeout).  This will also leave
63515533Seric **			buf containing a null string.
6367685Seric **		buf otherwise.
6377685Seric **
6387685Seric **	Side Effects:
6397685Seric **		none.
6407685Seric */
6417685Seric 
64214885Seric static jmp_buf	CtxReadTimeout;
6437685Seric 
6447685Seric char *
64557384Seric sfgets(buf, siz, fp, timeout)
6467685Seric 	char *buf;
6477685Seric 	int siz;
6487685Seric 	FILE *fp;
64957384Seric 	time_t timeout;
6507685Seric {
6517942Seric 	register EVENT *ev = NULL;
6527685Seric 	register char *p;
65346928Sbostic 	static int readtimeout();
6547685Seric 
65514885Seric 	/* set the timeout */
65657384Seric 	if (timeout != 0)
65714885Seric 	{
65814885Seric 		if (setjmp(CtxReadTimeout) != 0)
65914885Seric 		{
66036233Skarels # ifdef LOG
66136230Skarels 			syslog(LOG_NOTICE,
66236230Skarels 			    "timeout waiting for input from %s\n",
66357642Seric 			    CurHostName? CurHostName: "local");
66436233Skarels # endif
66536230Skarels 			errno = 0;
66640964Sbostic 			usrerr("451 timeout waiting for input");
66719037Seric 			buf[0] = '\0';
66814885Seric 			return (NULL);
66914885Seric 		}
67057384Seric 		ev = setevent(timeout, readtimeout, 0);
67114885Seric 	}
67214885Seric 
67314885Seric 	/* try to read */
67415533Seric 	p = NULL;
67515533Seric 	while (p == NULL && !feof(fp) && !ferror(fp))
6767942Seric 	{
6777942Seric 		errno = 0;
6787942Seric 		p = fgets(buf, siz, fp);
67915533Seric 		if (errno == EINTR)
68015533Seric 			clearerr(fp);
68115533Seric 	}
68214885Seric 
68314885Seric 	/* clear the event if it has not sprung */
6847685Seric 	clrevent(ev);
68514885Seric 
68614885Seric 	/* clean up the books and exit */
6878055Seric 	LineNumber++;
68815533Seric 	if (p == NULL)
68916880Seric 	{
69015533Seric 		buf[0] = '\0';
69116880Seric 		return (NULL);
69216880Seric 	}
69352106Seric 	if (!EightBit)
69452106Seric 		for (p = buf; *p != '\0'; p++)
69552106Seric 			*p &= ~0200;
69616880Seric 	return (buf);
6977685Seric }
6987685Seric 
6997685Seric static
7007685Seric readtimeout()
7017685Seric {
70214885Seric 	longjmp(CtxReadTimeout, 1);
7037685Seric }
7047786Seric /*
7057786Seric **  FGETFOLDED -- like fgets, but know about folded lines.
7067786Seric **
7077786Seric **	Parameters:
7087786Seric **		buf -- place to put result.
7097786Seric **		n -- bytes available.
7107786Seric **		f -- file to read from.
7117786Seric **
7127786Seric **	Returns:
71357135Seric **		input line(s) on success, NULL on error or EOF.
71457135Seric **		This will normally be buf -- unless the line is too
71557135Seric **			long, when it will be xalloc()ed.
7167786Seric **
7177786Seric **	Side Effects:
7187786Seric **		buf gets lines from f, with continuation lines (lines
7197786Seric **		with leading white space) appended.  CRLF's are mapped
7207786Seric **		into single newlines.  Any trailing NL is stripped.
7217786Seric */
7227786Seric 
7237786Seric char *
7247786Seric fgetfolded(buf, n, f)
7257786Seric 	char *buf;
7267786Seric 	register int n;
7277786Seric 	FILE *f;
7287786Seric {
7297786Seric 	register char *p = buf;
73057135Seric 	char *bp = buf;
7317786Seric 	register int i;
7327786Seric 
7337786Seric 	n--;
73417350Seric 	while ((i = getc(f)) != EOF)
7357786Seric 	{
73617350Seric 		if (i == '\r')
73717350Seric 		{
73817350Seric 			i = getc(f);
73917350Seric 			if (i != '\n')
74017350Seric 			{
74117350Seric 				if (i != EOF)
74223105Seric 					(void) ungetc(i, f);
74317350Seric 				i = '\r';
74417350Seric 			}
74517350Seric 		}
74657135Seric 		if (--n <= 0)
74757135Seric 		{
74857135Seric 			/* allocate new space */
74957135Seric 			char *nbp;
75057135Seric 			int nn;
75157135Seric 
75257135Seric 			nn = (p - bp);
75357232Seric 			if (nn < MEMCHUNKSIZE)
75457135Seric 				nn *= 2;
75557135Seric 			else
75657232Seric 				nn += MEMCHUNKSIZE;
75757135Seric 			nbp = xalloc(nn);
75857135Seric 			bcopy(bp, nbp, p - bp);
75957135Seric 			p = &nbp[p - bp];
76057135Seric 			if (bp != buf)
76157135Seric 				free(bp);
76257135Seric 			bp = nbp;
76357135Seric 			n = nn - (p - bp);
76457135Seric 		}
76557135Seric 		*p++ = i;
76617350Seric 		if (i == '\n')
76717350Seric 		{
76817350Seric 			LineNumber++;
76917350Seric 			i = getc(f);
77017350Seric 			if (i != EOF)
77123105Seric 				(void) ungetc(i, f);
77217350Seric 			if (i != ' ' && i != '\t')
77352647Seric 				break;
77417350Seric 		}
7757786Seric 	}
77657135Seric 	if (p == bp)
77752647Seric 		return (NULL);
77852647Seric 	*--p = '\0';
77957135Seric 	return (bp);
7807786Seric }
7817860Seric /*
7827886Seric **  CURTIME -- return current time.
7837886Seric **
7847886Seric **	Parameters:
7857886Seric **		none.
7867886Seric **
7877886Seric **	Returns:
7887886Seric **		the current time.
7897886Seric **
7907886Seric **	Side Effects:
7917886Seric **		none.
7927886Seric */
7937886Seric 
7947886Seric time_t
7957886Seric curtime()
7967886Seric {
7977886Seric 	auto time_t t;
7987886Seric 
7997886Seric 	(void) time(&t);
8007886Seric 	return (t);
8017886Seric }
8028264Seric /*
8038264Seric **  ATOBOOL -- convert a string representation to boolean.
8048264Seric **
8058264Seric **	Defaults to "TRUE"
8068264Seric **
8078264Seric **	Parameters:
8088264Seric **		s -- string to convert.  Takes "tTyY" as true,
8098264Seric **			others as false.
8108264Seric **
8118264Seric **	Returns:
8128264Seric **		A boolean representation of the string.
8138264Seric **
8148264Seric **	Side Effects:
8158264Seric **		none.
8168264Seric */
8178264Seric 
8188264Seric bool
8198264Seric atobool(s)
8208264Seric 	register char *s;
8218264Seric {
82256795Seric 	if (*s == '\0' || strchr("tTyY", *s) != NULL)
8238264Seric 		return (TRUE);
8248264Seric 	return (FALSE);
8258264Seric }
8269048Seric /*
8279048Seric **  ATOOCT -- convert a string representation to octal.
8289048Seric **
8299048Seric **	Parameters:
8309048Seric **		s -- string to convert.
8319048Seric **
8329048Seric **	Returns:
8339048Seric **		An integer representing the string interpreted as an
8349048Seric **		octal number.
8359048Seric **
8369048Seric **	Side Effects:
8379048Seric **		none.
8389048Seric */
8399048Seric 
8409048Seric atooct(s)
8419048Seric 	register char *s;
8429048Seric {
8439048Seric 	register int i = 0;
8449048Seric 
8459048Seric 	while (*s >= '0' && *s <= '7')
8469048Seric 		i = (i << 3) | (*s++ - '0');
8479048Seric 	return (i);
8489048Seric }
8499376Seric /*
8509376Seric **  WAITFOR -- wait for a particular process id.
8519376Seric **
8529376Seric **	Parameters:
8539376Seric **		pid -- process id to wait for.
8549376Seric **
8559376Seric **	Returns:
8569376Seric **		status of pid.
8579376Seric **		-1 if pid never shows up.
8589376Seric **
8599376Seric **	Side Effects:
8609376Seric **		none.
8619376Seric */
8629376Seric 
8639376Seric waitfor(pid)
8649376Seric 	int pid;
8659376Seric {
8669376Seric 	auto int st;
8679376Seric 	int i;
8689376Seric 
8699376Seric 	do
8709376Seric 	{
8719376Seric 		errno = 0;
8729376Seric 		i = wait(&st);
8739376Seric 	} while ((i >= 0 || errno == EINTR) && i != pid);
8749376Seric 	if (i < 0)
8759376Seric 		st = -1;
8769376Seric 	return (st);
8779376Seric }
8789376Seric /*
87910685Seric **  BITINTERSECT -- tell if two bitmaps intersect
88010685Seric **
88110685Seric **	Parameters:
88210685Seric **		a, b -- the bitmaps in question
88310685Seric **
88410685Seric **	Returns:
88510685Seric **		TRUE if they have a non-null intersection
88610685Seric **		FALSE otherwise
88710685Seric **
88810685Seric **	Side Effects:
88910685Seric **		none.
89010685Seric */
89110685Seric 
89210685Seric bool
89310685Seric bitintersect(a, b)
89410685Seric 	BITMAP a;
89510685Seric 	BITMAP b;
89610685Seric {
89710685Seric 	int i;
89810685Seric 
89910685Seric 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
90010685Seric 		if ((a[i] & b[i]) != 0)
90110685Seric 			return (TRUE);
90210685Seric 	return (FALSE);
90310685Seric }
90410685Seric /*
90510685Seric **  BITZEROP -- tell if a bitmap is all zero
90610685Seric **
90710685Seric **	Parameters:
90810685Seric **		map -- the bit map to check
90910685Seric **
91010685Seric **	Returns:
91110685Seric **		TRUE if map is all zero.
91210685Seric **		FALSE if there are any bits set in map.
91310685Seric **
91410685Seric **	Side Effects:
91510685Seric **		none.
91610685Seric */
91710685Seric 
91810685Seric bool
91910685Seric bitzerop(map)
92010685Seric 	BITMAP map;
92110685Seric {
92210685Seric 	int i;
92310685Seric 
92410685Seric 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
92510685Seric 		if (map[i] != 0)
92610685Seric 			return (FALSE);
92710685Seric 	return (TRUE);
92810685Seric }
929*58247Seric /*
930*58247Seric **  TRANSIENTERROR -- tell if an error code indicates a transient failure
931*58247Seric **
932*58247Seric **	This looks at an errno value and tells if this is likely to
933*58247Seric **	go away if retried later.
934*58247Seric **
935*58247Seric **	Parameters:
936*58247Seric **		err -- the errno code to classify.
937*58247Seric **
938*58247Seric **	Returns:
939*58247Seric **		TRUE if this is probably transient.
940*58247Seric **		FALSE otherwise.
941*58247Seric */
942*58247Seric 
943*58247Seric bool
944*58247Seric transienterror(err)
945*58247Seric 	int err;
946*58247Seric {
947*58247Seric 	switch (err)
948*58247Seric 	{
949*58247Seric 	  case EIO:			/* I/O error */
950*58247Seric 	  case ENXIO:			/* Device not configured */
951*58247Seric 	  case EAGAIN:			/* Resource temporarily unavailable */
952*58247Seric 	  case ENOMEM:			/* Cannot allocate memory */
953*58247Seric 	  case ENODEV:			/* Operation not supported by device */
954*58247Seric 	  case ENFILE:			/* Too many open files in system */
955*58247Seric 	  case EMFILE:			/* Too many open files */
956*58247Seric 	  case ENOSPC:			/* No space left on device */
957*58247Seric #ifdef ETIMEDOUT
958*58247Seric 	  case ETIMEDOUT:		/* Connection timed out */
959*58247Seric #endif
960*58247Seric #ifdef ESTALE
961*58247Seric 	  case ESTALE:			/* Stale NFS file handle */
962*58247Seric #endif
963*58247Seric #ifdef ENETDOWN
964*58247Seric 	  case ENETDOWN:		/* Network is down */
965*58247Seric #endif
966*58247Seric #ifdef ENETUNREACH
967*58247Seric 	  case ENETUNREACH:		/* Network is unreachable */
968*58247Seric #endif
969*58247Seric #ifdef ENETRESET
970*58247Seric 	  case ENETRESET:		/* Network dropped connection on reset */
971*58247Seric #endif
972*58247Seric #ifdef ECONNABORTED
973*58247Seric 	  case ECONNABORTED:		/* Software caused connection abort */
974*58247Seric #endif
975*58247Seric #ifdef ECONNRESET
976*58247Seric 	  case ECONNRESET:		/* Connection reset by peer */
977*58247Seric #endif
978*58247Seric #ifdef ENOBUFS
979*58247Seric 	  case ENOBUFS:			/* No buffer space available */
980*58247Seric #endif
981*58247Seric #ifdef ESHUTDOWN
982*58247Seric 	  case ESHUTDOWN:		/* Can't send after socket shutdown */
983*58247Seric #endif
984*58247Seric #ifdef ECONNREFUSED
985*58247Seric 	  case ECONNREFUSED:		/* Connection refused */
986*58247Seric #endif
987*58247Seric #ifdef EHOSTDOWN
988*58247Seric 	  case EHOSTDOWN:		/* Host is down */
989*58247Seric #endif
990*58247Seric #ifdef EHOSTUNREACH
991*58247Seric 	  case EHOSTUNREACH:		/* No route to host */
992*58247Seric #endif
993*58247Seric #ifdef EDQUOT
994*58247Seric 	  case EDQUOT:			/* Disc quota exceeded */
995*58247Seric #endif
996*58247Seric #ifdef EPROCLIM
997*58247Seric 	  case EPROCLIM:		/* Too many processes */
998*58247Seric #endif
999*58247Seric #ifdef EUSERS
1000*58247Seric 	  case EUSERS:			/* Too many users */
1001*58247Seric #endif
1002*58247Seric #ifdef EDEADLK
1003*58247Seric 	  case EDEADLK:			/* Resource deadlock avoided */
1004*58247Seric #endif
1005*58247Seric 		return TRUE;
1006*58247Seric 	}
1007*58247Seric 
1008*58247Seric 	/* nope, must be permanent */
1009*58247Seric 	return FALSE;
1010*58247Seric }
1011